Handler系列源码解析

前言

总是看大佬们分析这个东西,也看了一些文章,总感受云里雾里,决定本身来对着源码理一理,能力有限,先写下本身所理解的,后期再加上来。bash

image

在我努力认真(边玩边睡)的阅读下,终于,瞧出了一丝门道,下面就给你们分析分析。网络

浅谈理解

多线程

谈这个以前,我以为首先须要聊聊多线程这个东西(毕竟Handler不少时候是跨线程通讯的),那么,什么是多线程呢?好比,咱们去银行办理业务,因为是淡季,咱们发现办理窗口只开了一个,你们都得排着队一个一个来(这就是单线程),那么,开了两个甚至多个窗口同时处理银行的事务,那么这样子就等于开了多个线程(这就是多线程)。 数据结构

image

了解了多线程,咱们就开始转移到咱们的主要战地(Handler)上面,咱们使用Handler的时候,不少的时候都是在子线程中作完操做后须要UI线程(主线程)中更新界面,通常的作法就是多线程

使用

Handler handler = new Handler(){
 @Override
 public void handleMessage(Message msg) {
 /**
 * 根据参数作一些操做
 */
 }
 };
​
handler.sendEmptyMessageDelayed(1,2000);
复制代码

Handler是Android线程间通信的一种方式,它常被咱们用来更新UI,是的,我是这么用,还有延时,只有拿出来总结的时候,才会发现有时候使用的时候是有缺漏的。因此总结很重要啊!异步

目前为止总结的一些使用状况以下:async

  • 1.子线程发送消息到主线程ide

  • 2.在子线程中更新UI函数

  • 3.在子线程中使用Handleroop

  • 4.使用HandlerThread源码分析

  • 5.Handler的callback回调

一、子线程发送消息到主线程
Handler mainHandler = new Handler() {
 @Override
 public void handleMessage(Message msg) {
 super.handleMessage(msg);
​
 Toast.makeText(HandlerActivity.this, "消息", Toast.LENGTH_SHORT).show();
​
 }
 };
