【Android 学习】深入理解Handler机制

来源:互联网 发布:java二级联动下拉列表 编辑:程序博客网 时间:2024/05/18 14:23

Android 提供了Handler和Looper来来满足线程间的通信,而前面我们所说的IPC指的是进程间的通信。这是两个完全不同的概念。大家不要搞混了。

Handler先进先出原则,Looper类用来管理特定线程内消息的交换(MessageExchange);

我们了解Handler之前首先要知晓一下几点:

  1. Looper:一个线程有一个Looper,由Looper来管理当前线程的MessageQueue(消息队列),就是无限将消息队列里的取出,一个Handler必须绑定一个Looper。
  2. Handler:Handler的主要作用是将某一任务切换到特定的线程来执行。为什么要有这个机制昵?接下来会为大家介绍:
  3. MessageQueueu:消息队列用来存放线程发出的消息。
  4. Tread:线程通常是指UI线程也就是主线程,每个线程创建时都会为其创建MessageQueue。

1、为什么会有Handler机制

我们刚说Handler机制的主要作用是将某一任务切换到特定的线程来执行,我们做项目可能都遇到过ANR(Application Not Response),这就是因为执行某项任务的时间太长而导致程序无法响应。这种情况我们就需要将这项耗时较长的任务移到子线程来执行,从而消除ANR。而我们都知道Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,那么程序就会抛出异常。而Android提供Handler就是为了解决在子线程中无法访问UI的矛盾。

2、demo

我们来先看一个简单的Demo吧!

MainActivity.java

package com.example.terry.hanlderdemo;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.widget.TextView;/** * Demo描述: * * 示例步骤如下: * 1 子线程给子线程本身发送消息 * 2 收到1的消息后,子线程给主线程发送消息 * 3 收到2的消息后,主线程给子线程发送消息 * * 为实现子线程给自己本身发送消息,关键还是在于构造Handler时传入的Looper. * 在此就传入该子线程自己的Looper即调用Looper.myLooper(),代码如下: * Looper.prepare(); * mHandlerTest1=new HandlerTest1(Looper.myLooper()); * Looper.loop(); * * 所以当mHandlerTest1.sendMessage(message);发送消息时 * 当然是发送到了它自己的消息队列. * * 当子线程中收到自己发送的消息后,可继续发送消息到主线程.此时只要注意构造 * Handler时传入的Handler是主线程的Handler即可,即getMainLooper(). * 其余没啥可说的. * * * 在主线程处理消息后再发消息到子线程 * * * 其实这些线程间发送消息,没有什么;关键还是在于构造Handler时传入谁的Looper. * */public class MainActivity extends Activity {    private TextView mTextView;    private HandlerTest1 mHandlerTest1;    private HandlerTest2 mHandlerTest2;    private int counter=0;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        init();    }    private void init() {        mTextView = (TextView) findViewById(R.id.textView);        //1 子线程发送消息给本身        new Thread() {            public void run() {                Looper.prepare();                mHandlerTest1=new HandlerTest1(Looper.myLooper());                Message message = new Message();                message.obj = "子线程发送的消息Hi~Hi";                mHandlerTest1.sendMessage(message);                Looper.loop();            }        }.start();    }    private class HandlerTest1 extends Handler {        private HandlerTest1(Looper looper) {            super(looper);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            System.out.println("子线程收到:" + msg.obj);            //2  收到消息后可再发消息到主线程            mHandlerTest2=new HandlerTest2(getMainLooper());            Message message = new Message();            message.obj = "O(∩_∩)O";            mHandlerTest2.sendMessage(message);        }    }    private class HandlerTest2 extends Handler {        private HandlerTest2(Looper looper) {            super(looper);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            mTextView.setText("在主线程中,收到子线程发来消息:" + msg.obj);            //3  收到消息后再发消息到子线程            if (counter==0) {                Message message = new Message();                message.obj = "主线程发送的消息Xi~Xi";                mHandlerTest1.sendMessage(message);                counter++;            }        }    }}


3、Android 消息机制分析

3.1 TreadLocal的工作原理

  1. TreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后只有在特定的线程中可以获取到存储的数据,对于其他线程来说则无法获取到。ThreadLocal用一句大白话来讲解,就是看上去只new了一份,但在每个不同的线程中却可以拥有不同数据副本的神奇类。其本质是ThreadLocal中的Values类维护了一个Object[],而每个Thread类中有一个ThreadLocal.Values成员,当调用ThreadLocal的set方法时,其实是根据一定规则把这个线程中对应的ThreadLocal值塞进了Values的Object[]数组中的某个index里。这个index总是为ThreadLocal的reference字段所标识的对象的下一个位置。

3.2  MessageQueue的工作原理

MessageQueue的工作原理:主要方法为enqueueMessage和next。 
 a. enqueueMessag主要就是一个单链表的插入操作, 
 b. next方法是一个无限循环,如果消息队列中没有消息,next方法就阻塞,有新消息到来时,next方法会返回这条消息并将其从单链表中删除。

3.3  Looper的工作原理: 

a. prepare方法,为当前没有Looper的线程创建Looper。
b. prepareMainLooper和getMainLooper方法用于创建和获取ActivityThread的Looper。
c. quit和quitSafely方法,前者立即退出,后者只是设定一个标记,当消息队列中的所有消息处理完毕后会才安全退出。子线程中创建的Looper建议不需要的时候都要手动终止。
d. loop方法,死循环,阻塞获取msg并丢给msg.target.dispatchMessage方法去处理,这里的target就是handler。

3.4Handler的工作原理:

a. 无论sendMessage还是post最终都是调用的sendMessageAtTime方法。
b. 发送消息其实就是把一条消息通过MessageQueue的enqueueMessage方法加入消息队列,Looper收到消息就会调用handler的dispatchMessage方法。它的处理过程参考流程图,一看就懂~ 


c. 这里我补充一个东西,当我们直接Handler h = new Handler()时,本质调用的是Handler(Callback callback, Boolean async)构造方法,这个方法里会调用Looper.myLooper()方法,这个方法其实就是返回的ThreadLocal里保存的当前线程的Looper,这也就解释了为什么我们在主线程中这样new没有问题,子线程中如果不先Looper.prepare会抛出异常的原因,前面多次说了,因为ActivityThread会在初始化的时候创建自己的Looper。

3.5主线程的消息循环:

这里写图片描述

1 0