天天看点

Handler基本原理先来说一下流程Handler的简单使用

在Android开发中,Handler机制是一个很重要的知识点,主要作用是消息通信。

Handler基本原理

Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

先来说一下流程

在ActivityThread中通过prepareMainLooper()创建looper,并通过threadLocal将当前线程与Looper绑定【一个线程绑定一个Looper】,同时也创建了MessageQueue【一个Looper维护一个MessageQueue队列】。

然后Looper.loop()开启死循环,当没有message时,循环是阻塞的

handler在子线程中发送了一条消息,将handler实例赋值给target,并将消息按时间的顺序插入到MessageQueue队列中,循环会被唤醒,looper取出消息,通过target开始分发到对应的handler回调方法中【一个线程可以对应多个Handler】

各种类的含义

Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。

MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。

Thread:线程,负责调度整个消息循环,即消息循环的执行场所

可以把handler基本工作原理看作一个传送履带,HandlerMessage发送消息,消息内容会往MessageQueue消息队列中添加,同时开启线程,一个线程对应一个Looper,Looper.loop()方法会无限循环调用MessageQueue的next()方法来获取新消息,而next是是一个阻塞操作,但没有信息时,next方法会一直阻塞在那里,这也导致loop方法一直阻塞在那里。如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息。最后handler接收处理消息。

Handler的简单使用

我们一般使用handler发送消息,只需要两步,首先是创建一个Handler对象,并重写handleMessage方法,就是上图中的3(Message.target.handleMeesage),然后需要消息通信的地方,通过Handler的sendMessage方法发送消息(这里我们创建了一个子线程,模拟子线程向主线程发送消息)。

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";

    private Handler mHandler;
    private Button btnSendeToMainThread;
    private static final int MSG_SUB_TO_MAIN= 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 1.创建Handler,并重写handleMessage方法
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                // 处理消息
                switch (msg.what) {
                    case MSG_SUB_TO_MAIN:
                        // 打印出处理消息的线程名和Message.obj
                        Log.e(TAG, "接收到消息: " +    Thread.currentThread().getName() + ","+ msg.obj);
                        break;
                    default:
                        break;
                }
            }
        };

        btnSendeToMainThread = (Button) findViewById(R.id.btn_sendto_mainthread);
        btnSendeToMainThread .setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建一个子线程,在子线程中发送消息
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message msg = Message.obtain();
                        msg.what = MSG_SUB_TO_MAIN;
                        msg.obj = "这是一个来自子线程的消息";
                        // 2.发送消息
                        mHandler.sendMessage(msg);
                    }
                }).start();
            }
        });
    }
}
           

代码如下:点击了发送按钮,看log:

Handler基本原理先来说一下流程Handler的简单使用

Looper.prepare()

public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        // 规定了一个线程只有一个Looper,也就是一个线程只能调用一次Looper.prepare()
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 如果当前线程没有Looper,那么就创建一个,存到sThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }
           

从上面的代码可以看出,一个线程最多只有一个Looper对象。当没有Looper对象时,去创建一个Looper,并存放到sThreadLocal中,sThreadLocal是一个static的ThreadLocal对象,关于它的详细使用,以后有机会再介绍,这里只要知道,它存储了Looper对象的副本,并且可以通过它取得当前线程在之前存储的Looper的副本。如下图:

Handler基本原理先来说一下流程Handler的简单使用

接下来看Looper的构造方法:

private Looper(boolean quitAllowed) {
        // 创建了MessageQueue,并供Looper持有
        mQueue = new MessageQueue(quitAllowed);
        // 让Looper持有当前线程对象
        mThread = Thread.currentThread();
    }
           

这里主要就是创建了消息队列MessageQueue,并让它供Looper持有,因为一个线程最大只有一个Looper对象,所以一个线程最多也只有一个消息队列。然后再把当前线程赋值给mThread。

MessageQueue的构造方法没有什么可讲的,它就是一个消息队列,用于存放Message。

所以Looper.prepare()的作用主要有以下三点

  1. 创建Looper对象
  2. 创建MessageQueue对象,并让Looper对象持有
  3. 让Looper对象持有当前线程

new Handler()

Handler有很多构造方法,主要是提供自定义Callback、Looper等,我们先从最简单的无参构造方法看起:

public Handler() {
        this(null, false);
    }

    public Handler(Callback callback, boolean async) {
      // 不相关代码
       ......
        //得到当前线程的Looper,其实就是调用的sThreadLocal.get
        mLooper = Looper.myLooper();
        // 如果当前线程没有Looper就报运行时异常
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 把得到的Looper的MessagQueue让Handler持有
        mQueue = mLooper.mQueue;
        // 初始化Handler的Callback,其实就是最开始图中的回调方法的2
        mCallback = callback;
        mAsynchronous = async;
    }
           

首先,调用了Looper.myLooper,其实就是调用sThreadLocal.get方法,会得到当前线程调用sThreadLocal.set保存的Looper对象,让Handler持有它。接下来就会判断得到的Looper对象是否为空,如果为空,就会报

"Can't create handler inside thread that has not called Looper.prepare(),这不就是我们之前在没有调用Looper.prepare就在子线程中创建Handler时报的错误嘛。的确,当我们没有调用Looper.prepare(),则当前线程中是没有Looper对象的。

然后,让Handler持有得到的Looper对象的MessageQueue和设置处理回调的Callback对象(最开始图中的回调方法2)。

到这里,默认的Handler的创建过程就结束了,主要有以下几点

  1. 创建Handler对象
  2. 得到当前线程的Looper对象,并判断是否为空
  3. 让创建的Handler对象持有Looper、MessageQueu、Callback的引用

(查看更多(出处))