复制代码
new Thread(new Runnable() {
 @Override
 public void run() {
 try {
 Thread.sleep(1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 /*子线程传给主线程*/
 mainHandler.sendEmptyMessage(0);
 }
 }).start();
复制代码

这里是在子线程中Handler对象发送一个空消息,而后在handleMessage方法中进行操做,此时Toast执行已是在UI线程了。

而后刚刚测试了一下,不只仅是子线程往主线程发消息,主线程也能够向子线程发消息,子线程也能够向子线程发消息,本身手动去试一下才会理解Handler这个线程间通讯是怎么回事。

二、在子线程中更新UI
Handler updateHandler = new Handler();
复制代码
new Thread(new Runnable() {
 @Override
 public void run() {
 try {
 Thread.sleep(1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 /*在子线程中更新UI*/
 updateHandler.post(new Runnable() {
 @Override
 public void run() {
 /*
 *更新UI的操做
 * */
 }
 });
 }
 }).start();
复制代码

这里的代码都是一些局部的代码块,这里的updateHandler是在主线程声明的,子线程是开在主线程下的, 而后updateHandler对象在子线程使用post方法,new了一个Runnable去切换线程到主线程执行更新UI的代码。固然,也能够像上面那样发送一个消息在Handler的handleMessage里更新UI喔~!

三、在子线程中使用Handler

匿名内部类实现

new Thread(new Runnable() {//建立一个子线程
 @Override
 public void run() {
​
 Looper.prepare();//建立与当前线程相关的Looper
 myThreadTwoHandler = new Handler() {    //建立一个子线程里的Handler
 @Override
 public void handleMessage(Message msg) {
 super.handleMessage(msg);
 Log.e(TAG, "当前存在线程为" + Thread.currentThread().getName());
 }
 };
 Looper.loop();//调用此方法,消息才会循环处理
 }
 }).start();
​
 myThreadTwoHandler.sendEmptyMessageDelayed(0, 5000);
复制代码

子类继承Thread实现

//MyThread 子类继承 Thread
 public class MyThread extends Thread {
 public Looper childLooper;
​
 @Override
 public void run() {
 Looper.prepare();//建立与当前线程相关的Looper
 childLooper = Looper.myLooper();//获取当前线程的Looper对象
 Looper.loop();//调用此方法,消息才会循环处理
 }
 }
复制代码
/*在子线程使用Handler*/
 MyThread myThread = new MyThread();
 myThread.start();
 myThreadHandler = new Handler(myThread.childLooper) {//与MyThread线程绑定
 @Override
 public void handleMessage(Message msg) {
 super.handleMessage(msg);
 Log.e(TAG, "当前存在线程为" + Thread.currentThread().getName());
 }
 };
 //主线程调用子线程的Handler对象发送消息
 myThreadHandler.sendEmptyMessageDelayed(0, 5000);
复制代码

HandlerThread实现

HandlerThread handlerThread = new HandlerThread("ceshi");
 handlerThread.start();
 //经过HandlerThread的getLooper方法能够获取Looper
 Looper looper = handlerThread.getLooper();
 //经过Looper咱们就能够建立子线程的handler了
 Handler handler = new Handler(looper) {
 @Override
 public void handleMessage(Message msg) {
 super.handleMessage(msg);
 //测试结果是在ceshi这个线程内
 Log.e(TAG, "这个是HandlerThread线程哦 : " + Thread.currentThread().getName());
 }
 };
 handler.sendEmptyMessageDelayed(0, 1000);
复制代码

Handler的内存泄露

为何会致使内存泄露呢?而在Java语言中,非静态内部类会持有外部类的一个隐式引用,因此,Handler会持有Activity的引用啦,而后就会有可能形成外部类,也就是Activity没法被回收,致使内存泄露。

那么,如何避免内存泄漏,使用正确的Handler呢?

  • 使用静态的匿名内部类,并持有外部类的弱引用

声明静态的Handler内部类,持有外部类的弱引用,经过外部类实例去引用外部类的各类控件实例,参数实例等等。而后当GC回收时,由于外部类是弱引用,因此会被回收。

/**
 * 声明一个静态的Handler内部类,并持有外部类的弱引用
 */
 private static class MyHandler extends Handler {
​
 private final WeakReference<HandlerActivity> mActivty;
​
 private MyHandler(HandlerActivity mActivty) {
 this.mActivty = new WeakReference<>(mActivty);
 }
​
 @Override
 public void handleMessage(Message msg) {
 super.handleMessage(msg);
 HandlerActivity activity = mActivty.get();
 if (activity != null) {
 Log.e("eee", "handleMessage: " + Thread.currentThread().getName());
 }
 }
 }
复制代码

在外部类中声明MyHandler对象

private final MyHandler mHandler = new MyHandler(this);
复制代码

而后调用发送消息,post的方式和sendMessage的方式

mHandler.post(sRunnable);
mHandler.sendMessage(message);
复制代码

若是使用sendMessage方法的话,会被MyHandler的 handleMessage方法接收。那么,若使用post方法的话,咱们还须要声明一个静态的Runable来完成咱们的post

private static final Runnable sRunnable = new Runnable() {
 @Override
 public void run() {
 // ...你的操做
 Log.e(TAG, "这里是run");
 }
 };
复制代码

异步任务引起的资源泄露,好比handler或者thread。这种状况发生的缘由主要是异步任务的生命周期与activity生命周期不一样步形成的,以handler中的message为例:

Handler handler =  new Handler();
handler.postDelayed(new Runnable() {
 @Override
 public void run() {
 tvContent.setText("newContent");
 }
}, 2000);
handler.obtainMessage(1).sendToTarget();
复制代码

不论是使用哪一种形式来发送message,message都会直接或者间接引用到当前所在的activity实例对象,若是在activity finish后,还有其相关的message在主线程的消息队列中,就会致使该activity实例对象没法被GC回收,引发内存泄露。因此通常咱们须要在onDestroy阶段将handler所持有的message对象从主线程的消息队列中清除。示例以下:

@Override
protected void onDestroy() {
 super.onDestroy();
 if (handler != null) {
 handler.removeCallbacksAndMessages(null);
 }
}
复制代码

源码分析

在这里,我就不粘贴太多的源码了,毕竟大佬们的文章里面都写的很详细,我在这里就用我本身的理解给你们举个栗子。

举个栗子

仍是拿银行的业务做比喻,在Handler的整套流程中,有四个主要的类 Looper、Handler、MessageQueue、Message。那么在咱们去银行的办理业务流程中分别表明啥咧?

假设只有一个办理窗口,能够看作是主线程,咱们本身作的事情也是一个子线程作的一些准备工做,如网络请求,如今咱们须要处理一些事务(咱们本身处理不了的事务),须要银行窗口处理(类同于咱们当前的子线程的更新UI的操做须要给到主线程去处理),那么,到银行的第一步咱们须要去取窗口排队的编号,这里取号的过程,就能够理解为咱们去获得相应线程的new Handler对象同样,毕竟咱们的事务须要到这个窗口才能处理。而后取号成功时就向银行的消息处理系统里面添加了一个新的待处理消息,这里能够理解为发送了一个Message(Runable),而后,在银行的消息处理系统中,咱们的这个消息就被排在了上一个消息的后面,先来先处理嘛,这里就能够用MessageQueue里面加入了新的消息(Message)来比喻;这个系统是用来循环检查是否还有人在排队等候,若是有的话,就提示这个编号到窗口去处理业务,那么这个循环的工做就是Looper来作的,它从MessageQueue里面循环的去检查队列里面是否还有有消息(Message),有的话就把这个Message分发给对应的Handler对象去处理。这里的能够比喻为银行广播提示咱们能够去窗口(Handler)处理咱们的事务了。

image

那么,整个过程能够理解为,咱们获得主线程Handler的一个实例对象,而后经过这个对象向消息队列(MessageQueue)里面添加新的消息(Message),而后Looper一直在那里循环检查队列是否有消息,有的话就把这个消息分发给咱们获得的这个Handler对象来处理,这样子,一个消息的流程就走完了(简单的过程)

image

Emmmmm,不知道表达的怎么样,可是,有几个问题,在这里我来解答一下

相关问题

一、Looper一直运行,那它啥时候启动的咧?

这就好比银行的消息系统同样,确定是要启动了以后,才能去处理事务的,那银行的系统是一通电一开门就打开了,咱们主线程的Looper从哪里启动的咧?

从这里开始,咱们就大概的过一下源码,从new Handler下手,咱们看哈实例化时作了啥

final Looper mLooper;
 final MessageQueue mQueue;
 final Callback mCallback;
​
public Handler(Callback callback, boolean async) {
 if (FIND_POTENTIAL_LEAKS) {
 final Class<? extends Handler> klass = getClass();
 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
 (klass.getModifiers() & Modifier.STATIC) == 0) {
 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
 klass.getCanonicalName());
 }
 }
​
 mLooper = Looper.myLooper();   //1
 if (mLooper == null) {
 throw new RuntimeException(
 "Can't create handler inside thread " + Thread.currentThread()
 + " that has not called Looper.prepare()");
 }
 mQueue = mLooper.mQueue;   //2
 mCallback = callback; 
 mAsynchronous = async;
 }
复制代码

这里我标注出了2个地方,第一个,咱们获得一个Looper对象,第二个是从这个Looper对象里面获得MessageQueue的引用对象,那么获得Looper对象的方法作了啥,咱们瞧瞧

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
 return sThreadLocal.get();
}
复制代码

能够看到,这里是从当前线程中取出Looper对象,很奇怪的是,这里只有取,没有看到存,咱们初始化的时候也只作了这些操做呀,那么啥时候存的咧?从大佬们的总结中能够看到,主线程的Looper的初始化是在ActivityThread的main方法中进行的

public static void main(String[] args) {
 ....
 Looper.prepareMainLooper();
​
 ...
 Looper.loop();
 .....
 }
复制代码

这里,首先调用了prepareMainLooper方法,而后调用了loop方法,咱们来看看这两个方法分别作了啥?(省略了大量代码,有兴趣仔细看的能够本身去看源码)

public static void prepareMainLooper() {
 prepare(false);
 ......
 }
​
private static void prepare(boolean quitAllowed) {
 ......
 sThreadLocal.set(new Looper(quitAllowed));
 }
​
public static void loop() {
 ......
 for (;;) {
 Message msg = queue.next(); // might block
 ......
 msg.recycleUnchecked();
 }
 }
复制代码

从这几个方法里面看到,咱们的 存 是在这里进行的,而后loop方法就让这个Looper开始循环的检查消息队列了。

image

二、消息(Message)那么多,咱们怎么知道是那个Handler发的,那最后处理的时候怎么找到这个Handler来处理列?

这个问题,咱们就须要看一哈Message的源码

....
public int what;
public int arg1;
public int arg2;
public Object obj;
Handler target;
Message next;
....
复制代码

咱们能够看到,这些参数中,有个target(Handler ),其实,这个就是最后分发消息的时候,消息能找到对应Handler的字段(这里咱们看到了next(Message),熟悉数据结构单链表的哥们确定知道是啥,因此从这个字段能够看出,MessageQueue控制了这个单链表的存取方式为队列的方法。)

那么,这个target是啥时候设置上的?咱们来看看,咱们发消息的操做

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
 Message msg = Message.obtain();
 msg.what = what;
 return sendMessageDelayed(msg, delayMillis);
 }
