Handler的使用及简单分析

Handler的使用及简单分析

一、Handler常用场景及使用方式

Handler在经常用在线程间通信上,是Android中常用的异步消息处理机制。线程通信通常是主线程与子线程通信或者子线程之间互相通信,主线程与子线程之间通信又包括主线程向子线程发消息和子线程向主线程发消息。常用的写法如下:

1.子线程发消息至主线程

通常我们在子线程中进行耗时操作,比如耗时的计算、网络操作等,得到的结果通常需要展示到UI,但是Android子线程更新UI不安全,所以需要将结果发送给UI线程进行展示。这时就可以使用Handler解决。主要分两步

private Handler mHandler = new Handler (){
        @Override
        public void handleMessage(Message msg) {
        	// 收到子线程的消息,进行UI的更新
            switch (msg.what){
                case 0:
                    mTextView.setText ((String)msg.obj);
                    Log.d (TAG, "handleMessage: normal message");
                    break;
                default:
                    break;
            }
        }
    };
findViewById (R.id.button).setOnClickListener (new View.OnClickListener () {

            @Override
            public void onClick(View v) {
                new Thread (new Runnable (){
                
                    @Override
                    public void run() {
                    	// 子线程发消息
                        Message message = mHandler.obtainMessage ();
                        message.what = 0;
                        message.obj = "子线程更新UI";
                        mHandler.sendMessage (message);
                    }
                }).start ();
            }
        });

首先在主线程new一个Handler并重写handleMessage(Message msg)方法,子线程中发送来的消息就在这里处理。子线程中通过这个Handler拿到一个Message对象,让该对象携带要发送的信息,并通过这个Handler发出去,于是Handler收到消息,进行更新UI等操作。
这里有一个runOnUIThread(Runnable action)也能实现,它其实也是基于Handler的。

2.主线程发消息至子线程

如果是子线程处理主线程的消息,就需要在该子线程中定义一个Handler,并使用该Handler在主线程中发消息。

	// 子线程Handler
    private Handler mOtherHandler = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate (savedInstanceState);
        setContentView (R.layout.activity_main);

        new Thread (new Runnable (){   
            @Override
            public void run() {
                Looper.prepare ();
                
                mOtherHandler = new Handler (){
                    @Override
                    public void handleMessage(Message msg) {
                        if(msg.what == 1){
                            Log.d (TAG, "handleMessage: " + (String)msg.obj);
                        }
                    }
                };
                
                Looper.loop ();
            }
        }).start ();
    }
findViewById (R.id.button2).setOnClickListener (new View.OnClickListener () {
            @Override
            public void onClick(View v) {
                if(mOtherHandler != null){
                    Message message = mOtherHandler.obtainMessage ();
                    message.what = 1;
                    mOtherHandler.sendMessage (message);
                }
            }
        });

同样是两个步骤,主线程发消息,子线程处理消息。唯一不同的是子线程中实例化Handler之前需要调用Looper.prepare ()方法,实例化之后需要调用Looper.loop ()方法,这个后面我们会进行简单分析。

3.子线程间通信

这个就和第二点基本一样了,唯一不同就是发消息是在子线程中。

二、简单分析

Handler的使用及简单分析_第1张图片
整个过程如上图,Handler发送消息到消息队列中,而Looper则负责从队列中取出消息,并通过HandlerdispatchMessage(Message msg)方法,而在该方法中会调用handlerMessage(Message msg)方法,从而实现了线程间的通信。

1.msg加入队列

Handler的一系列send方法最终调用sendMessageAtTime(Message msg, long uptimeMillis)方法,我们看一下该方法
Handler的使用及简单分析_第2张图片
它会调用enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法
Handler的使用及简单分析_第3张图片
最终调用MessageQueue#enqueueMessage(Message , long)方法
Handler的使用及简单分析_第4张图片
这个方法很长,但是其实就是做了一件事,把msg加进队列中。MessageQueue内部维护一个Message对象mMessage,而每一个Message对象内部维护一个名为next的Message对象,指向它的下一个Message。
红框中逻辑表示如果这个mMessage是空的的话,那么相当于把msg放在队列的头部,同时唤醒队列。如下图
Handler的使用及简单分析_第5张图片
紫框中的逻辑则实现了将msg放进队列中,它会遍历队列中的message,直到找到一个message它的next为空,然后把这个msg设置为它的next,并把msg的next设置为空,便于下一次加进来消息。如下图
Handler的使用及简单分析_第6张图片
至此,完成了msg加进队列的操作。

2.Looper循环拿消息并处理

消息放入队列中,需要有人去处理消息,这里就要用到Looper
Handler的使用及简单分析_第7张图片
Looper内部维护一个MessageQueue,而遍历这个队列的方法就是Looper.loop()方法。如下
Handler的使用及简单分析_第8张图片
Handler的使用及简单分析_第9张图片
这个方法也很长,但是省去log和时间计算,它其实就做了一件事,轮询队列处理消息,如果消息处理没了,就是queue.next()为空了,就跳出循环,如果消息存在,就调用它的target的dispatchMessage(Message msg)
查看Message,其内部维护了一个名为target的Handler对象Handler的使用及简单分析_第10张图片
就是调用了Handler的dispatchMessage(Message msg)方法。找到这个方法,跟进去:
Handler的使用及简单分析_第11张图片
这个handleMessage(msg)就很眼熟了,这不就是我们new出来的handler实例,然后重写的那个方法嘛!到这里,我们就完成了这个异步消息的处理。

3.一些细节问题

这里其实有一个问题,Looper是如何拿到消息队列的?其实我们在子线程使用Handler时调用了Looper.prepare()方法,
Handler的使用及简单分析_第12张图片
Handler的使用及简单分析_第13张图片
到这里就明白了为什么这两个方法一定要调用了。
但是为什么主线程使用时就没有调用呢?而且也没有出问题?这时我们需要到ActivityThread中找答案了
Handler的使用及简单分析_第14张图片
Android作为java语言开发的工程,肯定会有一个main方法,这个方法位于ActivityThread.java文件中,是整个Android工程入口方法。UI线程作为贯穿app生命周期的线程,肯定需要在工程启动时就开始配置了。果然在main方法中找到了prepare()方法和loop()方法。所以我们在主线程使用Handler就不用配置了。

你可能感兴趣的:(分享,学习笔记)