Android 开发艺术探索笔记(十四) 之 Android 消

2018-04-08  本文已影响32人  innovatorCL

一、Android 消息机制概述

Android 消息机制是指 Handler 的运行机制,Handler 的运行需要底层的 MessageQueue (消息队列)Looper(消息循环) 的支撑。Handler 主要是将一个任务切换到某个指定的线程中去执行。

二、Handler 的使用栗子

public class MainActivity extends Activity implements Button.OnClickListener {

    private TextView statusTextView;

    //uiHandler在主线程中创建,所以自动绑定主线程的 Looper
    private Handler uiHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusTextView = (TextView)findViewById(R.id.statusTextView);
        Button btnDownload = (Button)findViewById(R.id.btnDownload);
        btnDownload.setOnClickListener(this);
        System.out.println("Main thread id " + Thread.currentThread().getId());
    }

    @Override
    public void onClick(View v) {
        DownloadThread downloadThread = new DownloadThread();
        downloadThread.start();
    }

    class DownloadThread extends Thread{
        @Override
        public void run() {
            try{
                System.out.println("DownloadThread id " + Thread.currentThread().getId());
                System.out.println("开始下载文件");
                //此处让线程DownloadThread休眠5秒中,模拟文件的耗时过程
                Thread.sleep(5000);
                System.out.println("文件下载完成");
                //文件下载完成后更新UI
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("Runnable thread id " + Thread.currentThread().getId());
                        MainActivity.this.statusTextView.setText("文件下载完成");
                    }
                };
                uiHandler.post(runnable);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

Handler 是在创建的时候绑定实例化所在的线程的 Looper,所以当 Handler 发送消息的时候,所绑定的 Looper 就会在绑定的线程中取消息,从而将任务切换到制定的线程中执行。

三、Handler 机制分析

举个栗子:

public class MainActivity extends AppCompatActivity {

    private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBooleanThreadLocal.set(true);
        Log.i("TAG","[MainThread] 的值:"+mBooleanThreadLocal.get());

        new Thread("Thread#1"){
            @Override
            public void run() {
                mBooleanThreadLocal.set(false);
                Log.i("TAG","[Thread#1] 的值:"+mBooleanThreadLocal.get());
            }
        }.start();


        new Thread("Thread#2"){
            @Override
            public void run() {
                Log.i("TAG","[Thread#2] 的值:"+mBooleanThreadLocal.get());
            }
        }.start();
    }
}

打印结果:

04-06 08:30:01.403 2824-2824/com.innovator.handlertest I/TAG: [MainThread] 的值:true
04-06 08:30:01.408 2824-2849/com.innovator.handlertest I/TAG: [Thread#1] 的值:false
04-06 08:30:01.411 2824-2850/com.innovator.handlertest I/TAG: [Thread#2] 的值:null

可以看到在哪个线程设置了值后,在对应线程取出的数据不受其他线程的影响。原因就是:不同线程访问同一个 ThreadLocal 对象的 get(),ThreadLocal 内部会从各自的线程中取出一个数组,然后在从数组中根据当前 ThreadLocal 的索引去查找对应的 value 值。显然不同线程的数组是不同的。

Android 中的消息队列是指 MessageQueue,它主要包含两个操作:enqueueMessage(插入消息)next(读取删除消息)。原理主要是单链表的插入和删除操作。

打印结果:

Runnable 运行在:Thread#3

可见 Runnable 是运行在 Handler 实例化的线程,Looper.loop(); 是一个死循环操作,一直阻塞读取消息,所以如果在子线程中使用 Looper,记得要在处理完所有事情后调用 quit() 终止消息循环,否则子线程就一直处于等待的状态。

Handler 的 diapatchMessage()

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

首先检查 Message 的 callback 是否为 null,不为 null 就通过 handleCallback(msg); 来处理消息。 Message 的 callback 是一个 Runnable 对象,就是 Handler 的 post() 的参数,其实就是执行这里面的 run() 方法的内容。

其次就是检查 mCallBack 是否为空,不为空就调用 mCallBackhandleMessage()

所以在实例化 Handler 的时候有两种构造方法:

mMainHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message message) {
                return false;
            }
        });
mMainHandler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                
            }

        };

四、Handler、Looper、MessageQueue的关系

Looper 负责的就是创建一个 MessageQueue,然后进入一个无限循环体。在这个循环体中不断从该 MessageQueue 中读取消息,而消息的创建者就是一个或多个 Handler 。

Looper 的 prepare():

public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        // 保证了一个线程只有一个 Looper,一个 MessageQueue
        sThreadLocal.set(new Looper(true));  
} 

这里可以看到 Looper 里面有一个 ThreadLocal 的成员变量,所以 Looper 能保证每个线程存放的 Looper 对象都不一样。

Looper 的构造方法:

private Looper(boolean quitAllowed) {  
        mQueue = new MessageQueue(quitAllowed);  
        mRun = true;  
        mThread = Thread.currentThread();  
}  

可以看到 Looper 实例化了一个 MessageQueue 对象并保存了当前的线程对象。到这里我们就更能确定 Looper 只与绑定的线程有关,这也是为了后面 Handler 能将任务切换到指定的线程做铺垫。

具体的流程:

1、首先 Looper.prepare() 在本线程中保存一个 Looper 实例,然后该实例中保存一个 MessageQueue 对象;因为 Looper.prepare() 在一个线程中只能调用一次,所以 MessageQueue 在一个线程中只会存在一个。

2、Looper.loop() 会让当前线程进入一个无限循环,不断从MessageQueue 的实例中读取消息,然后回调 msg.target.dispatchMessage(msg) 方法。

3、Handler 的构造方法,会首先得到当前线程中保存的 Looper 实例,进而与 Looper 实例中的 MessageQueue 相关联。

4、Handler 的sendMessage() 方法,会给 msg 的 target 赋值为
handler 自身,然后加入 MessageQueue 中。

5、在构造 Handler 实例时,我们会重写 handleMessage() 方法,也就是 msg.target.dispatchMessage(msg) 最终调用的方法。

看完这么多分析后,我可以推断我之前想到的一个问题了:一个线程能拥有多个 Handler 吗?这些 Handler 会互相干扰吗?

答案:一个线程可以拥有多个 Handler,并且这些 Handler 都是共用同一个线程的 Looper 和 MessageQueue,而且不会互相干扰,因为 Looper 取消息后的回调是 dispatchMessage(),这里的 msg.target 是之前发送消息的那个 Handler。

五、参考资料

上一篇下一篇

猜你喜欢

热点阅读