​
public final boolean sendMessageDelayed(Message msg, long delayMillis)
 {
 if (delayMillis < 0) {
 delayMillis = 0;
 }
 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
 }
​
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
 MessageQueue queue = mQueue;
 if (queue == null) {
 RuntimeException e = new RuntimeException(
 this + " sendMessageAtTime() called with no mQueue");
 Log.w("Looper", e.getMessage(), e);
 return false;
 }
 return enqueueMessage(queue, msg, uptimeMillis);
 }
​
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
 msg.target = this;
 if (mAsynchronous) {
 msg.setAsynchronous(true);
 }
 return queue.enqueueMessage(msg, uptimeMillis);
 }
复制代码

方法的调用过程有点长,咱们就简单的理解为,新建了一个Message对象,而后经过MessageQueue的引用对象把这个Message对象添加到队列中去了。咱们重点看最后一个方法的这一句

msg.target = this;
复制代码

在这里,咱们就知道了,target是在发送(添加消息的时候)设置上的。

image

三、消息是怎么分发的,又怎么就到了本身实现的handleMessage方法了呢?

刚才在前面,咱们大概看了一下Looper的loop方法,里面是个死循环一直从MessageQueue里面取消息,那取到消息以后咧?咱们来看看

for (;;) {
 Message msg = queue.next(); // might block
 .....
 final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
 final long dispatchEnd;
 try {
 msg.target.dispatchMessage(msg);   //1
 dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
 } finally {
 if (traceTag != 0) {
 Trace.traceEnd(traceTag);
 }
 }
 .....
​
 msg.recycleUnchecked();
 }
