Handler
在Android
中的地位没必要说了,学习安卓开发必然跳不过Handler
,讲解Handler
的文章也是很是的多,这里我对我所了解的Handler
这种Android
中多线程间的通讯方式的相关知识作一个总结。面试
Handler
做为线程间通讯的方式,最常使用的地方就是子线程更新UI
。由于Android
的UI
控件不是线程安全的,若是在多线程下并发访问可能会致使UI
控件处于不可预期的状态。因此在子线程想要更新UI
的时候会使用handler.sendMessage(Message msg)
方法通知主线程更新。
关于Handler
的经常使用方法,除了sendMessage
系列方法,handler
还有一个post
系列方法,能够在子线程中经过handler.post(Runnable r)
方法进行一些在主线程的操做。
sendMessage系列方法:安全
public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public final boolean sendMessageAtFrontOfQueue(Message msg)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
复制代码
post系列方法:bash
public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean postDelayed(Runnable r, Object token, long delayMillis)
public final boolean postAtFrontOfQueue(Runnable r)
复制代码
简单运用:网络
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_SEND:
mTitleText.setText((String) msg.obj);
break;
}
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
new Thread(new Runnable() {
@Override
public void run() {
try {
//子线程耗时操做
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
@Override
public void run() {
mTitleText.setText("post");
}
});
}
}).start();
break;
case R.id.button2:
new Thread(new Runnable() {
@Override
public void run() {
try {
//子线程耗时操做
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//发送消息
Message message = Message.obtain();
message.what = MESSAGE_SEND;
message.obj = "sendMessage";
handler.sendMessage(message);
}
}).start();
break;
}
}
复制代码
Handler
的基本工做流程。过程当中主要涉及了如下四个类:
接下来首先就从Handler
看起。多线程
使用Handler
第一步就是建立一个Handler
对象,从而首先调用的就是Handler
的构造方法。固然Handler
构造方法有不少的不一样参数的重载,这里只看最主要的两个。并发
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
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());
}
}
//获取当前线程的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//给Handler中的成员变量初始化
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
复制代码
第一个是传参带有Looper
的所调用的方法,其中就是作了些初始化的操做,调用这个方法建立的Handler
的Looper
就是做为参数传入的Looper
。
第二个构造方法中第一个if
判断当前Handler
类是否有内存泄漏的隐患,主要是检验当前类是不是匿名类、成员类、局部类是否静态修饰等。接着经过Looper.myLooper()
方法获取当前线程中的Looper
对象。接下来判断若是这个Looper
为空,说明当前Handler
初始化所在线程没有Looper
,会抛出Exception
。这里就决定了Handler
初始化所在线程必须有Looper
,因此在子线程中建立Handler
以前先要经过Looper.prepare()
方法建立Looper
。接着就是对Handler
中一些成员变量进行初始化,将Looper
中的消息队列引用传递给mQueue
,将构造中传入的callback
初始化等。
来看下Looper
的myLooper()
方法:app
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
复制代码
这里是从ThreadLocal
中获取到一个Looper
。关于ThreadLocal
的相关知识点能够看Android进阶知识:ThreadLocal。less
Handler
建立以后,在须要进行主线程操做的时候,咱们会使用handler
的sendMessage
系列方法,或者post
系列方法。这里一样有不少重载,具体的方法在前文中已经列举。这里先看post
方法:异步
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
复制代码
能够看到无论哪一个post
方法中,都是经过getPostMessage()
方法构建一个Message
最终仍是调用对应的sendMessage
方法。async
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
复制代码
getPostMessage
方法中经过Message.obtain()
获取一个Message
将传入的Runnable
赋给Message
中的callback
,接着返回这个Message
。
由于post
方法的最后又都调用了对应的sendMessage
方法,因此接下来看sendMessage
方法的实现:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
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);
}
复制代码
这里看到通过层层调用最终执行了enqueueMessage
方法。这里要注意的是sendMessageDelayed
方法中设置的延迟时间是经过SystemClock.uptimeMillis()
+ 延迟时间来计算的。
SystemClock.uptimeMillis()
方法是获取从开机到如今的毫秒数,与System.currentTimeMillis()
获取从1970年1月1日到如今的毫秒数不一样,后者会受到手机系统时间影响,而系统时间能够手动修改。sendMessageAtTime
方法中对MessageQueue
进行是否为null
的判断,为null
抛出异常,这里的MessageQueue
就是在Handler
构造函数中Looper
中的Queue
。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
enqueueMessage
方法中经过msg.target=this
这一句,将Handler
的引用传递给了Message
中的target
。接着调用了MessageQueue
的enqueueMessage
方法。接下来进入MessageQueue
。
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) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); 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; } 复制代码
MessageQueue
的enqueueMessage
方法中,传入的msg
就是要插入到消息队列的新消息,when
是这个任务的延时时间。方法里首先对消息里的target
也就是Handler
进行空判断,为空抛出异常。接着判断了这个消息是否被使用和消息队列是否退出等。看到msg.when = when
;这一行将延迟时间传递保存到消息内部,下一行定义了一个临时指针p
用来指向mMessage
,这个mMessage
就是消息队列的首结点,接下来的这个if-else
作的就是将新消息根据他的when
大小,将他按顺序加入到队列中合适位置上。这里能够看出这个消息队列其实是个链表,每一个Message
是一个结点,结点中有一个next
指针存放下一个结点位置。这里的新消息的添加,就是向这个链表中插入一个节点。
先看if
中判断,p==null
即首结点为null
,when=0
及延时时间为0,当即执行,when<p.when
即新节点的延时时间小于当前链表首结点的延时时间,这三种状况下直接将新消息节点插到链表头部,即msg.next=p;mMessages=msg
这两行的操做,而后唤醒消息线程处理新消息。else
就要将新节点根据when
的大小插入到链表中合适位置,这里又定义了一个临时指针prev
,指向p
指向的前一个节点,看到for (;;)
循环中,将p
指针不断向后移,直到p
等于null
即链表结尾或者新结点的when<p.when
的时候,即这个链表是按照节点when
的从小到大的顺序排列插入的。此时break
出循环,将新节点插入到此处,即msg.next=p;prev.next=msg
。到此消息发送加入消息队列的过程节结束了。
有往消息队列里加消息,就有从消息队列取消息。谁来取呢?就是Looper
,以前看到在Handler
的构造方法里,经过Looper.myLooper()
方法获取到当前线程(handler
建立所在线程)的Looper
对象。并且还知道了建立Handler
的线程必须存在一个Looper
对象不然会抛出异常。这也是咱们不能在子线程里直接建立使用Handler
的缘由。那么为何主线程能够直接建立Handler
呢?是由于主线程中有Looper
。那么主线程的Looper
又是哪来的呢?这须要看到ActivityThread
类里的代码。
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
......
}
复制代码
ActivityThread
中main
方法在app
启动时调用,这里省去了一些无关代码,只看与主线程Looper
相关的。能够看到在main
方法里调用了Looper.prepareMainLooper()
方法,以后获取了一个主线程的Handler
,接着调用了Looper.loop()
方法。一个方法一个方法来看,先是prepareMainLooper()
方法:
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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
复制代码
prepareMainLooper()
方法中调了prepare(false)
方法,这里quitAllowed
传的是false
,这个标记会传递到MessageQueue
中,这里说明主线程的消息队列是不容许退出的。prepare()
方法里初始化了new
了一个Looper
对象,并将它添加到当前线程的ThreadLocal
中。接着到Looper
的构造函数中看看:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
复制代码
Looper
的构造函数很简单,建立了MessageQueue
消息队列保存了一个当前线程的对象。从这里看出来建立一个Handler
须要传入建立线程的Looper
,而建立Looper
又对应建立了一个MessageQueue
。下面回到main
方法中看thread.getHandler()
这个获取主线程Handler
方法:
final Handler getHandler() {
return mH;
}
复制代码
这里直接返回mH
这个Handler
,那么这个Handler
是在哪里建立的呢?
final H mH = new H();
复制代码
跟踪下去发现这个mH
是ActivityThread
类的成员变量,而且直接初始化。因此这个Handler
就是在main
方法中建立ActivityThread
对象时就初始化了。最后调用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(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
}
复制代码
这里一样省略了部分代码,来看主要的流程,首先仍是获取当前线程Looper
对空作了校验。而后从Looper
中获取到MessageQueue
,接着进入for
循环,调用queue.next()
从消息队列中取出消息,根据注释,这个方法有可能会阻塞,若是返回的msg为null
,说明消息队列正在退出。接着在try
代码块中调用了msg.target.dispatchMessage(msg)
方法,这个msg.target
就是在前面enqueueMessage
方法中设置的发送消息的Handler
,因此这里调用了Handler的dispatchMessage(msg)
方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
dispatchMessage
方法中对callback
进行了判断,这里有两个callback,msg.callback
是对应Handler
中的post
方法,将传入的Runnable
存入Message
的callback
中,若是调用post
方法msg.callback
不为空调用handleCallback
方法,最终会执行Runnable
的run
方法,开始执行post
时传进来的任务。
private static void handleCallback(Message message) {
message.callback.run();
}
复制代码
第二个mCallback
,对应的是建立Handler
时的传参,若是不为空会执行mCallback.handleMessage
方法。若是初始化时没传mCallback
,就会执行handleMessage(msg)
方法:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
复制代码
这个方法就是咱们本身须要实现的处理消息的方法,也是咱们最经常使用重写的方法。至此UI
主线程中建立Handler
,Looper
,而且Looper
开启轮询到调用了Handler
的dispatchMessage
处理消息的过程就结束了。
回到上面说的,这是主线程中Handler
、Looper
的初始化,那么要在子线程使用Handler
该怎么作呢?
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
//处理消息
}
};
Looper.loop();
}
}).start();
复制代码
其实和主线程同样,由于子线程中没有Looper
因此须要咱们本身建立Looper
而且调用Looper.loop()
方法开始轮询。这里的Looper.prepare()
方法和prepareMainLooper()
方法同样最终会调用prepare(boolean quitAllowed)
方法,这时传入的quitAllowed为true
,表示消息队列能够退出。
至此Handler
机制相关类Handler
、Looper
、MessageQueue
的主要方法源码都看完了,他们之间的工做流程相互关系也都清楚了。
其实还剩一个Message
消息类,Message
类中主要看一个obtain()
方法,Message
除了能够经过new
来建立,还能够经过obtain()
方法来得到,而且obtain()
方法是从全局池中获取Message
对象,能避免从新分配对象。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
复制代码
这里看到只要sPool
不等于null
,就从sPool
头上去一个消息节点返回。因此使用obtain
方法,相对直接new
一个Message
能减小内存分配。
A1:主要涉及到Handler
、Looper
、MessageQueue
、Message
这四个类。
Handler:发送消息到消息队列。
Looper:从消息队列中取出消息,交给Handler的dispatchMessage方法处理。
MessageQueue:消息队列存储管理消息插入取出。
Message:消息类,携带着消息数据。
A2:经过以前的源码阅读知道,是有顺序的,是根据Message.when
这个相对时间排列的。
A3:一样从源码中能够知道,子线程中不能直接建立Handler
,Handler
建立须要指定一个Looper
,子线程中没有Looper
,须要先建立Looper
,调用Looper.loop
方法。
A4:不是,内部实现实际上是一个单链表。
A5:quit
方法会清空消息队列中的全部消息,quitSafely
方法只会清除全部延迟消息,非延迟消息仍是分发出去交给Handler
处理。具体仍是看源码:
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
复制代码
这里其实是调用了MessageQueue
的quit
方法:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
复制代码
quit
方法传入的是false
调用的是removeAllMessagesLocked()
方法,quitSafely
传入的是true
调用的是removeAllFutureMessagesLocked
方法。
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
复制代码
removeAllMessagesLocked
方法中直接将全部消息清空。
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
复制代码
removeAllFutureMessagesLocked
方法中先作判断若是首节点when
大于当前时间说明全是延迟消息,就一样调用removeAllMessagesLocked
处理所有清空,不然循环找到队列中when
大于now
也就是大于当前时间的节点位置,将该节点消息同其后的全部消息清空。
A6:这个涉及到Linux
的Epoll
机制。简单来讲就是Android
应用程序的主线程在进入消息循环过程前,会在内部建立一个Linux
管道(Pipe
),这个管道的做用是使得Android
应用程序主线程在消息队列为空时能够进入空闲等待状态,而且使得当应用程序的消息队列有消息须要处理时唤醒应用程序的主线程。
具体解释:Android中为何主线程不会由于Looper.loop()里的死循环卡死?
Handler
致使的内存泄露,是平时写代码不注意很是容易出现的问题,并且内存泄露多了对应用性能影响较大,因此单独研究下。
通常咱们使用Handler
更新UI
都是这样的:
public class HandlerActivity extends Activity {
private TextView mTextView;
private final int MESSAGE_SEND = 0x01;
private MyHandler handler = new MyHandler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
mTextView = findViewById(R.id.textView);
Message obtain = Message.obtain();
obtain.what = MESSAGE_SEND;
obtain.obj = "文字";
handler.sendMessageDelayed(obtain, 1000 * 60 * 10);
}
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_SEND) {
mTextView.setText((String) msg.obj);
}
}
}
}
复制代码
这里看到进入Activity
就发送了一个延迟消息,现实开发中多是网络有延迟又或者进入一个界面后马上离开这时数据还没加载好,只要是耗时任务尚未完成,当前的Activity
又须要销毁,这时候由于此时MyHander
,它隐式持有外部类的引用,当Activity
销毁时,此时异步耗时任务尚未结束,仍然持有Activity
的引用,使得Activity
没法回收,形成内存泄漏。 经过集成LeakCanary
能够检测到内存泄漏,以下图:
AndroidStudio Profiler
能够查看到内存泄露:
HandlerActivity
,而后观察内存状况,能够发现即便在我手动
GC
屡次后,仍然存在多个实例没有被回收的现象。
内存泄漏解决方法:
Handler
定义为静态,静态内部类不会持有外部类的引用。Handler
中没法访问外部类的成员,须要用一个外部类的弱引用来访问外部成员,又由于是弱引用,在GC
时能够将其回收,不会形成内存泄露。Activity
的onDestory
方法中调用removeCallbacksAndMessages
方法清除消息队列。解决内存泄漏:
public class WeakHandlerActivity extends Activity {
private TextView mTextView;
private static final int MESSAGE_SEND = 1;
private MyHandler handler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
mTextView = findViewById(R.id.textView);
Message obtain = Message.obtain();
obtain.what = MESSAGE_SEND;
obtain.obj = "文字";
handler.sendMessageDelayed(obtain, 1000 * 60 * 10);
}
//静态内部类
static class MyHandler extends Handler {
private final WeakReference<WeakHandlerActivity> mActivty;
public MyHandler(WeakHandlerActivity activity) {
//初始化Activity的弱引用
mActivty = new WeakReference<WeakHandlerActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
WeakHandlerActivity activity = mActivty.get();
if (activity != null) {
if (msg.what == MESSAGE_SEND) {
activity.mTextView.setText((String) msg.obj);
}
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//清除消息队列
handler.removeCallbacksAndMessages(null);
}
}
复制代码
经过这三个方法结合使用,就能够解决Handler
致使的内存泄漏的问题。此次再经过Profiler
来查看内存状况:
WeakHandlerActivity
实例。
GC
事后内存中的
WeakHandlerActivity
已经被所有回收,不会继续占用内存,形成泄漏。
Handler是Android提供的一种线程间通讯方式。由于Android中UI控件不是线程安全的,多线程并发访问会出现同步问题,因此若是子线程想更新UI一般经过Handler来完成线程间通讯。
Handler的工做流程主要是由Handler发送消息,将消息添加到消息队列MessageQueue中,再经过轮询器Looper从消息队列中取出消息,交给Handler去分发处理消息对应任务。
Handler使用时容易发生内存泄露,记得经过静态内部类+弱引用的方式使用Handler,而且在Activity的onDestory方法里记得清除消息队列。