目录
一.Handler
一.什么是Handler
Handler是android 机制的一个上层接口,Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。
1.可以让对应的Message和Runnable在未来的某个时间点进行相应处理
2.让想要处理的耗时操作放在子线程。让更新UI的操作放在主线程
(在很多应用场景中需要做一些耗时操作,当这些耗时操作完成后,需要在UI上进行相应的改变。然而耗时操作是在子线程中进行的,android的线程是不安全的,不能在子线程中更新UI,android引入了handler机制,通过handler发送消息机制来通知主线程更新UI)
二.Handler的使用方法
1.post(runnable)
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
//获取一个message,把Runnable封装成了一个Message对象
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
//delayMillis为延误时间,上一层传值为0
{
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);
}
在源码中,可以看到post(runnable)方法是把Runnable封装成了一个Message对象,并且将Runnable对象设置给了Message对象的callback字段,最后将Message对象插入消息队列。
再来重点看sendMessage方法
2.sendMessage(message)
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
看源码和post方法一样同样调用了sendMessageDelayed(msg,time)方法。将消息发送到MessageQueue中。
看一下使用方法:
1.首先要创建一个Handler对象,复写handleMessage方法,根据Message what的值进行不同的处理操作。
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
//在这里可以进行UI操作
text.setText("你好");
break;
default:
break;
}
}
};
2.创建Message对象,可以通过Message what来进行赋值,设置Message所携带的数据,然后把Message放到Handler中,通过handler.sendMessage方法,将Message传入Handler中,然后通过handleMessage方法进行UI线程的处理
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
}).start();
三.Handler机制的原理
Looper:是每一个线程中所独有的,用来为一个线程开启一个消息循环,它通过它的loop()方法读取它下面MessageQueue的消息,读取到消息后把消息发送给Handler来进行处理。
MessageQueue:是一个消息队列,它是一个先进先出的方式来管理的Message,在创建Looper的时候,其实它已经创建了MessageQueue,所以创建Looper的时候,Looper和MessageQueue已经关联到了一起。
Message:是消息对象,它里面有很多参数(what,arg1,arg2,obj(可以传一些复杂的对象))
Handler:它有两个作用,发送消息和处理消息,处理Looper发送过来的消息,Looper发送消息也是通过Handler来进行的,Handler发送消息不是漫无目的发送,它不能发送到其他线程,它只能发送到它相关线程的MessageQueue当中,而MessageQueue又是和Looper关联的,所以说Handler要发送消息必须有一个维护它的Looper,这时候就把Looper,MessageQueue,Handler关联到了一起
我们看一下Handler的源码
首先看一下Handler的构造方法
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 that has not called Looper.prepare()");
}
//通过Looper创造了MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从源码里可以看到在构造方法中 “mLooper = Looper.myLooper()” 创造了Looper,“mQueue = mLooper.mQueue”又通过Looper的成员变量创造了MessageQueue。在构造方法当中Handler已经和MessageQueue关联到了一起,而MessageQueue又是通过Looper来管理的。
那么Looper是如何获取的呢?可以看下myLooper这个方法
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到是通过ThreadLocal 这个JAVA中的机制通过get()方法获取的。
ThreadLocal 线程本地存储区(Thread Local Storage) 通过不同的线程访问同一个ThreadLocal,不管是它的set()方法还是get()方法,它们对ThreadLocal所做的读写操作仅限于各自线程的内部。也就是不同线程彼此不能访问对方的ThreadLocal。
这就是Handler里为什么要通过ThreadLocal来保存Looper,这样它就可以使每一个线程有单独唯一的Looper。
那么ThreadLocal 的set()方法在什么时候调用的呢?Handler机制在什么时候设置的ThreadLocal?
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到在prepare()方法中“ sThreadLocal.set(new Looper(quitAllowed))”,就是它创建了一个Looper对象,并且把这个Looper对象设置给了ThreadLocal,保证了每一个线程Looper的唯一性。Looper内部维护一个MessageQueue。
这时候整个的MessageQueue通过Looper跟线程关联上了,这样不同的线程就不能访问其他的消息队列了。
现在已经创建好了Looper,创建好了MessageQueue,我们知道创建Handler的两种方法(post(runnable),sendMessage(message)),两种方法都需要通过开启Looper.loop()方法才能从MessageQueue中获取消息。消息循环的建立就是通过Looper.loop()方法。我们来看一下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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { // 死循环
Message msg = queue.next(); // 获取消息(might block 没消息会阻塞)
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//处理消息
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//回收消息
msg.recycleUnchecked();
}
}
通过代码可以看出,loop()方法实际上就是创建了一个for的死循环,然后从消息队列中逐个获取消息(当没有消息时,queue.next()会阻塞,直到有消息才继续执行),最后处理消息的过程。
总结一下:Looper通过Looper.prepare()来创建Looper对象,保存在ThreadLocal中,然后通过Looper.loop()开启循环,来进行消息的分发
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
消息循环当中,最终调用“msg.target.dispatchMessage(msg) ”
从Message源码中可以看到target其实就是一个Handler,所以其实最后还是通过Handler将消息传给了消息队列,而消息队列又将消息分发给Handler来处理。这就是Handler的两个作用,一个是发送消息,一个是处理消息
然后看一下dispatchMessage(msg)方法的源码
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
从源码中可以看到dispatchMessage(msg)方法其实只是一个分发的方法,首先会判断msg.callback是否为空,(.callback其实是一个Runnable对象,在上面的使用方法中有写)如果不为空,就执行handleCallback方法,而handleCallback方法里面其实调用的就是线程的run()方法。如果为空,将执行handleMessage来进行处理。
总结:如图,创建一个Looper,创建Looper的同时Looper内部又创建了一个MessageQueue,而在创建Handler的时候取出当前线程的Looper,又通过Looper对象获取到MessageQueue,然后Handler在该MessageQueue中添加一条Message。Looper开启一个循环,不断的从MessageQueue中获取消息,然后从MessageQueue头部获取的Message以Message.target的形式交给Handler,通过dispatchMessage分发由handleMessage或者调用callback进行处理,处理完成之后还是会发送消息到Looper中不断的又开启循环从MessageQueue中获取消息。
四.Handler引起的内存泄漏以及解决办法
看一下刚才写的Handler用法
我们可以看到提示上说代码可能会发生内存泄漏。
那么内存泄漏是如何发生的呢:在Java中非静态内部类和匿名内部类都会隐式持有当前类的外部引用,通过图上代码可以看到我们创建的Handler不是静态内部类,所以它会隐匿的持有Handler 这个Activity的引用。如果Activity被回收了,而在Handler的内部还在做一些耗时操作从而导致Handler没有被释放,所以Handler持有的Activity的引用也不能被释放,导致Activity无法被GC回收,就会造成内存泄漏。
如何解决内存泄漏:
1.把Handler改成静态内部类
private MyHandler myHandler = new MyHandler();
private static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
2.Handler内部持有外部Actvity弱引用
private MyHandler myHandler = new MyHandler(MainActivity.this);
private static class MyHandler extends Handler {
//持有弱引用MainActivity,GC回收时会被回收掉.
WeakReference<MainActivity> weakReference;
public MyHandler(MainActivity mainActivity){
weakReference = new WeakReference<MainActivity>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (weakReference.get()!=null){
//执行业务逻辑
weakReference.get().text.setText("你好");
}else {
Log.e("","MainActivity已经被销毁");
}
}
}
3.在Activity生命周期的onDestroy()方法中调用Handler的removeCallbacksAndMessages()方法。
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}