Handler系列源码解析

Handler系列源码解析

Android小彩虹2021-07-09 6:28:28160A+A-

前言

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

image

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

浅谈理解

多线程

谈这个之前,我觉得首先需要聊聊多线程这个东西(毕竟Handler很多时候是跨线程通信的),那么,什么是多线程呢?比如,我们去银行办理业务,由于是淡季,我们发现办理窗口只开了一个,大家都得排着队一个一个来(这就是单线程),那么,开了两个甚至多个窗口同时处理银行的事务,那么这样子就等于开了多个线程(这就是多线程)。

image

了解了多线程,我们就开始转移到我们的主要战地(Handler)上面,我们使用Handler的时候,很多的时候都是在子线程中做完操作后需要UI线程(主线程)中更新界面,一般的做法就是

使用

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

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

目前为止总结的一些使用情况如下:

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

  • 2.在子线程中更新UI

  • 3.在子线程中使用Handler

  • 4.使用HandlerThread

  • 5.Handler的callback回调

1、子线程发送消息到主线程
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这个线程间通信是怎么回事。

2、在子线程中更新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喔~!

3、在子线程中使用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,不知道表达的怎么样,但是,有几个问题,在这里我来解答一下

相关问题

1、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

2、消息(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

3、消息是怎么分发的,又怎么就到了自己实现的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

可以看到,这里我们的队列对新入的消息进行了列队处理,然后,依次处理收到的消息。

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

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们