Android并发编程之Handler,MessageQueue,Looper详解
来源:互联网 发布:平面设计排版软件 编辑:程序博客网 时间:2024/05/18 02:17
Handler和Looper,MessageQueue之间是什么关系?
Looper和MessageQueue是线程中的概念,但是线程默认是没有Looper和MessageQueue的,我们需要手动去设置他们,当一个线程有了Looper和MessageQueue后,就可以关联一个Handler,我们再通过这个Handler,就可以从别的线程中发送消息给这个线程来执行。
我们给一个线程配置了Looper和MessageQueue后,当有消息通过Handler发送到本线程后,就会加入到MessageQueue中,然后Looper会不断的循环从MessageQueue中取出消息,然后放到Handler的handleMessage()中去执行。
如何给一个线程配置一个Looper和MessageQueue
给一个线程配置一个Looper和MessageQueue很简单,只需要调用Looper.prepare()和Looper.loop()就可以了。
首先我们先来看一下Looper.prepare()中都做了什么
/** *当我们调用了prepare后,我们就给这个线程配置了一个Looper,然后调用 *loop()方法后会创建MessageQueue并且looper会进入无限循环来从消息 *队列中取出消息去处理,我们调用quit()方法可以使looper结束这个无限 *循环 */ public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //ThreadLocal是线程本地存储,每个线程的ThreadLocal中存的数据 //都是不一样的,他们只属于本线程,当我们为该线程创建了一个Looper //之后,将这个Looper存储到ThreadLocal中,一个线程只能有一 //个Looper,如果再次调用prepare方法的话,就会抛出异常 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //new出了一个Looper sThreadLocal.set(new Looper(quitAllowed)); }
再来看一下Looper.loop()方法中做了什么
/** * 当调用了loop方法后,Looper会无限循环从MessageQueue中取出消息, * 因此我们一定要调用quit()来退出循环 */ public static void loop() { //获取当前线程的Looper final Looper me = myLooper(); if (me == null) { //如果没有调用Loop.prepare()的话,就会抛出下面这个异常 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //创建一个MessageQueue final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); //无限循环 for (;;) { //从MessageQueue中取出消息 Message msg = queue.next(); // might block if (msg == null) { // 没有消息,说明消息队列已经退出了,因此跳出循环 return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } //msg.target就是与此线程关联的Handler对象, //dispatchMessage方法将msg交给Handler去处理 msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } //msg已经交给Handler去处理,这里将msg复位 msg.recycleUnchecked(); } }
我们看到从MessageQueue取出的消息会调用Handler的dispatchMessage方法去执行,我们看看dispatchMessage做了什么事情
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
dispatchMessage方法中会判断msg中有没有设置callback,这个callback是一个Runnable对象,如果设置了的话就会交给handleCallback方法去执行
private static void handleCallback(Message message) { //因为callable是一个Runnable对象,因此在这里会调用Runnable的run方法 message.callback.run(); }
如果msg中没有设置callback,那么就直接将消息交给handleMessage去处理,这个handleMessage我们都很熟悉,我们写Handler时候都会重写这个方法
Handler如何关联一个有Looper和MessageQueue的线程
Handler关联一个Looper其实很简单,Looper是属于一个线程的,我们只需要用Handler的一个构造方法,我们传入哪个线程的Looper,Handler就会将消息发送到哪个线程。
/** * Use the provided {@link Looper} instead of the default one. * * @param looper The looper, must not be null. */ public Handler(Looper looper) { this(looper, null, false); }
工作线程发送消息到主线程
工作线程发消息到主线程这个大家应该是最熟悉不过的了,我们平常最常见的写法是这样的
public class MainActivity extends AppCompatActivity { //创建Handler,不指定Looper是默认关联当前线程即main线程的Looper private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i("zhangqi","消息发送到"+Thread.currentThread().getName()); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(){ @Override public void run() { try { //模拟耗时操作 TimeUnit.SECONDS.sleep(2); //发送消息 Message message = mHandler.obtainMessage(); message.sendToTarget(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }}
Handler导致的内存泄露
我们看到消息成功发送到了主线程中,我们通常通过这种方式在工作线程做耗时操作,然后通过Handler发送消息到主线程来更新UI,但是这样的写法会导致内存泄露。我们看一下AS给我们的提示:这个Handler应该是静态的否则会因此内存泄露
我们来解释一下,因为我们的Handler的创建是一个内部类的方式,一个内部类会持有他的外部类的一个引用,因此这回影响GC来回收外部类,也就是GC无法回收当前的Activity,如果我们的Handler是关联的工作线程的Looper和MessageQueue,这样做则没有影响。如果Handler关联的是主线程的Looper和MessageQueue,则我们应该写成静态内部类的方式,如果我们的静态内部类中要用到外部类的成员变量,我们应该传入一个外部类的弱引用,弱引用会在内存不够用的情况下被GC回收,因此不会造成内存泄露的问题。同样,我们的Thread也是一个内部类,他持有外部类的一个引用,因此Thread也应该写成静态内部类的方式,那么正确的写法应该如下
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyThread mThread = new MyThread(new MyHandler()); mThread.start(); } /** * 写成静态内部类的形式,不会持有外部类的引用 */ private static class MyThread extends Thread { //Thread中持有MyHandler的一个弱引用 private WeakReference<Handler> mHandler; //在构造方法中将Handler传进来 public MyThread(Handler handler) { mHandler = new WeakReference<Handler>(handler); } @Override public void run() { Handler handler = mHandler.get(); if (handler!=null) { try { TimeUnit.SECONDS.sleep(2); //发送一个消息 Message message = handler.obtainMessage(); message.sendToTarget(); } catch (InterruptedException e) { e.printStackTrace(); } }else{ //Handler被GC回收了 } } } private static class MyHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i("zhangqi", "消息发送到" + Thread.currentThread().getName()); } }}
主线程发送消息到工作线程
主线程发送消息到工作线程,我们要创建一个工作线程并且给他配置Looper和MessageQueue,然后创建一个Handler并且关联这个线程的Looper后,在主线程可以调用Handler的send,post等方法发送一个message或者runnable到Handler所在的线程执行了。下面这个例子是谷歌官方推荐的写法
public class MainActivity extends AppCompatActivity { private Worker mWorker; private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //当Worker创建完毕后,他是一个配置了Looper和 //MessageQueue的线程 mWorker = new Worker("工作线程"); //创建一个Handler并且关联一个Looper mHandler = new MyHandler(mWorker.getLooper()); //通过Message资源池中拿到一个Message Message message = mHandler.obtainMessage(); //将message发送到Handler所关联的线程中去 message.sendToTarget(); } /** * 这个Worker的构造方法是阻塞式的,只有当调用了Looper.prepare * 和Looper.loop之后才会构造完成,因此只要Worker构造出来了, * 那他就一定已经配置好了Looper和MessageQueue */ private static class Worker implements Runnable{ //对象锁 private Object lock = new Object(); //当前线程的Looper对象 private Looper mLooper; //构造方法 public Worker(String name){ //创建一个线程,线程的第二个参数是一个Runnable对象, //我们传入this表示这个Thread会执行此Runnable的run方法 Thread t = new Thread(null,this,name); //开启线程,这样就会去执行run方法 t.start(); //同步代码块 synchronized (lock){ //mLooper实在run方法中赋值,如果mLooper还没有 //被赋值的话,就会阻塞等待 while (mLooper == null){ try { lock.wait(); } catch (InterruptedException e) { } } } } //调用了thread.start后就会执行这个run方法 @Override public void run() { //同步代码块,记住锁一定要和上面的锁是同一个锁 synchronized (lock){ //调用prepare方法,配置looper Looper.prepare(); //给mLooper赋值 mLooper = Looper.myLooper(); //当给mLooper赋值之后,就调用notifyAll去唤醒上面 //构造方法 lock.notifyAll(); } //配置MessageQueue并且Looper开始轮巡 Looper.loop(); } //暴露一个方法来获得当前线程的looper public Looper getLooper(){ return mLooper; } } /** * 静态内部类不持有MainActivity的引用,避免内存泄露,并且重写handleMessage() * Handler发送的消息最终会在handleMessage中执行 */ private static class MyHandler extends Handler{ public MyHandler(){ } public MyHandler(Looper looper){ super(looper); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i("zhangqi","消息发送到"+Thread.currentThread().getName()); } }}
我们在Handler的handleMessage中打印一个Log,这个Log会告诉我们这个消息发送到了哪个线程中
那如果我们在创建Handler的时候不指定关联哪个Looper的话,消息会发送的主线程
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWorker = new Worker("工作线程"); //Handler不指定Looper的话,就会自动关联创建Handler线程 //的Looper,这里就会关联主线程的Looper mHandler = new MyHandler(); //通过obtainMessage可以从Message资源池中拿到一个message Message message = mHandler.obtainMessage(); //调用sendToTarge就可以将message发送到handler关联的工作线程中了 message.sendToTarget(); }
一定不要忘了调用Looper.quit来结束Looper的循环
刚才前面我们说过,如果我们不调用Looper.quit的话,那么Looper就会无限循环从MessageQueue中取消息,这将导致Thread无法停止工作,无法被GC回收,如果我们的Thread中还保存了Activity的引用的话,将导致Activity也无法被GC回收,最终会导致内存泄露,我们可以在Activity的onDestroy中判断当前线程是否结束
@Override protected void onDestroy() { super.onDestroy(); Log.i("zhangqi", "onDestroy");// mWorker.getLooper().quit(); while(mWorker.isAlive()){ Log.i("zhangqi","线程仍在运行"); } Log.i("zhangqi","线程结束运行"); }
我们发现即使Acitvity的onDestroy已经执行了,但是线程仍然在工作着,所以我们需要在onDestroy中调用Looper的quit方法
@Override protected void onDestroy() { super.onDestroy(); Log.i("zhangqi", "onDestroy"); mWorker.getLooper().quit(); while(mWorker.isAlive()){ Log.i("zhangqi","线程仍在运行"); } Log.i("zhangqi","线程结束运行"); }
我们再来看一下
最后这个线程结束了运行,因此我们千万不要忘记调用Looper的quit方法来结束他的循环,否则将导致线程永远执行下去
工作线程发送消息到工作线程
public class MainActivity extends AppCompatActivity { private Worker mWorker; private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWorker = new Worker("工作线程B"); new Thread("工作线程A") { @Override public void run() { //在工作线程A中创建Handler并且关联工作线程B的Looper mHandler = new MyHandler(mWorker.getLooper()); Message message = mHandler.obtainMessage(); //发送消息 message.sendToTarget(); } }.start(); } /** * 这个Worker的构造方法是阻塞式的,只有当调用了Looper.prepare * 和Looper.loop之后才会构造完成,因此只要Worker构造出来了, * 那他就一定已经配置好了Looper和MessageQueue */ private static class Worker implements Runnable { private Thread t; //对象锁 private Object lock = new Object(); //当前线程的Looper对象 private Looper mLooper; //构造方法 public Worker(String name) { //创建一个线程,线程的第二个参数是一个Runnable对象, //我们传入this表示这个Thread会执行此Runnable的run方法 t = new Thread(null, this, name); //开启线程,这样就会去执行run方法 t.start(); //同步代码块 synchronized (lock) { //mLooper实在run方法中赋值,如果mLooper还没有 //被赋值的话,就会阻塞等待 while (mLooper == null) { try { lock.wait(); } catch (InterruptedException e) { } } } } //调用了thread.start后就会执行这个run方法 @Override public void run() { //同步代码块,记住锁一定要和上面的锁是同一个锁 synchronized (lock) { //调用prepare方法,配置looper Looper.prepare(); //给mLooper赋值 mLooper = Looper.myLooper(); //当给mLooper赋值之后,就调用notifyAll去唤醒上面 //构造方法 lock.notifyAll(); } //配置MessageQueue并且Looper开始轮巡 Looper.loop(); } //暴露一个方法来获得当前线程的looper public Looper getLooper() { return mLooper; } public boolean isAlive(){ return t.isAlive(); } } /** * 静态内部类不持有MainActivity的引用,避免内存泄露,并且重写handleMessage() * Handler发送的消息最终会在handleMessage中执行 */ private static class MyHandler extends Handler { public MyHandler() { } public MyHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i("zhangqi", "消息发送到" + Thread.currentThread().getName()); } } @Override protected void onDestroy() { super.onDestroy(); Log.i("zhangqi", "onDestroy"); mWorker.getLooper().quit(); while(mWorker.isAlive()){ Log.i("zhangqi","线程仍在运行"); } Log.i("zhangqi","线程结束运行"); }}
我们在工作线程A中创建了一个Handler并且关联了工作线程B的Looper,在工作线程A中发送一个消息,最终消息发送到了工作线程B中。
为了验证创建Handler的时候,不手动关联Looper的话是默认关联创建Handler所在线程的Looper,我们在工作线程中创建Handler的时候不手动指定Looper,我们看看会出什么问题
抛出了运行时异常:不能在没有调用Looper.prepare的线程中创建Handler,那我们给工作线程A加上Looper.prepare和Looper.loop
new Thread("工作线程A") { @Override public void run() { Looper.prepare(); //在工作线程A中创建Handler并且不指定Looper mHandler = new MyHandler(); Message message = mHandler.obtainMessage(); //发送消息 message.sendToTarget(); Looper.loop(); } }.start();
好了,这就证实了如果我们创建Handler的时候不手动指定他要关联哪一个Looper的话,就会默认关联创建Handler的线程的Looper,但是当前线程一定要配置Looper和MessageQueue,否则会报错。
那为什么刚才在主线程中直接创建Handler没有报错呢?那是因为主线程中已经默认配置了Looper和MessageQueue,因此我们可以直接创建Handler。
结束语
关于Handler,MessageQueue和Looper已经说完了,这是我自己通过写Demo和看资料总结出来的,如果有哪些地方不对希望大家可以帮我指正。或者有哪些没说到的也希望大家帮我补充。
- Android并发编程之Handler,MessageQueue,Looper详解
- Android并发编程之Handler,MessageQueue,Looper详解
- android多线程编程详解,关于Handler ,Looper , Message , MessageQueue
- android多线程编程详解,关于Handler ,Looper , Message , MessageQueue
- android多线程编程详解,关于Handler ,Looper , Message , MessageQueue
- Android多线程编程详解,关于Handler ,Looper , Message , MessageQueue
- android多线程编程详解,关于Handler ,Looper , Message , MessageQueue
- android多线程编程详解,关于Handler ,Looper , Message , MessageQueue
- Android系统开发之五:多线程编程详解(Handler ,Looper , Message , MessageQueue)
- Android之Message,MessageQueue,Looper,Handler详解(带示例)
- Handler、Looper、MessageQueue详解
- Android 中Message,MessageQueue,Looper,Handler详解
- Android 中Message,MessageQueue,Looper,Handler详解
- Android中Handler、Looper、Message、MessageQueue详解
- Handler Looper MessageQueue之MessageQueue
- Android Handler Looper MessageQueue
- Android Handler+Looper+MessageQueue
- android handler looper MessageQueue
- 闲话Zynq UltraScale+ MPSoC (连载1)——忆老前辈Zynq-7000
- TinyOS论文04:Demo: Towards Bug-free Implementation for Wireless Sensor Networks
- build maven项目时出现错误noclassdeffounderror:org/apache/tools/ant/DefaultLogger
- jqmobi跳转链接情况分析
- 画饼图
- Android并发编程之Handler,MessageQueue,Looper详解
- 数据库SQl语言最常用的字符串(String)函数
- nyoj+区间dp石子合并
- 在Visual Studio Code配置GoLang开发环境
- iOS本地通知
- C语言之数组
- Mybatis 延迟加载
- NULL指针、零指针、野指针---详解
- 创建精灵的方法