出现背景:
为了方便线程之间的通讯。比如:主线程处理耗时操作就会引起卡顿ANR(Activity Not Respondinig),这是我们就要在子线程中去做耗时操作,通过Handler去通知主线程更新。
用法简介
接受消息方式
1.在主线程中创建一个匿名内部类Handler,并且重写handleMessage方法。
2直接去实现Handler里的CallBack接口,Handler通过回调CallBack接口里的handleMessage方法从而实现对UI的操作.
发消息的方式。
sendEmptyMessage(int what):发送空消息
sendEmptyMessageDelayed(int what,long delayMillis):指定延时多少毫秒后发送空信息
sendMessage(Message msg):立即发送信息
sendMessageDelayed(Message msg):指定延时多少毫秒后发送信息
Handler工作过程
1在一个Android应用启动的时候,会创建一个主线程,就是ActivityThread。在ActivityThread中有一个静态的main方法为程序入口。再次方法中首先调用Looper.prepareMainLooper()函数。
public static void main(String[] args) {
......
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
1从Looper构造方法中,创建了一个消息队列MessageQueue。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
1.1并且保存当前线程的对象Thread.currentThread()。但是构造方法私有,只有prepare方法中可以看出,ThreadLocal 中取出,如下:
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));
}
在这里新建了一个Looper对象,然后保存在ThreadLocal中,当我们下次需要用到Looper的之后直接从这个sThreadLocal中取出即可。ThreadLocal类它实现了本地变量存储,我们将当前线程的数据存放在ThreadLocal中,若是有多个变量共用一个ThreadLocal对象,这时候在当前线程只能获取该线程所存储的变量,而无法获取其他线程的数据。在Looper这个类中为我们提供了myLooper来获取当前线程的Looper对象。从上面的方法还能够看出,一个线程只能创建一次Looper对象。
2通过prepareMainLooper方法内部,先调用prepare(false)声明主线程不能终止消息循环,子线程可以。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
接下来看一下main方法Looper.loop()做了事情。
public static void loop() {
// xxxxx==============>1获取当前线程中的Looper,并从Looper中获得消息队列
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;
// Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is.
//xxxxx==============> 确保这个线程的身份是本地进程的,并且与真实的token保持联系(这里只翻译下注释)
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// xxxxx==============>所谓的死循环
for (;;) {
// xxxxx==============>从消息队列中取出消息,并且只有当取出的消息为空的时候才会跳出循环。
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 Printer logging = me.mLogging;
// xxxxx==============> 必须在UI线程记录系统日志,我们不用管直接跳过
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// xxxxx==============> 将消息重新交由Handler处理。
msg.target.dispatchMessage(msg);
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.
// xxxxx==============> 确保调用过程中线程没有被销毁。 (这里只翻译下注释)
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);
}
// xxxxx==============> 对消息进行回收处理。
msg.recycleUnchecked();
}
}
其实这里我看的不太明白,黑体字是我说一下自己此时的理解。
msg.target是发送消息的Handler,通过Handler中的dispatchMessage方法又将消息交由Handler处理。消息处理完成之后便对消息进行回收处理。
MessageQueue
MessageQueue翻译为消息队里,在这个消息队列中是采用单链表的方式实现的,提高插入删除的效率。对于MessageQueue在这里我们也只看一下它的入队和出队操作。 这个我暂时理解的不是很好,暂时不做解释,以后补充。
消息入队:
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
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.
// xxxxx==============> 新的消息,如果阻塞的话,唤醒事件队列。
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//1 Inserted within the middle of the queue .
// 2 Usually we don't have to wake up the event queue unless there is a barrier at the head of the queue
// 3and the message is the earliest asynchronous message in the queue.
// xxxxx==============>
// 1若是在中间插入,3则根据Message创建的时间进行插入 ,2并且这时消息队列如果处于等待状态的话则将其唤醒
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;
}
消息出队(一脸懵逼,还望赐教)这里我就不BB了,希望大神赐教:
Message next() {
...... int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue. do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message. mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages. nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled. if (mQuitting) {
dispose();
return null;
}
...... }
..... }
}
退出队列的方法
void quit(boolean safe) {
// xxxxx==============> 主线程的消息队列是不允许被退出的 mark。
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
// xxxxx==============> mQuitting设为true可以退出消息队列 mark。
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr);
}
}
Handler
窝草!终于翻篇了。
Handler构造方法
public Handler(Callback callback, boolean async) {
......
// xxxxx==============> 一个线程想创建Handler必须Looper.prepare() ,否则直接异常。
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
sendMessage的最终调用方法:
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方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
从上边看出,Handler 通过sendMessage向MessageQueue中插入一条消息,这个MessageQueue是有Looper创建的(Looper的构造方法中),三者的主要连接桥梁就是Looper ,而且Looper和线程又是对应的,所以这四个的关系就是一一对应的。Looper 的最作用就是轮训消息队列,Hander就是发消息和处理消息,MessageQueue只负责插入、存储消息。
关于Handler有两点需要补充一下。文章开始提到的,Handler是个匿名内部类,会持有Activity的引用,假设做了一下耗时操作,Activity返回时候没有及时处理就很容易内存泄漏。避免方法有2点:
A手动关闭这些应该回收的资源
1.Acitivty 结束方法中,销毁后台线程,切断了链接,就直接Gc回收了,整个线程回收了,不用担心泄漏了。
2假如有延迟发送的消息,就调用Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。
B 将Handler声明为静态类,是的条件不成立
static classMyHandlerextendsHandler{ WeakReference mActivityReference;
static class MyHandler extends Handler {
MyHandler(Activity activity) {
mActivityReference= new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
这样的话,Activity随意销毁就行了,一切交个Gc。但是这样只保证了Activity中的handler不会西楼,handler对象还是在Message中排队,故需要在onstop或者ondestory手动释掉所有消息:mHandler.removeCallbacksAndMessages(null);
收工~~~~~
网友评论