1、能讲讲Android的handler机制吗?
消息处理机制本质:<strong>一个线程开启循环模式持续监听并依次处理其它线程发送给它的消息.</strong>
简单的说:Android应用程序是通过消息来驱动的,系统为每个应用程序维护一个消息队列(MessageQueue),应用程序的主线程不断地(Looper)从这个消息队列中获取消息(Message),然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序的执行.
2、Android消息处理机制的工作原理
3、Handler、Looper、MessageQueue、Message之间的关系.
Handler:
消息的发送和处理者,一般使用sendMessage()和handleMessage()方法来发送和处理消息。
Message:
线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。
MessageQueue:
用于存放所有的Handler发送的消息的队列(单链表),这些消息会一直存在于消息队列中等待被处理,消息的处理遵循先进先出原则。
每个线程中只有一个MessageQueue对象。
Looper:
循环监听MessageQueue中是否存在消息,如果存在就会将消息取出并传递给Handler处理。
每个线程中只有一个Looper对象。
4、ThreadLocal是什么?Android如何保证一个线程中最多只有一个Looper,一个MessageQueue?
ThreadLocal可以在不同的线程之中互不干扰的存储并提供数据。
工作原理:每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正要存储的Object。每次获取或者设置value都是对该ThreadLocalMap映射表进行的操作,是与其他线程分开的。
在Handler的实现机制中,默认通过Looper类的prepare()方法来创建Looper对象并将其存储在ThreadLocal中(实际存储在了当前Thread所维护的ThreadLocalMap中,key为sThreadLocal,值为新建的Looper对象),这就保证了当前线程中有且仅有一个Looper对象。而Looper对象的创建同时也伴随着MessageQueeu对象的创建,自然而然也确定了其唯一性。具体代码如下:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
//如果线程的TLS已有数据,则会抛出异常,一个线程只能有一个Looper,prepare不能重复调用。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//往线程的TLS插入数据,简单理解相当于map.put(sThreadLocal,new Looper(quitAllowed));
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
5、怎样实现一个带有消息循环(Looper)的线程?
Android系统的UI线程就是一种带有消息循环(Looper)机制的线程,这种线程可以绑定Handler对象,并通过Handler的sendMessage()函数向线程发送消息。
private Handler mHandler;
private Looper mLooper;
private void createLooperThread(){
new Thread(new Runnable() {
@Override
public void run() {
// Log.e("MainActivity",Thread.currentThread().getName());
Looper.prepare(); //创建Looper和MessageQueue对象
mLooper = Looper.myLooper(); // 获取当前线程下的Looper对象
createHandler();
Looper.loop(); // 开启Looper循环 由于loop()里面是个死循环,有消息就处理,没消息就挂起休眠,因此此行代码之后的代码是无法运行的。只有调用mLooper.quit()方法后,loop才会中止,其后的代码才能得以运行。
}
}).start();
}
// 创建属于 mLooper对象所在线程 的Handler对象
private void createHandler(){
mHandler = new Handler(mLooper){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// Log.e("MainActivity",Thread.currentThread().getName());
};
}
}
6、谈谈对HandlerThread的理解
HandlerThread本质上就是一个普通Thread,只不过在内部建立了Looper循环,它的实现很简单,就是在run方法中通过Loop.prepare()来创建消息队列,并通过Loop.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler。
HandlerThread比较适用于单线程+异步队列的场景,比如一个长时间运行且没有 UI 交互的任务,就像在将用户数据上传到服务器前进行的数据压缩的操作就适合用HandlerThread。
另:IntentService内部就是通过HandlerThread来实现的。
HandlerThread的使用方法如下:
private HandlerThread mHandlerThread;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建一个线程
mHandlerThread = new HandlerThread("handlerthread");
// 开启一个线程
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 这个方法运行在mHandlerThread线程中,可执行耗时操作
Log.d("handler","消息:" + msg.what + " 线程:" + Thread.currentThread().getName());
}
};
//在主线程给handler发送消息
mHandler.sendEmptyMessage(1);
new Thread(new Runnable() {
@Override
public void run() {
//在子线程给handler发送数据
mHandler.sendEmptyMessage( 2 ) ;
}
}).start() ;
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
}
7、发送消息有哪些方法?
Handler类 - sendxxx()方法:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
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);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler类 - postxxx()方法:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postAtFrontOfQueue(Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
Activity类 - runOnUiThread()方法:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
View类 - postxxx()方法:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().postDelayed(action, delayMillis);
return true;
}
8、插入消息的流程?
分析上述发送消息的方法,不难发现消息的发送最终都会到enqueueMessage()方法:
// 向消息队列中插入消息
boolean enqueueMessage(Message msg, long when) {
// 判断消息接收者handler是否为null
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) { // 判断msg是否正在被使用
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) { // MessageQueue处于quit状态
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为使用状态
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) { // 若:消息队列为空 or 欲在消息头插入消息 or 欲插入的消息先于消息头的消息执行,则:插入消息
// 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) { // 若已轮询到消息队列尾 or 欲插入的消息先于轮询到的消息执行 则:跳出循环 -> 插入消息
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;
}
9、取出消息->处理消息的流程?
// 消息队列的循环遍历
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
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.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg); // 关键语句 调用handler的dispatchMessage()方法
} 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();
}
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) { // 发送消息 如调用了post(Runnable r)等系列方法(会执行 msg.callback = runnable; 操作),则此处不为null
handleCallback(msg);
} else {
if (mCallback != null) { // 创建Handler对象时作为参数传递进来,可以用来实现消息的拦截
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
10、如何实现Handler消息拦截(不接收消息)
private Handler handler = new Handler(new Callback(){
@Override
public boolean handleMessage(Message msg) {
return true; // 设置true拦截消息
}
}){
@Override
public void handleMessage(Message msg) {
// 根据消息类型对消息进行处理
}
};
相信聪明的读者熟悉问题9中的代码后自然会一目了然此间原理
11、消息机制中的消息池实现原理,消息池为什么不会引起OOM?
通常,我们使用Message.obtain()
从消息池中获取Message
,避免直接构造Message
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) { // 消息池链表头部Message不为空 出池
Message m = sPool; // 取出链表头部Message
sPool = m.next; // 链表的下一个Message为新的链表头
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--; // 链表池大小 -1
return m;
}
}
return new Message(); // 消息池中没有Message则重新构造
}
那么消息池中的消息哪里来的呢?我们知道,消息池的主要作用是消息的复用,那就只有当一个消息被new出来并使用结束后,才会进入消息池,也就是这个消息被回收到池中,等待复用,我们找找消息使用结束被回收的函数recycle()
:
public void recycle() {
if (isInUse()) { // 消息处于正在处于使用状态
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// 把这个Message所有成员赋值成最初的状态
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) { // 入池
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
看到这,相信你已经知道了消息是怎样加到消息池和怎样从消息池中取出:消息在使用结束recycle
的时候入池,在下次obtain
消息的时候从消息池中取出。
那么Android会因为Message Pool缓存的Message对象而造成OOM吗?
对于这个问题,我可以明确的说APP不会因Message Pool而OOM
我们知道消息池中保存的最大消息数为MAX_POOL_SIZE
表示链表的最大长度为50,当我们执行入池操作时:
- 将待回收的Message对象字段置空(避免因Message过大,使静态的消息池内存泄漏)。因此无论原先的Message对象有多大,最终被缓存进Message Pool前都被置空,那么这些缓存的Message对象所占内存大小对于一个app内存来说基本可以忽略。所以说,Message Pool并不会造成OOM。
- 以内置锁的方式(线程安全),判断当前线程池的大小是否小于50。若小于50,直接将Mesaage插入到消息池链表尾部;若大于等于50,则直接丢弃掉,那么这些被丢弃的Message将交由GC处理。
12、主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
造成ANR的原因一般有两种:
- 当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了);
- 当前的事件正在处理,但没有及时完成。
因为Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。
而且主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
<b>也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。</b>
13、Handler内存泄漏的问题
造成内存泄漏的原因:
- Handler的生命周期与Activity不一致;
- Handler引用Activity阻止了GC对Activity的回收。
解决方案:
使用显式引用:1.静态内部类。 2. 外部类
使用弱引用:WeakReference
具体代码:
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> mActivity;
public MyHandler(MainActivity activity) {
mActivity = new WeakReference< MainActivity >(activity);
}
@Override
public void handleMessage(Message msg) {
System.out.println(msg);
if (mActivity.get() == null) {
return;
}
mActivity.get().todo();
}
}
@Override
public void onDestroy() {
// If null, all callbacks and messages will be removed.
mHandler.removeCallbacksAndMessages(null);
}
未完待续...
参考文集:
Android 消息处理机制(Looper、Handler、MessageQueue,Message)
Android的消息机制之ThreadLocal的工作原理
Android面试:主线程中的Looper.loop()一直无限循环为什么不会造成ANR?