Handler在Android的作用主要是线程间通讯的,现在也有各种文章在讲解Handler的作用以及源码分析,但是必定这些都是别人自己的总结和整理,和自己总结还是有区别的,为了加深自己的记忆所以自己也来分析一下Handler以及它的小伙伴们。
转载请标明出处: githubchen001.github.io/2016/05/23/… 本文来自:【JunJun的博客】
什么是Handler
什么是Handler?
再华丽的解释也不过是官方的解解释吧:先看官网上的一段话
A Handler allows you to send and process Message and Runnable objects associated with
a 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的实例都会绑定到创建它的线程中。
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常用的方法
(1)、在某一时刻执行某些事情 (2)、在不同的线程中执行操作 (也就是线程间通信)
| 方法名 | 描述 |
|---|---|
| boolean post (Runnable r) | 立即执行操作 |
| postAtTime(Runnable, long) | 在指定的时间执行某些操作 |
| postDelayed(Runnable, long) | 延时执行某些操作 |
| sendEmptyMessage(int) | 发送一个含有what的信息 |
| sendMessage(Message) | 发送一个Message |
| sendMessageAtTime(Message, long) | 在指定的时间发送消息 |
| sendMessageDelayed(Message, long) | 延时发送消息 |
Handler常用的使用方式
1、解决ANR,子线程更新UI线程
在Android应用程序中我们最常见的异步莫过于ANR了(主线程执行耗时操作了,发起网络请求,或操作IO等),通常情况下在Activity是5秒钟响应就会报这个错,在BroadCast中是10秒钟,使用Handler可以很容易的解决这个问题。
解决方法:
- 1、定义Handler并重写handleMessage方法
private Handler mHandler = new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
String passedChangeTv = (String) msg.obj;
tv.setText(passedChangeTv);
break ;
}
}
} ;
new Thread(new Runnable() {
public void run() {
* 1 请求服务器 * 2 解析数据 * 3 获取数据 */
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() {
mtimeTaskHandler.postDelayed(new Runnable() {
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(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
int passedChangeTv = (int) msg.obj;
tv.setText(passedChangeTv+"");
preSecondGetServer() ;
break ;
}
}
} ;
private int i ;
private void preSecondGetServer() {
new Thread(new Runnable() {
public void run() {
i++ ;
Message message = new Message() ;
message.what = 1 ;
message.obj = i ;
preSecondHandler.sendMessageDelayed(message,1000) ;
}
}).start();
这样就可以一秒请求一次服务器,并且把结果显示在TextView上了
3、两个子线程进行通讯
Handler的作用就是是进行线程间通信的,上面说的都是UI线程(主线程)和子线程之间的通信,那么两个工作线程(子线程)可以通信吗,答案是肯定 的。
private Handler threadHandler ;
class MyThread extends Thread{
{
start();
}
public void run() {
super.run();
Looper.prepare();
threadHandler = new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
String passedChangeTv = (String) msg.obj;
tv.setText(passedChangeTv);
}
} ;
Looper.loop();
}
}
注意:子线程默认是没有Looper的所以我们在子线程中一定要声明Looper.prepare()和Looper.loop()在它们之间去初始化Handler
private void threadHandlerTask() {
new Thread(new Runnable() {
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来通信。
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 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);
}
在这里不是不有些小激动呢,我们知道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) {
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) {
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;
prev.next = msg;
}
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()该上场了。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...省略部分代码
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
... 省略部分代码
msg.target.dispatchMessage(msg);
}
}
综上代码,我们可以得出结论Lopper.loop()方法主要作用是取出MessageQueue中消息然后调用Handler的dispatchMessage方法把消息分发出去(前提是Message中要有消息)
接下来我们看Handler的dispatchMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else { 否则,mCallback不为空
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
似乎看到熟悉的方法了,对就是handleMessage(msg),就是Handler的回调方法,就是处理消息的方法。
这样我们大体了解到,Handler的处理流程了,大致如下:
Looper.parpare() ;
Handler handler = new Handler(){
handleMessage(Message msg){
...
...
}
}
Looper.loop() ;
那么如何把消息插入到消息队列呢,就靠Handler的sendMessage()方法了,所以当调用了Handler的sendMessage(),Looper.loop()监听到MessageQueue中有消息,就调用dispatchMessage方法分发消息,最终调用handler的handleMessage(Message msg)方法来处理消息。
Handler的原理如下图所示
Activty默认有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中的,我们来看一下源码
public final class ActivityThread {
... 省略一大坨代码
final Looper mLooper = Looper.myLooper();
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 ;
private Handler mHandler = new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
...
...
}
} ;
}
乍一看,好像没有什么问题,但是我告诉你,这段代码可能会存在内存泄露的,这是为什么呢?答案是非静态内存部会持有外部类的引用(Java的特性),在这里Handler是一个非静态的内存部类,会隐式的持有MainActivity的引用的。
解决办法
解决这个问题的办法就是避免使用非静态内部类。
static class MyHandler extends Handler {
public void handleMessage(Message msg) {
}
}
如果要操作Activity的一些对象,这里使用弱引用
static class MyHandler extends Handler {
private WeakReference mOuter;
public MyHandler(MyActivity activity) {
mOuter = new WeakReference(activity);
}
public void handleMessage(Message msg) {
MyActivity outer = mOuter.get();
if (outer != null) {
}
}
}
完整的代码
public class MyActivity extends AppCompatActivity {
private MyHandler mHandler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new MyHandler(this);
}
protected void onDestroy() {
mHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
static class MyHandler extends Handler {
private WeakReference mOuter;
public MyHandler(MyActivity activity) {
mOuter = new WeakReference(activity);
}
public void handleMessage(Message msg) {
MyActivity outer = mOuter.get();
if (outer != null) {
}
}
}
}
当然我们可以把Handler单独写到一个类中,配置RxBus或EventBus来更新UI或执行某些操作