Handler,MessageQueue,Looper详解

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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值