Android 消息处理机制
Posted YaoYong_BigData
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 消息处理机制相关的知识,希望对你有一定的参考价值。
一、概述: 1、我们先说下什么是android消息处理机制? 消息处理机制本质:一个线程开启循环模式持续监听并依次处理其他线程给它发的消息。 简单的说:一个线程开启一个无限循环模式,不断遍历自己的消息列表,如果有消息就挨个拿出来做处理,如果列表没消息,自己就堵塞(相当于wait,让出cpu资源给其他线程),其他线程如果想让该线程做什么事,就往该线程的消息队列插入消息,该线程会不断从队列里拿出消息做处理。 2、Android消息处理机制的工作原理? 打个比方:公司类比App PM 的主要工作是设计产品,写需求文档,改需求,中途改需求,提测前改需求... UI 主要工作是UI设计,交互等。 RD 工作我就不说了 CEO 不解释。 公司开创之后(App启动),那么CEO开始干活了(主线程【UI线程】启动),这时候CEO开启了无限循环工作狂模式,自己的公司没办法啊(相当于UI主线程转成Looper线程)CEO招了一名RD(new Handler 实例)并把告诉PM和UI,如果你们有什么任务和需求就让RD(Handler实例)转告给我(CEO)。RD会把PM和UI的需求(Message)一条条记到CEO的备忘录里(MessageQueue)。CEO 无限循环的工作就是不断查看备忘录,看有什么任务要做,有任务就从备忘录一条一条拿出任务来,然后交给这一名RD(Handler 实例)去处理(毕竟CEO 不会写代码)。当然如果备忘录都做完了,这时候CEO就会去睡觉(线程堵塞【简单理解成线程wait】,让出CPU资源,让其他线程去执行)。但是这个备忘录有个特殊的功能就是没有任务的时候突然插入第一条任务(从无到有)就会有闹钟功能叫醒CEO起床继续处理备忘录。 整个消息处理机制的工作原理基本就是这样的。 这里先给一张Android消息处理机制流程图:二、Looper 1.Looper的构造函数: private Looper(boolean quitAllowed) mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();
2.含义: 在Android的消息机制中扮演着消息循环的角色,具体说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。
3.prepare()方法 /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * @link #loop() after calling this method, and end it by calling * @link #quit(). */ 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)); // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 将一个Looper的实例放入了ThreadLocal,并且判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例。 prepare()函数通过ThreadLocal机制,巧妙地把looper和调用线程关联在一起了。 /** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: @link #prepare() */ public static void prepareMainLooper() prepare(false); synchronized (Looper.class) if (sMainLooper != null) throw new IllegalStateException("The main Looper has already been prepared."); sMainLooper = myLooper();
/** Returns the application's main looper, which lives in the main thread of the application. */ public static Looper getMainLooper() synchronized (Looper.class) return sMainLooper;
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() //从sThreadLocal对象中取出Looper return sThreadLocal.get(); 主线程中的Handler也没有调用Looper.prepare()方法,为什么就没有崩溃呢?这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepareMainLooper()方法,而这个方法又会再去调用Looper.prepare()方法。因此我们应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。总结一下就是在主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象。在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。
4.loop()方法 /** * Run the message queue in this thread. Be sure to call * @link #quit() to end the loop. */ public static void loop() //mylooper()返回保存在调用线程TLV中的Looper对象。 final Looper me = myLooper(); if (me == null) throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); //取出这个Looper的消息队列 f inal 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 Printer logging = me.mLogging; if (logging != null) logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); //调用该消息的handler,交给它的dispatchMessage函数处理 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. 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.recycle(); 当Looper的quit或者quitSafely方法被调用时,Looper就会调用MessageQueue的quit方法并将MessageQueue标记为退出状态,由此 MessageQueue的next方法 ( next()实际上也有一个for(;;),而出口只有两个:消息队列已经退出,返回null;找到了一个合适的消息,将其返回。如果没有合适的消息,或者消息队列为空,会block或者由IdleHandler处理 )就会返回null。
5.Looper的作用: a.与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。 b.loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。 c.我们知道一个线程是一段可执行的代码,当可执行代码执行完成后,线程生命周期便会终止,线程就会退出,那么做为App的主线程,如果代码段执行完了会怎样?,那么就会出现App启动后执行一段代码后就自动退出了,这是很不合理的。所以为了防止代码段被执行完,只能在代码中插入一个死循环,那么代码就不会被执行完,然后自动退出,怎么在在代码中插入一个死循环呢?那么Looper出现了,在主线程中调用Looper.prepare()...Looper.loop()就会变当前线程变成Looper线程(可以先简单理解:无限循环不退出的线程),Looper.loop()方法里面有一段死循环的代码,所以主线程会进入while(true)...的代码段跳不出来,但是主线程也不能什么都不做吧?其实所有做的事情都在while(true)...里面做了,主线程会在死循环中不断等其他线程给它发消息(消息包括:Activity启动,生命周期,更新UI,控件事件等),一有消息就根据消息做相应的处理,Looper的另外一部分工作就是在循环代码中会不断从消息队列挨个拿出消息给主线程处理。
6.应用程序使用looper分为两种情况: a.主线程,也就是ActivityThread(参见条目四)。 b.普通线程: Lopper的准备工作(prepare)--->创建处理消息的handler--->Looper开始运作(loop) 示例: class LooperThread extends Thread public Handler mHandler; public void run() Looper.prepare(); mHandler = new Handler() public void handleMessage(Message msg) //处理消息的地方.... ; Looper.loop();
三、Handler 1.功能: 消息的发送和处理。简单说Handler用于同一个进程的线程间通信。Looper让主线程无限循环地从自己的MessageQueue拿出消息处理,既然这样我们就知道处理消息肯定是在主线程中处理的,那么怎样在其他的线程往主线程的队列里放入消息呢?其实很简单,我们知道在同一进程中线程和线程之间资源是共享的,也就是对于任何变量在任何线程都是可以访问和修改的,只要考虑并发性做好同步就行了,那么只要拿到MessageQueue 的实例,就可以往主线程的MessageQueue放入消息,主线程在轮询的时候就会在主线程处理这个消息。那么怎么拿到主线程 MessageQueue的实例,是可以拿到的(在主线程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 为了统一添加消息和消息的回调处理,又专门构建了Handler类,你只要在主线程构建Handler类,那么这个Handler实例就获取主线程MessageQueue实例的引用(获取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的时候就通过这个引用往消息队列里插入新消息。Handler 的另外一个作用,就是能统一处理消息的回调。这样一个Handler发出消息又确保消息处理也是自己来做,这样的设计非常的赞。具体做法就是在队列里面的Message持有Handler的引用(哪个handler 把它放到队列里,message就持有了这个handler的引用),然后等到主线程轮询到这个message的时候,就来回调我们经常重写的Handler的handleMessage(Message msg)方法。
2.构造函数: /** * Default constructor associates this handler with the @link Looper for the * current thread. * * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. */ public Handler() this(null, false);
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.myLooper()内部实现可以简单理解成:map.get(Thread.currentThread()) //获取当前线程的Looper mLooper = Looper.myLooper(); if (mLooper == null) throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); //让Handler 持有当前线程消息队列的引用,将mLooper里面的消息队列复制到自身的mQueue,这也 //就意味着Handler和Looper是公用一个消息队列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async;
/** * Use the provided @link Looper instead of the default one. * * @param looper The looper, must not be null. */ public Handler( Looper looper) this(looper, null, false); /** * Use the provided @link Looper instead of the default one and take a callback * interface in which to handle messages. * * @param looper The looper, must not be null. * @param callback The callback interface in which to handle messages, or null. */ public Handler( Looper looper, Callback callback) this(looper, callback, false); public Handler( Looper looper, Callback callback, boolean async) mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async;
补充:关于构造函数的说明 a. 在没有传Looper的情况下,Handler 对象在哪个线程下构建(Handler的构造函数在哪个线程下调用),那么Handler 就会持有这个线程的Looper引用和这个线程的消息队列的引用。 因为持有这个线程的消息队列的引用,意味着这个Handler对象可以在任意其他线程给该线程的消息队列添加消息,也意味着Handler的handlerMessage 肯定也是在该线程执行的。 b.如果该线程不是Looper线程,在这个线程new Handler 就会报错! c. public interface Callback public boolean handleMessage(Message msg); d. 当使用参数中含有Looper的构造方法时,那么此时无论Handler对象是在哪个线程构建的,该Handler对象所持有的Looper和MessageQueue都是构造方法中传入的那个Looper,而这个Looper一般都是与指定线程绑定的(否则就没必要使用这个构造函数了),此时相当于在当前线程下构造了一个与指定线程绑定的Handler对象,可以通过该Handler对象向指定线程发送消息,当然该Handler对象的handlerMessage也是运行在指定线程上的。 所以,可以得出结论, handler对象所绑定的线程其实并不取决于该handler对象由哪个线程构建,而是取决于该handler对象所绑定的Looper属于哪个线程。 3.发送消息: a.post方式: /** * Causes the Runnable r to be added to the message queue. * The runnable will be run on the thread to which this handler is * attached. * * @param r The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean post(Runnable r) return sendMessageDelayed(getPostMessage(r), 0);
public final boolean postAtTime(Runnable r, long uptimeMillis) return sendMessageAtTime(getPostMessage(r), uptimeMillis); private static Message getPostMessage(Runnable r) // Return a new Message instance from the global pool. Allows us to avoid allocating new objects in many cases. Message m = Message.obtain(); //将我们传入的参数封装成了一个消息(m.callback就是一个Runnable对象) m.callback = r; return m; 可以看到,在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message。 注:产生一个Message对象,可以new ,也可以使用 Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个 Message池用于Message的复用,避免使用new 重新分配内存。
b,send方式: 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);
/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is @link android.os.SystemClock#uptimeMillis.</b> * You will receive it in @link #handleMessage, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * @link android.os.SystemClock#uptimeMillis time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ 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);
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) //把Message的target设置为自己(Handler) msg.target = this; if (mAsynchronous) msg.setAsynchronous(true); //向消息队列中插入一条消息 return queue.enqueueMessage(msg, uptimeMillis);
MessageQueue中的 enqueueMessage()方法 : boolean enqueueMessage(Message msg, long when) if (msg.isInUse()) throw new AndroidRuntimeException(msg + " This message is already in use."); if (msg.target == null) throw new AndroidRuntimeException("Message must have a target.");
synchronized (this) if (mQuitting) RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false;
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; c. 综述:post的一系列方法最终也是通过send的一系列方法来实现的。 Handler发送消息的过程仅仅是向消息队列中插入了一条消息。 当调用Looper.loop()方法时,MessageQueue的next方法(消息队列的出队方法)就会返回这条消息给Looper,Looper收到消息后就将它传递到msg.target的dispatchMessage()方法中,那这里msg.target又是什么呢?其实就是Handler。通过Handler发送消息时,消息会回到Handler初始化的线程,而不一定是主线程。
d.补充: Handler调用关系整理如下: post()/postDelayed()/sendMessage()->sendMessageDelayed()->sendMessageAtTime()->enqueueMessage() postAtTime()->sendMessageAtTime()->enqueueMessage() postAtFrontOfQueue()->sendMessageAtFrontOfQueue()->enqueueMessage() 最后都以enqueueMessage()告终 enqueueMessage()->MessageQueue.enqueueMessage(Message msg, long when) 如果在消息队列中顺序找到了一个消息msg(消息队列的插入是由when顺序排列,所以如果当前的消息没有到执行时间,其后的也一定不会到),当前的系统时间小于msg.when,那么会计算一个timeout,以便在到执行时间时wake up;如果当前系统时间大于或等于msg.when,那么会返回msg给Looper.loop()。所以这个逻辑只能保证在when之前消息不被处理,不能够保证一定在when时被处理。很好理解: (1)在Loop.loop()中是顺序处理消息,如果前一个消息处理耗时较长,完成之后已经超过了when,消息不可能在when时间点被处理。 (2)即使when的时间点没有被处理其他消息所占用,线程也有可能被调度失去cpu时间片。 (3)在等待时间点when的过程中有可能入队处理时间更早的消息,会被优先处理,又增加了(1)的可能性。 所以由上述三点可知,Handler提供的指定处理时间的api诸如postDelayed()/postAtTime()/sendMessageDelayed()/sendMessageAtTime(),只能保证在指定时间之前不被执行,不能保证在指定时间点被执行。
4.处理消息: /** * 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); dispatchMessage定义了一套消息处理的优先级机制,它们分别是: a.msg的callback不为空,调用handleCallback方法: private final void handleCallback(Message message) message.callback.run(); 直接调用了一开始传入的Runnable对象的run()方法。 Message的callback是一个Runnable对象,实际上就是Handler的post方法所传递的Runable参数(Handler.post(Runnable r))。直接调用线程的run方法相当于是在一个普通的类调用方法,还是在当前线程执行,并不会开启新的线程。 b.Handler如果设置了全局的mCallback(mCallback不为空),调用mCallback.handleMessage(msg) public Handler(Callback callback, boolean async) ....... mCallback = callback; ........ /** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A @link android.os.Message Message object * @return True if no further handling is desired */ public interface Callback public boolean handleMessage(Message msg); 当用new Handler(Callback callback, boolean async)创建Handler对象时,不需要派生Handler的子类(通常通过派生一个Handler的子类并重写其handleMessage方法来处理具体的消息)。 c.如果上述都没有,该消息则会被交给Handler子类实现的handleMessage来处理。
5.补充说明: 其实Handler不仅可以更新UI,你完全可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。
四、主线程消息循环 我们知道,在主线程中创建 Handler 时不用 papare Looper,这是因为在主线程中系统默认创建了 Looper,它在不停地调度分发消息,因此四大组件的调度、我们的输入事件、绘制请求才能得到处理。 ActivityThread 就是我们说的主线程,而它的 main() 方法就是主线程的入口。在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环: public static void main(String[] args) /... Process.setArgV0("<pre-initialized>"); //创建Looper,并添加到线程中 Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); 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(); //可以看出来主线程也是在无限的循环的,异常退出循环的时候会报错. throw new RuntimeException("Main thread loop unexpectedly exited"); /... 主线程的消息循环开始了以后,ActivityThread还需要一个Handler来和消息队列进行交互,这个Handler就是ActivityThread.H,它内部定义了一组消息类型,主要包含了四大组件的启动和停止等过程: private class H extends Handler public static final int LAUNCH_ACTIVITY = 100; public static final int PAUSE_ACTIVITY = 101; public static final int PAUSE_ACTIVITY_FINISHING= 102; public static final int STOP_ACTIVITY_SHOW = 103; public static final int STOP_ACTIVITY_HIDE = 104; public static final int SHOW_WINDOW = 105; public static final int HIDE_WINDOW = 106; public static final int RESUME_ACTIVITY = 107; public static final int SEND_RESULT = 108; public static final int DESTROY_ACTIVITY = 109; public static final int BIND_APPLICATION = 110; public static final int EXIT_APPLICATION = 111; public static final int NEW_INTENT = 112; public static final int RECEIVER = 113; public static final int CREATE_SERVICE = 114; public static final int SERVICE_ARGS = 115; public static final int STOP_SERVICE = 116; //... public void handleMessage(Message msg) if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); 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: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; handleRelaunchActivity(r); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; //... if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); H 的代码很多,只节选一部分。从上述代码可以看到,H 内部定义了消息类型,然后根据消息的类型进行不同的处理。 主线程的消息机制如下: 1.ActivityThread (主线程)通过 ApplicationThread 与 AMS (ActivityManagerService)进行进程通信; 2.AMS 以进程通信的方式完成 ActviityThread 的请求后回调 ApplicationThread 的 Binder方法; 3.然后 ApplicationThread 向 H 发送消息,H 收到消息后会将 ApplicationThread中的逻辑切换到.ActivityThread(主线程)中去执行。
五、HandlerThread的原理 HandlerThread,继承自Thread,本质是Thread,它与普通Thread的差别就在于,它有个Looper成员变量。其内部就是通过Thread+Looper来实现的,说白了HandlerThread就是Android已经封装好的一个拥有自己looper的线程,我们可以利用它执行一些耗时任务。
@Override public void run() mTid = Process.myTid(); Looper.prepare(); synchronized (this) mLooper = Looper.myLooper(); notifyAll(); Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; 这个是HandlerThread的run方法,代码也比较简单,开始就通过Looper.prepare()去创建Looper对象,然后通过同步线程去给当前成员变量mLooper赋值,并唤醒等待线程(后续会解析为什么要唤醒等待线程),然后在Looper.loop()循环启动前调用了onLooperPrepared方法,到此Looper创建完成,循环线程也启动完成。现在我们也就明白了创建HandlerThread后为什么要调用start方法了,因为通过调用start方法,程序会去执行run方法,这样才会去创建Looper对象并启动Looper循环,最后我们才能把Looper对象传递给Handler实例。 /** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason is isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized. * @return The looper. */ public Looper getLooper() if (!isAlive()) return null; // If the thread has been started, wait until the looper has been created. synchronized (this) while (isAlive() && mLooper == null) try wait(); catch (InterruptedException e) return mLooper; 这个方法名一看就知道是获取looper对象的,虽然很简单,但是这里有个地方还是要说明一下,方法开始后先去判断当前线程是否是启动状态,如果线程已经启动,再通过一个同步代码块去判断当前成员变量mLooper是否为空,如果为空,那就wait(),直到mLooper创建完成,否则就返回mLooper对象,那么为什么会由可能为空呢?还记得前面的Looper对象是在哪里创建的吗?没错,是在子线程,这样我们就无法保障我们在调用getLooper方法时Looper已经创建完成。因此在前面的run方法中当Looper创建完成后会调用notifyAll方法就是为了唤醒getLooper方法中的wait等待机制。 小结:在获取mLooper对象的时候存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值。这里等待方法wait和run方法中的notifyAll方法共同完成同步问题。
六、 Handler内存泄漏原理及其解决方案 1.Handler内存泄漏原理 在java中非静态内部类和匿名内部类都会隐式持有当前类的外部引用,由于Handler是非静态内部类所以其持有当前Activity的隐式引用,如果Handler没有被释放,其所持有的外部引用也就是Activity也不可能被释放。 分析:当我们执行了Activity的界面时,被延迟的消息会在被处理之前存在于主线程消息队列中5分钟,而这个消息中又包含了Handler的引用,而我们创建的Handler又是一个匿名内部类的实例,其持有外部Activity的引用,这将导致了Activity无法回收,进而导致Activity持有的很多资源都无法回收,从而就造成了内存泄露问题! 27. public class MainActivity extends Activity 28. ...... 29. 30. @Override 31. protected void onCreate(Bundle savedInstanceState) 32. super.onCreate(savedInstanceState); 33. ...... 34. 35. new Thread(new Runnable() 36. @Override 37. public void run() 38. HttpUtils.downloadFile(url); 39. mHandler.sendEmptyMessage(MSG_SUCCESS); 40. 41. ).start(); 42. 43. 44. private Handler mHandler = new Handler() 45. @Override 46. public void handleMessage(Message msg) 47. if (msg.what == MSG_SUCCESS) 48. // your code 49. 50. 51. ; 52. 分析:在MainActivity启动时,开启一个子线程来下载文件。如果文件较大或网络不稳定的因素,导致短时间内无法执行完成,用户按下返回键退出了当前界面。此时子线程仍然在运行,并持有mHandler的引用,而mHandler是一个匿名内部类的对象,持有MainActivity的引用,这样MainActivity对象无法被回收,MainActivity内部的很多资源都无法被回收。从而就造成了内存泄露问题!
2.Handler内存泄漏解决方案: 解决这个问题思路就是使用静态内部类并继承Handler(或者也可以单独存放成一个类文件)。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理。另外关于同样也需要将Runnable设置为静态的成员属性。修改后不会导致内存泄露的代码如下: 1. public class HandlerActivity extends Activity 2. //创建一个2M大小的int数组 3. int[] datas=new int[1024*1024*2]; 4. // Handler mHandler = new Handler() 5. // @Override 6. // public void handleMessage(Message msg) 7. // super.handleMessage(msg); 8. // 9. // ; 10. /** 11. * 创建静态内部类 12. */ 13. private static class MyHandler extends Handler 14. //持有弱引用HandlerActivity,GC回收时会被回收掉. 15. private final WeakReference<HandlerActivity> mActivty; 16. public MyHandler(HandlerActivity activity) 17. mActivty =new WeakReference<HandlerActivity>(activity); 18. 19. @Override 20. public void handleMessage(Message msg) 21. HandlerActivity activity=mActivty.get(); 22. super.handleMessage(msg); 23. if(activity!=null) 24. //执行业务逻辑 25. 26. 27. 28. private static final Runnable myRunnable = new Runnable() 29. @Override 30. public void run() 31. //执行我们的业务逻辑 32. 33. ; 34. @Override 35. protected void onCreate(Bundle savedInstanceState) 36. super.onCreate(savedInstanceState); 37. setContentView(R.layout.activity_handler_leak); 38. MyHandler myHandler=new MyHandler(this); 39. //解决了内存泄漏,延迟5分钟后发送 40. myHandler.postDelayed(myRunnable, 1000 * 60 * 5); 41. 42. 我们也可以在activity的onDestory中添加了如下代码,清空所有handler中没有执行完的任务: protected void onDestroy() super.onDestroy(); handler.removeCallbacksAndMessages(null);
3.总结:
- handler造成内存泄漏是因为在Activity销毁的时候还有未执行完的任务。
- 单独采用静态static可以解决内存泄漏,但如果 Handler 不是以内部类的形式出现,那么 static 是不需要的。
- 使用弱引用也可以解决内存泄漏,但是需要等到handler的中任务都执行完,才会释放activity内存,不如直接static释放的快。
- 解决handler造成内存泄漏有两种方案:一种是业务逻辑上,在activity销毁的时候移除所有未执行的任务。一种是从GC上,通过static的Handler或者弱引用解决。但是单独使用弱引用性能不是太高。
七、总结: Android中的Looper类主要作用是来封装消息循环和消息队列的,用于在android线程中进行消息处理。handler是用来向消息队列中插入消息的并最好对消息进行处理。 (1) Looper类主要是为每个线程开启的单独的消息循环。 默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环) Looper对象负责管理MessageQueue,而MessageQueue主要是用来存放handler发送的消息,而且一个线程只能有一个Looper,对应一个MessageQueue。 (2) 我们通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper中的MessageQueue发送消息并且Handler还必须定义自己的处理方法。 默认情况下Handler会与其被定义时所在线程的Looper绑定,如Handler在主线程中定义,它是与主线程的Looper绑定。 mainHandler = new Handler() 等价于 new Handler(Looper.myLooper()) Looper.myLooper():获取当前进程的looper对象, Looper.getMainLooper() 用于获取主线程的Looper对象。 (3) 在非主线程中直接new Handler() 会报如下的错误: Can't create handler inside thread that has not called Looper.prepare() 原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper,然后再调用Looper.loop()。 (4) Looper.loop():启动looper中的循环线程,Handler就会从消息队列里取消息并进行对应处理。 最后要注意的是写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop()才会中止,其后的代码才能得以运行。 (5) Handler是如何实现线程之间的切换: Handler切换线程的机制最终原因是handleMessage方法的调用位置的切换,MessageQueue内部使用单链表来存储信息Message,Handler发送的Message全部都添加到了MessageQueue中,Looper.loop()方法通过调用MessageQueue.next()方法不断遍历链表中的Message,当取得符合的Message后,通过Message持有的Handler对象引用调用Handler.handleMessage方法,handler将自己的引用间接被Looper持有,如此, Looper.loop()在哪个线程调用的,handleMessage方法就切换到哪个线程了。 例如现在有A、B两个线程,在A线程中有创建了handler , 然后在B线程中调用handler发送一个message。 通过上面的分析我们可以知道,当在A线程中创建handler的时候, 同时创建了MessageQueue与Looper,Looper在A线程中调用loop进入一个无限的for循环从MessageQueue中取消息,当B线程调用handler发送一个message的时候,会通过msg.target.dispatchMessage(msg);将message插入到handler对应的MessageQueue中,Looper发现有message插入到MessageQueue中,便取出message执行相应的逻辑,因为Looper.loop()是在A线程中启动的,所以回到了A线程,达到了从B线程切换到A线程的目的。
以上是关于Android 消息处理机制的主要内容,如果未能解决你的问题,请参考以下文章