复制代码

在标注1的地方,咱们就能够看到,这里就调用了这个消息(Message)的Handler的dispatchMessage方法

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

在这里,咱们就看到了,它最后调用了handleMessage方法。

模拟Handler消息机制

了解了这些内容,是否是感受本身收获满满哒,莫慌还有一个大礼给你们。

咱们来使用多线程模仿Handler消息机制。那么开始,首先是四大类。

Handler类

咱们定义一个Handler类,里面有个MesQueue 的引用和Looper的引用,而后在构造函数里面进行初始化,定义一个发送消息的方法sendMessage和一个接收消息的方法handleMessage。

public class Handler {
 MesQueue queue;
 Looper looper;
​
 public Handler() {
 this.looper = Looper.myLoop();
 queue = looper.queue;
 }
​
 public Handler(Looper looper) {
 this.looper = looper;
 if (looper == null)
 System.out.println("looper is null");
 queue = looper.queue;
 }
​
 public void sendMessage(Message message){
 message.target = this;
 queue.enterQueue(message);
 }
​
 public void dispatchMessage(Message mes){
 handleMessage(mes);
 }
​
 public void handleMessage(Message msg) {
 }
}
复制代码

Looper类

public class Looper {
​
 static final ThreadLocal<Looper> mlocal = new ThreadLocal<Looper>();
​
 public MesQueue queue;
​
 public Looper() {
 queue = new MesQueue();
 }
​
 public static Looper myLoop() {
 return mlocal.get();
 }
​
 public static void prepare(){
 mlocal.set(new Looper());
 }
​
 public static void loop(){
 Looper me = myLoop();
 MesQueue queue = me.queue;
 for (;;) {
 try {
 Thread.sleep(3000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 Message message = queue.next();
 message.target.dispatchMessage(message);
 }
 }
} 
复制代码

使用ThreadLocal存储线程变量,带有MesQueue 的引用,prepare方法初始化当前线程的Looper对象,myLoop获得当前线程的Looper对象,loop方法开始运做处理消息队列的消息。

MesQueue

public class MesQueue {
​
 Message front ;
 Message rear;
​
 public MesQueue() {
 front = new Message();
 rear = front;
 front.next = null;
 }
​
 public synchronized void enterQueue(Message message){
 message.next = null;
 if (front.next == null){
 front.next = message;
 rear = message;
 notifyAll();
 }else {
 rear.next = message;
 rear = message;
 }
​
 System.out.println(Thread.currentThread() + " put Message what = " + message.what);
 }
​
 public synchronized Message next(){
 Message temp;
 while (true){
 if (front.next == null) {
 try {
 System.out.println("队列为空,开始阻塞");
 wait();
 } catch (InterruptedException e) {
 System.out.println("队列为空,异常");
 e.printStackTrace();
 }
 }else {
 temp = front.next;
 front.next = temp.next;
 break;
 }
 }
 return temp;
 }
}
复制代码

在构造方法里面初始化队列,定义enterQueue入队列的方法,next出队列的方法。

Message

public class Message {
 public Object obj;
 public int what;
 public Handler target;
 public Message next;
}
复制代码

这个就是消息结点了。

多线程测试

Main(模拟主线程)
public class MessageHandlerQus {
​
 public static void main(String[] args) {
​
 Looper.prepare();
​
 //模拟子线程向主线程发送消息
 Handler handler = new Handler(){
 @Override
 public void handleMessage(Message msg) {
 System.out.println(Thread.currentThread() +" msg what = " + msg.what);
 }
 };
​
 //模拟子线程向子线程发送消息
 ThreadB threadB = new ThreadB(){
 @Override
 protected void onLooperPrepared(Looper looper) {
 Handler handlerB = new Handler(looper){
 @Override
 public void handleMessage(Message msg) {
 System.out.println(Thread.currentThread() +" msg what = " + msg.what);
 }
 };
 ThreadA threadA = new ThreadA(handlerB);
 threadA.start();
​
 }
 };
 threadB.start();
​
 ThreadA threadA = new ThreadA(handler);
 threadA.start();
​
 Looper.loop();
 System.out.println("主线程结束");
 }
}
复制代码
ThreadA
public class ThreadA extends Thread {
​
 Handler handler;
 int value = 1;
​
 public ThreadA(Handler handler) {
 this.handler = handler;
 }
​
 @Override
 public void run() {
 System.out.println("run");
 while (true){
 Message message = new Message();
 message.what = value++;
 handler.sendMessage(message);
 try {
 Thread.sleep(1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }
}
复制代码
ThreadB
public class ThreadB extends Thread {
 Looper looper;
​
 public ThreadB() { }
​
 @Override
 public void run() {
 Looper.prepare();
 synchronized (this) {
 looper = Looper.myLoop();
 notifyAll();
 }
 onLooperPrepared(looper);
 Looper.loop();
 System.out.println("B end");
 }
​
 protected void onLooperPrepared(Looper looper) {
 }
}
复制代码
运行结果
run
run
Thread[Thread-2,5,main] put Message what = 1
Thread[Thread-1,5,main] put Message what = 1
Thread[Thread-2,5,main] put Message what = 2
Thread[Thread-1,5,main] put Message what = 2
Thread[Thread-2,5,main] put Message what = 3
Thread[Thread-1,5,main] put Message what = 3
Thread[Thread-0,5,main]  msg what = 1
Thread[main,5,main]  msg what = 1
Thread[Thread-1,5,main] put Message what = 4
Thread[Thread-2,5,main] put Message what = 4
Thread[Thread-1,5,main] put Message what = 5
Thread[Thread-2,5,main] put Message what = 5
Thread[Thread-2,5,main] put Message what = 6
Thread[Thread-1,5,main] put Message what = 6
Thread[main,5,main]  msg what = 2
Thread[Thread-0,5,main]  msg what = 2
Thread[Thread-2,5,main] put Message what = 7
Thread[Thread-1,5,main] put Message what = 7
Thread[Thread-1,5,main] put Message what = 8
Thread[Thread-2,5,main] put Message what = 8
Thread[Thread-1,5,main] put Message what = 9
Thread[Thread-2,5,main] put Message what = 9
Thread[main,5,main]  msg what = 3
Thread[Thread-0,5,main]  msg what = 3
Thread[Thread-1,5,main] put Message what = 10
Thread[Thread-2,5,main] put Message what = 10
Thread[Thread-1,5,main] put Message what = 11
Thread[Thread-2,5,main] put Message what = 11
Thread[Thread-1,5,main] put Message what = 12
Thread[Thread-2,5,main] put Message what = 12
Thread[main,5,main]  msg what = 4
Thread[Thread-0,5,main]  msg what = 4
Thread[Thread-1,5,main] put Message what = 13
Thread[Thread-2,5,main] put Message what = 13
复制代码

能够看到,这里咱们的队列对新入的消息进行了列队处理,而后,依次处理收到的消息。

好了,这篇文章先到这里,后期有新的的再补充(客套话)。

相关文章
相关标签/搜索