版权声明:本文为博主原创文章,未经博主允许不得转载。
PS:转载请注明出处 作者: 地址: 本文出自
教程简介
- 1、阅读对象
本篇教程适合新手阅读,老手直接略过
-
2、教程难度
初级
正文
摘要:
Handler 在 Android 的作用主要是线程间通讯的,现在也有各种文章在讲解 Handler 的作用以及源码分析,但是必定这些都是别人自己的总结和整理,和自己总结还是有区别的,为了加深自己的记忆所以自己也来分析一下 Handler 以及它的小伙伴们。
什么是 Handler
什么是 Handler?
再华丽的解释也不过是官方的解解释吧:先看官网上的一段话
A Handler allows you to send and process Message and Runnable objects associated witha thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.复制代码
大概意思就是 Handler 可以发送 Message 和 Runnable 对象发送到 Handler 所关联的线程队列中去,每个 Handler 的实例都会绑定到创建它的线程中。
Handler 的作用
再来看官网杂说的
There are two main uses for a Handler:(1) to schedule messages and runnables to be executed as some point in the future(2) to enqueue an action to be performed on a different thread than your own.复制代码
大概意思就是说,Handler 有两个用途:
(1)、在某一时刻执行某些事情 (2)、在不同的线程中执行操作 (也就是线程间通信)
Handler 常用的方法
方法名 | 描述 |
---|---|
boolean post (Runnable r) | 立即执行操作 |
postAtTime(Runnable, long) | 在指定的时间执行某些操作 |
postDelayed(Runnable, long) | 延时执行某些操作 |
sendEmptyMessage(int) | 发送一个含有what的信息 |
sendMessage(Message) | 发送一个Message |
sendMessageAtTime(Message, long) | 在指定的时间发送消息 |
sendMessageDelayed(Message, long) | 延时发送消息 |
Handler 默认是运行在 UI 线程中的,默认默认默认...哦 一定记得,至于为什么,我们后面会说的。
Handler 常用的使用方式
1、解决 ANR,子线程更新UI线程
在Android应用程序中我们最常见的异步莫过于 ANR 了(主线程执行耗时操作了,发起网络请求,或操作 IO 等),通常情况下在 Activity 是5秒钟响应就会报这个错,在 BroadCast 中是 10 秒钟,使用 Handler 可以很容易的解决这个问题。
解决方法:
- 1、定义 Handler 并重写 handleMessage 方法
//在UI线程中 private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: //取得传递过来的 msg String passedChangeTv = (String) msg.obj; tv.setText(passedChangeTv); break ; } } } ;复制代码
- 2、在子线程中去执行耗时操作
//比如请求服务器,然后更新 TextView new Thread(new Runnable() { @Override public void run() { //执行耗时操作 /** * 1 请求服务器 * 2 解析数据 * 3 获取数据 */ //比如从服务器取得的数据是" Handler 应用" String changeTv = "Handler应用" ; Message message = new Message() ; message.what = 1 ; message.obj = changeTv ; mHandler.sendMessage(message) ; } }).start();复制代码
这样就可以解决 ANR
2、可以用作轮询,或是定时器
- 1、举个栗子,我们要一秒钟打印一个字符串,我们可以使用 Handler 的 postDelayed 方法
private Handler mtimeTaskHandler = new Handler() ; private void timerTaskHandler() { //调用Handler自带的 mtimeTaskHandler.postDelayed(new Runnable() { @Override public void run() { Log.e("===","TAG:"+ System.currentTimeMillis()/1000) ; mtimeTaskHandler.postDelayed(this,1000) ; } },1000) ; }复制代码
- 2、一秒钟请求一次服务器,然后把服务器的最新数据显示在 TextView ,在这里我们使用 Handler 的sendMessageDelayed(Message msg,Long deayTime)方法
private Handler preSecondHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: //取得传递过来的 msg int passedChangeTv = (int) msg.obj; tv.setText(passedChangeTv+""); //再次调用获取服务器最新数据方法 preSecondGetServer() ; break ; } } } ; private int i ; private void preSecondGetServer() { new Thread(new Runnable() { @Override public void run() { //假设这里从服务器取回来的 i i++ ; Message message = new Message() ; message.what = 1 ; message.obj = i ; preSecondHandler.sendMessageDelayed(message,1000) ; } }).start();复制代码
这样就可以一秒请求一次服务器,并且把结果显示在 TextView 上了
3、两个子线程进行通信
Handler的作用就是是进行线程间通信的,上面说的都是UI线程(主线程)和子线程之间的通信,那么两个工作线程(子线程)可以通信吗,答案是肯定 的。
- 1、先声明 Handler 所在的线程
//成员变量 子线程中的 Handler private Handler threadHandler ;class MyThread extends Thread{ { //一实例化就开启线程 代码块 start(); } @Override public void run() { super.run(); // Handler 所在的线程默认是没有 looper 的(除了 Activity ) Looper.prepare(); threadHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //取得传递过来的 msg String passedChangeTv = (String) msg.obj; //设置显示 tv.setText(passedChangeTv); } } ; Looper.loop(); } }复制代码
注意:子线程默认是没有 Looper 的所以我们在子线程中一定要声明 Looper.prepare() 和 Looper.loop() 在它们之间去初始化 Handler
- 2、在另一个子线程中发送消息
private void threadHandlerTask() { new Thread(new Runnable() { @Override public void run() { String changeTv = "Handler应用" ; Message message = new Message() ; message.what = 1 ; message.obj = changeTv ; threadHandler.sendMessage(message) ; } }).start() ; }复制代码
- 3、最后在合适的地方调用,在这里我们为了演示就在 Activity 的onCreate() 方法中调用
MyThread t = new MyThread() ; threadHandlerTask() ;复制代码
以上就完成了两个子线程通过 Handler 来通信。
注意:上面的子线程能信有问题,不知道细心的朋友发现了没有,我故意留了一个 bug,忽略此 bug 不提,我们上面的程序性能是有问题的,我们一一解决。
1、上面代码存在的 bug
细心的朋友会说我靠,擦,FK,竟然在子线程中更新 UI,我们要以清楚的看到 threadHandler 是在子线程中的,那么我们是不能更新 UI的。会 ANR 的好不好呀,没错就是这个问题,不怕,程序员就是发现问题并解决问题的,我们来解决这个问题,修改代码如下。
- 1、首先在 MainActivity 中声明一个 threadHander( threadHander 一定是在主线程中的)
private Handler threadHander = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //取得传递过来的 msg String passedChangeTv = (String) msg.obj; tv.setText(passedChangeTv+""); } } ;复制代码
所以上面代码是可以更新 UI 的没有问题
- 2、声明子线程
class MyThread extends Thread{ private Looper mLooper ; private Handler mHandler ; /** * 代码块或是构造方法中开启线程都可以 */ public MyThread(){ Log.e("t所在的线程",this.getName()) ; start(); }// { // //一实例化就开启线程 代码块// start();// } @Override public void run() { super.run(); // Handler 所在的线程默认是没有 looper 的(除了Activity) Looper.prepare(); mLooper = Looper.myLooper() ; mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //取得传递过来的 msg String passedChangeTv = (String) msg.obj; //设置显示 这里的 Handler 运行在子线程中去的,所以不能更新UI // tv.setText(passedChangeTv); threadHander.sendMessage(threadHander.obtainMessage(1,passedChangeTv)) ; } } ; Looper.loop(); } public Handler getThreadHandler(){ return mHandler!=null?mHandler:null ; } }复制代码
- 3、在另一个线程中去发送消息
private void threadHandlerTask() { new Thread(new Runnable() { @Override public void run() { String changeTv = "Handler应用" ; Message message = new Message() ; message.what = 1 ; message.obj = changeTv ; t.getThreadHandler().sendMessage(message) ; } }).start() ; }复制代码
此处的t就是 MyThread 的实例,声明成成员变量,不用纠结
我们来捋一捋流程,首先调用
t.getThreadHandler().sendMessage(message)复制代码
将消息发送到 MyThread 的 Handler 中,然后在 MyThread 的 Handler (子线程中)中将消息发送到 MainActivity 的 threadHander(UI线程中),这样就完成了,子线程更新UI线程的过程
注:这里是为了显示更新UI线程所以多次发送,如果只是为了两个线程间通信,那么 threadHander 是可以不需要的
我们把改后的代码运行一下,并且我添加了一些线程的日志,看效果
从图中我们可以看到,子线程间通信是完全没有问题的,但是我们退出应用再进入应用的时候发现,线程数量在不停的增加,我靠如果打开 100 次岂不是又多了 100 个线程,1000... 次呢,想都不敢想。如何解决呢,往下看。
2、手动调用 Looper 需要手动释放
接着上面继续说,问题出现在那里呢,就是手动调用 Looper.perpare() 和 Looper.loop() 的时候一定要记得在任务完成以后,或是合适的地方要释放掉 looper,要调用 looper.quit(),结束消息队列,进而结束线程。如果不这么做,thread 会长时间存在不销毁这。这就是上面所说的性能问题,不断的开辟线程,线程的开辟是需要开销的,所以我们来解决这一问题
- 1、我们改造MyThread类
class MyThread extends Thread{ private Looper mLooper ; private Handler mHandler ; /** * 代码块或是构造方法中开启线程都可以 */ public MyThread(){ Log.e("t所在的线程",this.getName()) ; start(); }// { // //一实例化就开启线程 代码块// start();// } @Override public void run() { super.run(); //Handler所在的线程默认是没有looper的(除了Activity) Looper.prepare(); mLooper = Looper.myLooper() ; mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //取得传递过来的 msg String passedChangeTv = (String) msg.obj; //设置显示 这里的Handler运行在子线程中去的,所以不能更新UI // tv.setText(passedChangeTv); threadHander.sendMessage(threadHander.obtainMessage(1,passedChangeTv)) ; } } ; Looper.loop(); } //这里是新添加的方法 public void exit(){ if(mLooper!=null){ mLooper.quit(); mLooper = null ; } } public Handler getThreadHandler(){ return mHandler!=null?mHandler:null ; } }复制代码
我们只是在 MyThread 类中添加了一个 exit 方法,其它的都没有变.
- 2、为了方便测试,我们在 onDestory() 方法中调用
@Override protected void onDestroy() { super.onDestroy(); Log.e("onDestroy",t.getName()+"销毁") ; t.exit(); t=null ; }复制代码
我们重新跑一下应用程序,来观察结果
从效果图中我们可以看出,当我们手动调用 Looper.prepare() 和 Looper.loop() 的时候创建一个线程,但是们退出应用的时候会退出 Looper,线程会销毁,我们需要你的时候让你存在,不需要的时候让你销毁,这就大大提高了性能。我们看始终激活的线程数量都是5,这是我们所期望的
总结:在子线程中,如果手动为其创建了 Looper,那么在所有的事情完成后应该调用 quit 方法来终止消息循环
Handler 和它兄弟们
Handler 不能单独工作,必须和它的兄弟们一起协同才可以工作。而它的兄弟们就是以下三个
1、Looper
Looper 字面意思是一个轮询器,是用来检测 MessageQueue 中是否有消息,如果有消息则把消息分发出去,没有消息就阻塞在那里直到有消息过来为止。 一个线程对应一个 Looper,一个 Looper 对应一个 MessageQueue.
注意:默认情况下,线程是没有 Looper 的,所以要调用 Looper.prepare() 来给线程创建消息队列,然后再通过,Looper.loop() 来不停(死循环)的处理消息消息队列的消息
2、MessageQueue
是用来存放消息的,通过 Looper.prepare() 来初始化消息队列。MessageQueue 从字面意思来看是一个消息队列,但是内部实现并不是基于队列的。是基于一个单链表的数据结构来维护消息列表的,链表插入和删除优势很明显。
3、Message
传递信息的载体,可以携带一些信息,类似于 Intent,Bundle 一样。两个线程通过 Message 联系在一起。
Handler 源码分析
重点来了,下面我们一起来分析一下Handler的源码
1、sendMessage() 发生了什么
当我们调用 Handler 的 sendMessage 方法的时候到底发生了什么,我们从源码去看一下
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }复制代码
我们可以看到调用sendMessage方法调用sendMessageDelayed(msg, 0)
public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }复制代码
而 sendMessageDelayed 方法又调用 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) 方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { //取得MessageQueue MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } //把消息存放到MessageQueue中 return enqueueMessage(queue, msg, uptimeMillis); }复制代码
在这里不是不有些小激动呢,我们看到 sendMessage 最终调用的是 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); }复制代码
我勒个插,还在调用,这里代码我们基本上也能看懂,就是不知道msg.target 是个毛线,不过他是 Message 的一个属性,我们进去看一下发现 msg.target 是一个Handler,好我们再看queue.enqueueMessage(msg, uptimeMillis) 方法
boolean enqueueMessage(Message msg, long when) { //msg.target 这么熟悉,我去不就是 Handler 吗,要发送一个消息肯定要有 Handler 呀,不然发个毛毛呢 if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } //消息是否正在使用 if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { //消息插入链表头,条件是如果MessageQueue为空,或是消息发送的时刻为0或者消息发送的时刻小于链表头的派发时刻,就把消息插入链表头 msg.next = p; mMessages = msg; needWake = mBlocked; } else { //否则就找个合适的位置把消息插到链表中 Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }复制代码
总之这个方法就是把消息插入到消息队列中,我们现在可以归纳了,当调用handler.sendMessage方法的时候做了一件重要的事: 把消息插入到MessageQueue中
2、Looper 该上场了
根据前面所述,我们知道 Looper 的作用是创建消息队列,和处理消息。
- 1、Looper.prepare() 方法就是用来创建消息队列的,我们看源码
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }复制代码
我们看到 prepare 方法调用 prepare(true) 方法,此方法中我们目前也没有看不懂的语句,无非就是 sThreadLocal 是什么东东,不要紧,我们暂且当它是一个 List 一样用来存取 Looper 的,接下来我们看 new Looper(quitAllowed) 方法
private Looper(boolean quitAllowed) { //初始化消息队列 mQueue = new MessageQueue(quitAllowed); //取得当线线程 mThread = Thread.currentThread(); }复制代码
看到了吧,确实在上面方法中初始化了 MessageQueue。所以我们这里总结 Looper.prepare() 方法就是创建了一个消息队列
根据上面的分析,我们有了消息队列,也知道 sendMessage 是把消息插入到消息队列中去了,那么如何管理 MessageQueue中 的消息呢,Looper.loop() 该上场了。
- 2、Looper.loop(): 我们直接看源码
public static void loop() { //取得Looper final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //拿到 MessageQueue final MessageQueue queue = me.mQueue; ...省略部分代码 for (;;) { //这里是一个死循环,取出每个消息,然后分发 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ... 省略部分代码 //msg.target(Handler)这里调用 Handler 的dispatchMessage方法 msg.target.dispatchMessage(msg); } }复制代码
综上代码,我们可以得出结论 Lopper.loop() 方法主要作用是取出 MessageQueue 中消息然后调用 Handler 的 dispatchMessage 方法把消息分发出去(前提是 Message 中要有消息)
接下来我们看 Handler 的 dispatchMessage 方法
public void dispatchMessage(Message msg) { if (msg.callback != null) { //如果msg.callback不为空,则调用handleCallback handleCallback(msg); } else { 否则,mCallback不为空 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //调用handleMessage handleMessage(msg); } }复制代码
似乎看到熟悉的方法了,对就是 handleMessage(msg),就是 Handler 的回调方法,就是处理消息的方法。
这样我们大体了解到,Handler的处理流程了,大致如下:
Looper.parpare() ; //先准备好消息队列Handler handler = new Handler(){ @Override handleMessage(Message msg){ //如理发送过来的消息 ... ... }}Looper.loop() ; //是一个死循环,一直监听着 MessageQueue ,从消息队列中取消息,然后调用 handler. dispatchMessage 方法,最后调用的就是 handler 的 handleMessage(Message msg) 方法来处理消息复制代码
那么如何把消息插入到消息队列呢,就靠 Handler 的 sendMessage() 方法了,所以当调用了 Handler 的 sendMessage(),Looper.loop() 监听到 MessageQueue 中有消息,就调用 dispatchMessage 方法分发消息,最终调用 handler 的 handleMessage(Message msg) 方法来处理消息。
Handler的原理如下图所示
原创图:转载注明出处
Activity 默认有 Looper
Activity 中的 Looper 从何来
我们知道使用 Handler 的时候,Handler 一定要存在于有Looper.parpare() 和 Looper.loop() 的线程中可是我们都知道 Activity 中使用 Handler 的时候不用写 Looper.parpare() 和 Looper.loop(),但是为什么不用写有没有想过,那么我们来分析一下
Activity 所在线程也叫 UI 线程,为什么叫 UI 线程呢,其实它是通过 ActivityThread 来管理的,我们知道 Android 应用层是用 Java 开发的,那么 Java 肯定是有一个入口方法即 main 方法,main 方法到底在那里呢? 答案就是在 ActivityThread.java 中,我们来看一下源码
public final class ActivityThread { ... 省略一大坨代码 final Looper mLooper = Looper.myLooper(); //H就是一个Handler final H mH = new H(); ... 省略若干代码 private class H extends Handler { 省略一大堆 String codeToString(int code) { if (DEBUG_MESSAGES) { switch (code) { case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY"; case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY"; case PAUSE_ACTIVITY_FINISHING: return "PAUSE_ACTIVITY_FINISHING"; case STOP_ACTIVITY_SHOW: return "STOP_ACTIVITY_SHOW"; ...省略大量代码 } } return Integer.toString(code); } public void handleMessage(Message msg) { switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case RELAUNCH_ACTIVITY: ... case PAUSE_ACTIVITY: ... break; ... 省略部分代码 case RESUME_ACTIVITY: ... break; case SEND_RESULT: ... break; case DESTROY_ACTIVITY: ... break; ... 省略部分代码 case CREATE_SERVICE: ... break; case BIND_SERVICE: ... break; case UNBIND_SERVICE: ... break; ... 省略又臭又长的代码 } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); } } public static void main(String[] args) { ...省略部分代码 Process.setArgV0(""); Looper.prepareMainLooper(); ...省略部分代码 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }} 复制代码
从上面代码中可以隐约的看到 Activity 的 oncreate ,onresume... Service 的 oncreate bindService 等等方法都在这里处理了,再从上面看到了 main 方法并且调用了 Looper.prepareMainLooper(); 和 Looper.loop(); 由此就知道了,为什么我们的 Activity 默认是有 Looper 的。
结论
Activity 叫 UI 线程,其实指的就是 ActivityThread,Activity 是由ActivityThread 管理启动,暂停等,并且 ActivityThread 的入口方法中开启了 Looper.prepareMainLooper() 和 Looper.loop(),所以我们的 Activity 默认就会有一个 Looper,Service 同理
Handler引起的内存泄露
引子
我们先来看一个例子
public class MainActivity extends AppCompatActivity { private TextView tv ; //在UI线程中 private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); ... ... } } ; }复制代码
乍一看,好像没有什么问题,但是我告诉你,这段代码可能会存在内存泄露的,这是为什么呢?答案是非静态内存部会持有外部类的引用(Java的特性)
,在这里Handler是一个非静态的内存部类,会隐式的持有 MainActivity 的引用的。
解决办法
解决这个问题的办法就是避免使用非静态内部类。
static class MyHandler extends Handler { @Override public void handleMessage(Message msg) { } }复制代码
如果要操作Activity的一些对象,这里使用弱引用
static class MyHandler extends Handler { // WeakReference to the outer class's instance. private WeakReferencemOuter; public MyHandler(MyActivity activity) { mOuter = new WeakReference (activity); } @Override public void handleMessage(Message msg) { MyActivity outer = mOuter.get(); if (outer != null) { // Do something with outer as your wish. } } }复制代码
完整的代码
public class MyActivity extends AppCompatActivity { private MyHandler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHandler = new MyHandler(this); } @Override protected void onDestroy() { // Remove all Runnable and Message. mHandler.removeCallbacksAndMessages(null); super.onDestroy(); } static class MyHandler extends Handler { // WeakReference to the outer class's instance. private WeakReferencemOuter; public MyHandler(MyActivity activity) { mOuter = new WeakReference (activity); } @Override public void handleMessage(Message msg) { MyActivity outer = mOuter.get(); if (outer != null) { // Do something with outer as your wish. } } }}复制代码
当然我们可以把Handler单独写到一个类中,配置 RxBus 或 EventBus来更新UI或执行某些操作,这里就介绍完了 Handler 的相关知识点了
想成为一个牛 B 的人都会点喜欢或分享的