当前位置: 首页 > 最新文章 > 正文

详解handler机制

Android中有很多机制,打开源码最先遇到的应该就是handler机制了,handler主要为了解决线程间通讯的问题,首先看一下handler该怎么用。大家是不是觉得很神奇,下面带大家看一下handler的源码,源码中了无秘密。= null) { handleCallback; } else { if (mCallback !下面来看接受消息的线程,在代码开始的位置,先调用了Looper.pre

admin

Android中有很多机制,打开源码最先遇到的应该就是handler机制了,handler主要为了解决线程间通讯的问题,首先看一下handler该怎么用。大家是不是觉得很神奇,下面带大家看一下handler的源码,源码中了无秘密。= null) { handleCallback; } else { if (mCallback !下面来看接受消息的线程,在代码开始的位置,先调用了Looper.prepare();方法,来看一下这个方法做了什么public static void prepare() { prepare; } pr

Android中有很多机制,打开源码最先遇到的应该就是handler机制了,handler主要为了解决线程间通讯的问题,首先看一下handler该怎么用。

##handler的用法

1.在主线程中用handler

首先自定义一个handler,这里这么写是为了避免handler造成的内存泄漏

 static class LoadDataHandler extends Handler { private SoftReference<MainActivity> activitySRF = null; public LoadDataHandler(MainActivity activity) { activitySRF = new SoftReference<MainActivity>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 因为Handler是异步的,存在退出当前类之后才接收到handler消息的情况, // 并且软引用持有的对象会在堆内存不足时存在被回收的可能, // 所以这里需要判空处理 if(null == activitySRF || null == activitySRF.get()){ return; } switch(msg.what){ case 0:{ activitySRF.get().mUserNameTxt.setText(msg.obj.toString()); } break; default:{ } break; } } }

上面通过复写handler的handleMessage方法,将具体的主线程操作放进来。

 private TextView mUserNameTxt = null; public LoadDataHandler handler= new LoadDataHandler(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mUserNameTxt=findViewById(R.id.gone); Thread thread = new Thread(){ @Override public void run() { super.run(); try { Thread.sleep(3000); Message message = Message.obtain(); message.what=0; handler.sendMessage(message); } catch (InterruptedException e) { e.printStackTrace(); } } }; }

当我们在子线程中完成耗时操作,通过 handler.sendMessage(message);发送一个消息,主线程通过复写handleMessage方法,进行相应的UI操作。

大家是不是觉得很神奇,下面带大家看一下handler的源码,源码中了无秘密。

刚才和大家提到了handler机制中的两个大将,一个handler ,一个是message

handler主要做的是消息的发送和处理

message则是对信息的封装

还有两个幕后黑手,一个是Looper和一个MessageQueue

在我们的主线程中,会自动的给我们创建一个Looper对象,当创建好后,通过一个ThreadLock对线程和Looper进行一个绑定,确保一个线程只有一个Looper对象,而Looper内部管理着一个MessageQueue消息队列,当我们在子线程中通过handler.sendmessage发送消息后,消息就回被放到这个消息队列中,Looper通过loop方法,开启一个消息泵,对MessageQueue进行轮询,当有满足条件的消息时就会取出消息,通过handle.dispatchMessage()去处理消息

public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }

通过对Message中callback的判断,进入两个handler的方法

1. handleCallback(msg);

2.handleMessage(msg);

有人肯定会问callback哪里哪里的,大家不要忘了handler还有有个post方法,可以传一个Runnable接口,当我们在主线程中调用handler.post()方法时,也会把消息放到消息队列中,当取出消息调用handleCallback时,就会去执行Runnable的run方法,所以可以看出,run()的执行是在主线程中,而不是通过post发送的消息,则交给了handleMessage()去处理,也就完成了线程间的通信。

上面是在主线程中应用handler,进行线程间通信,那我们在子线程中该怎么用handler了

2.在子线程中用handler

Thread thread1 = new Thread(){ @Override public void run() { super.run(); Looper.prepare(); handler =new Handler(Looper.myLooper()){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: break; } } }; Looper.loop();  } };Thread thread2 = new Thread(){ @Override public void run() { super.run(); for (int i=0 ; i<10; i++){ handler.sendEmptyMessage(1); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; thread1.start(); thread2.start();

上边的代码很好理解,新建一个线程隔3s发送一次消息,这和在之前handler发送消息没什么不同。下面来看接受消息的线程,在代码开始的位置,先调用了Looper.prepare();方法,来看一下这个方法做了什么

详解handler机制

public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }

通过代码可以知道,通过prepare方法,新建了一个loop对象,并通过ThreadLocal就线程和looper进行了一个绑定,这也正好印证了我在上边说过主线程的操作,我们要在子线程中操作handler也要仿照主线程的做法,给子线程绑定一个looper对象,在通过调用looper.loop()方法,开启轮训。这就是在子线程中应用handler。

#注

问:有人可能比较迷惑,不是主线程不能做耗时操作吗,主线程中也通过looper.loop()方法进行轮训,为什么系统没有卡死在那里?

答:有编程经验的肯定都知道,等代码执行完了,程序也就结束了,正因为主线程一直做着死循环,所以程序才没有一闪而过,还有就是 ,android是以事件驱动的,主线程的操作都是通过handler进行的,虽然处于死循环,但是在循环的过程中线程也进行这事件的处理,比如,打开新页面,更新ui,所以系统永远不会卡死在那里

问:一直这个死循环,会不会很费电

答:这里设计到Linux 的pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源


上一篇: 盘点4种极速进化中的动物:卷尾猴迈入石器时代? 下一篇:程序员复习一下pojo:VO、DTO、DO、PO
返回顶部