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和看资料总结出来的,如果有哪些地方不对希望大家可以帮我指正。或者有哪些没说到的也希望大家帮我补充。

3 0