Handler机制应该算是面试时候被问到的频率比较高的一个问题了,也是我们工作中常用到的知识点。所以这里想全面深入的结合源码研究一下安卓的handler机制究竟是什么,以及和Handler相关的一些知识点。
2:ActivityThread是线程吗?它是安卓的UI线程吗?主线程到底是什么?
3:Looper是线程吗?一个线程可以有几个Looper,几个Handler?
6:post(Runnable)是开了一个线程吗?postDelay(Runnable,delayMills)发出的消息在MessageQueue是如何按先后顺序执行的?
1:安卓的Handler机制是什么?
handler 是Google为我们提供的方便在子线程中去与主线程进行通信进而在主线程来刷新UI的一套非常重要的机制。安卓系统之所以为我们提供这样一套机制是因为 Android 中所有的UI控件都不是线程安全的,这就会导致在多线程并发操作UI刷新的时候造成一些问题。而多线程进行加锁处理又会让 UI 访问逻辑变的很复杂,开发者需要时刻考虑多线程并发将会带来的问题,其次锁机制也严重影响 UI 的访问效率。而有了handler开发者根本不用去关心多线程问题,所有的更新UI的操作都是在主线程的消息队列(MessageQueue是一个链表结构)中去轮询处理的。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++截取Google一段对Handler的说明:
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed at some point in the future;
(2) and to enqueue an action to be performed on a different thread than your own.
翻译一下:
Handler允许您发送和处理与线程的MessageQueue关联的消息和Runnable对象。每个Handler实例都与单个线程和该线程的消息队列相关联。当您创建一个新的Handler时,它就和创建她的线程以及这个线程的消息队列绑定到一起了。它将向该消息队列发送messages和runnables,并在它们从消息队列出来时执行它们。
Handler有两种主要用途:
(1)将消息和runnables安排在未来的某个时刻执行;
(2)将一个要在不同线程上执行的操作排队。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
下面我们来看一下Handler的结构图:
Handler扮演了往MQ上添加消息和处理消息的角色,即通知MQ它要执行一个任务(sendMessage等),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。
下面我们从源码角度来看一下Handler机制的几个核心类:Looper,MessageQueue和Message 都做了哪些事情。
首先我们从ActivityThread的main()入口函数开始:
ActivityThread类{
public static void main(String[] args) {
.....
Looper.prepareMainLooper();
.....
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
....
Looper.loop();
}
}
这里主要做了三件事:
- 初始化主线程Looper-->prepareMainLooper
- new一个ActivityThread,并进行attach操作(关于这块代码知识点,可以参考我的另一篇博客 TODO:安卓的启动流程)
- 开启loop循环
下面我们看一下Looper类的源码看一下1和3都做了哪些事情:
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();
}
}
/**
* 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();
}
/** 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));
}
}
首先是Looper.prepareMainLooper()方法,其最后调用到prepare方法。这里有个参数quitAllowed需要注意一下,这里传入的是flase。这个参数最终是传递给了MessageQueue的quit方法:主线程是不可以quit的。
MessageQueue类{
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);
}
}
}
言归正传我们继续看prepare这个方法都做了什么:
Looper类{
-------------------------------------------------------------------------------
// Looper的一个变量,用来和当前线程进行绑定
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
-------------------------------------------------------------------------------
//Lopper.prepare()这里是创建了一个Looper对象,并将当前线程的Looper对象存到ThreadLocal中。
//首先判断一下当前线程的Looper是不是已经创建了,如果未创建则创建Looper并存入ThreadLocal中。这也
//为什么 Handler 构造方法里面的 Looper 不是直接 new ?
//如果在 Handler 构造方法里面直接 new Looper(), 可能是无法保证 Looper 唯一,只有用
//Looper.prepare() 才能保证唯一性(ThreadLocal)
//也解释了为什么一个线程只能有一个Looper。
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));
}
}
我们看一下ThreadLocal的get和set方法:
//Handler 就是通过这个ThreadLocal完成线程切换的。
ThreadLocal类{
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
}
这里有个ThreadLocalMap,说白了这就是个Map,以当前线程作为Key,存储自己的一些变量副本。
我们继续言归正传正传,看看Looper.loop都做了哪些操作:
Looper类{
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();//这个方法就一句代码sThreadLocal.get()得到当前线程的Looper,见上面源码;
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//得到和自己绑定的MessageQueue
....
//这里执行了一个for的死循环,不断的从MessageQueue中取Message
for (;;) {
Message msg = queue.next(); // might block //往下看,后面讲
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
....
....
try {
//这个target就是把这个msg添加到MessageQueue的handler(这也就是为什么handler谁发消息谁就处理这里),如果msg不为null,handler就要分发消息了
msg.target.dispatchMessage(msg);
....
} catch (Exception exception) {
....
} finally {
....
}
....
....
}
}
}
MessageQueue类{
@UnsupportedAppUsage
Message next() {
for (;;) {
....
//这里就主要看这一行代码,该方法将一直阻塞直到添加新消息为止
nativePollOnce(ptr, nextPollTimeoutMillis);
....
}
}
//添加message时,如postRunnable等,最后会调用这个方法,执行nativeWake方法进行唤醒
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 插入到queue
}
if (needWake) { nativeWake(mPtr); }
}
return true;
}
}
面试时候我们经常会被问到:为什么主线程的Looper.loop()死循环不会导致ANR?主线程的死循环一直运行是不是特别消耗CPU资源呢?这里我们就要注意上边源码的这行代码了:Message msg = queue.next()--->nativePollOnce(),这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
Handler的整个流程大概就这些,对于nativiePollOnce方法底层的实现会在以后的文章中详细说明。
2:ActivityThread是线程吗?它是安卓的UI线程吗?主线程到底是什么?
ActivityThread不是UI主线程。它甚至都没继承Thread,本质上和线程没有任何关系。他只是App进程的一个初始类,它的main函数是App进程的入口,App进程中UI事件的执行代码段都是由ActivityThread提供的。而UI线程在哪呢?我们知道当Zygote启动时孵化了system_server进程和Launcher进程。点击桌面APP图标时,app在内存中是无实例的,Launcher进程通过Binder机制给system_server进程发消息,然后system_server通过socket机制通知zygote进程去fork出app进程并执行ActivityThread的main方法。也就是说,Main Thread实例是存在的,只是创建它的代码我们不可见。ActivityThread的main函数就是在这个Main Thread里被调用执行的。
3:Looper是线程吗?一个线程可以有几个Looper,几个Handler?
Looper同样没有继承Tread类或实现Runnable接口,所以Looper并不是一个线程,他的作用主要是维护一个for的死循环,不断的从MessageQueue中取Message,并让传入这个Message的Handler进行处理(handler在发消息的时候,Message中有个target,这target就是发送这个Message的handler)。Looper使用ThreadLocal保证了每个线程只有一个Looper,但是你可以去new很多个Handler去往这个Looper中发消息。
4:IdleHandler是什么,怎么使用?
IdleHandler是MessageQueue中的一个接口。
MessageQueue类{
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*/
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
}
它可以用来提升性能,idle的意思是闲置的,它主要用在我们希望能够在当前线程消息队列空闲时做些事情(譬如 UI 线程在显示完成后,如果线程空闲我们就可以提前准备其他内容)的情况下,不过最好不要做耗时操作。具体用法如下(参考自 面试题之 IdleHandler 相关原理浅析):
//getMainLooper().myQueue()或者Looper.myQueue()
Looper.myQueue().addIdleHandler(new IdleHandler() {
@Override
public boolean queueIdle() {
//你要处理的事情
return false;
}
});
关于 IdleHandler 在 MessageQueue 与 Looper 和 Handler 的关系原理源码分析如下:
/**
* 获取当前线程队列使用Looper.myQueue(),获取主线程队列可用getMainLooper().myQueue()
*/
public final class MessageQueue {
......
/**
* 当前队列将进入阻塞等待消息时调用该接口回调,即队列空闲
*/
public static interface IdleHandler {
/**
* 返回true就是单次回调后不删除,下次进入空闲时继续回调该方法,false只回调单次。
*/
boolean queueIdle();
}
/**
* <p>This method is safe to call from any thread.
* 判断当前队列是不是空闲的,辅助方法
*/
public boolean isIdle() {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
return mMessages == null || now < mMessages.when;
}
}
/**
* <p>This method is safe to call from any thread.
* 添加一个IdleHandler到队列,如果IdleHandler接口方法返回false则执行完会自动删除,
* 否则需要手动removeIdleHandler。
*/
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
/**
* <p>This method is safe to call from any thread.
* 删除一个之前添加的 IdleHandler。
*/
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
......
//Looper的prepare()方法会通过ThreadLocal准备当前线程的MessageQueue实例,
//然后在loop()方法中死循环调用当前队列的next()方法获取Message。
Message next() {
......
for (;;) {
......
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
......
//把通过addIdleHandler添加的IdleHandler转成数组存起来在mPendingIdleHandlers中
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
//循环遍历所有IdleHandler
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//调用IdleHandler接口的queueIdle方法并获取返回值。
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果IdleHandler接口的queueIdle方法返回false说明只执行一次需要删除。
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
......
}
}
}
5:HandlerThread是什么,怎么使用?
HandlerThread说白了就是Thread+Handler+Looper。我想很多人面试时候被问到或者平时自己都写过实现Looer机制的线程,肯定也遇到这样那样的问题,HandlerThread就是google给我封装好的一个实现Looper自制的线程,它的源码也就100多行,copy过来方便大家阅读:
/**
* A {@link Thread} that has a {@link Looper}.
* The {@link Looper} can then be used to create {@link Handler}s.
* <p>
* Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason 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;
}
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
需要注意的是 HandlerThread 是需要手动quit的,这里给我提供了两个方法,两个方法最终执行到MessageQueue的quit方法中。这里有个if..else判断:
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
//安全的会调用这个方法,它会根据Message.when这个属性(不懂这个属性的后边会讲到),判断我们当前消息队列是否正在处理消息,
//没有正在处理消息的话,直接移除所有回调,正在处理的话,等待该消息处理处理完毕再退出该循环。
//因此说quitSafe()是安全的,而quit()方法是不安全的。
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);
}
}
}
//不安全的会调用removeAllMessagesLocked()这个方法,其实就是遍历Message链表,移除所有信息的回调,并重置为null
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
6:post(Runnable)是开了一个线程吗?postDelay(Runnable,delayMills)发出的消息在MessageQueue是如何按先后顺序执行的?
我们经常会在主线程中new一个Handler,然后postDelayed()去执行一个定时操作,或者直接用view去postDelayed【Handler.post和View.post的区别】,其实这并不是开了一个线程,只是发了一个消息到Looper的MessageQueue中。最终执行还是在主线程中执行的。Handler中给我们提供了很多发消息的方法:比如我们常用的post,postDelayed,sendMessage,sendEmptyMessage等,最后执行到下边的方法中:
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;//注意:这里的this就是当前发消息的Handler,handler作为message的target属性,就是上边讲到的当Looper.loop方法执行到 msg.target.dispatchMessage(msg)的时候用到。
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里我们看到uptimeMillis = SystemClock.uptimeMillis() + delayMillis,【大家可以思考一下为什么这里不用System.currentTimeMillis()而是SystemClock.uptimeMillis()】,并最终传入了MessageQueue的enqueueMessgae方法中
boolean enqueueMessage(Message msg, long when) {
....
synchronized (this) {
....
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//如果当前发的这个message比MessageQueue(单链表结构)表头消息的时间要小,也就是要先执行,那么就放到消息队列最前边,成为新的表头
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// 新的头节点,如果queue正在阻塞状态就唤醒
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 (;;) {
//这里的for循环对所有的消息做了一个时间排序
prev = p;
p = p.next;
// 一致循环,直到最后一个消息(p == null)
// 或者这个 message 的 when 小于我们当前这个 message 的 when
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;
}
7:IntentService是什么,怎么使用?
IntentService是一个抽象类,继承Service,所以本质上是个Service,内部封装了一个HandlerThread,我们可以把它看成是Service和HandlerThread的结合体。IntentService在完成了使命之后会自动停止,不需要我们手动结束。如果启动 IntentService 多次,那么每一个耗时操作会以工作队列的方式在 IntentService 的 onHandleIntent 回调方法中执行,依次去执行,使用串行的方式,执行完自动结束。
IntentService和Service的区别:普通的Service与它所在的应用处于同一个进程,Service也不是一条新的线程,所以不可以在Service中直接处理耗时的任务。IntentService使用单独的线程来处理任务,可以用来处理耗时任务,任务执行后会自动停止。
8:什么是同步屏障?
什么是Handler的消息同步屏障呢?说白了就是一个让异步消息优先处理的机制。我们知道Message有普通消息(同步消息)、屏障消息(同步屏障)和异步消息三种,屏障消息和普通消息的区别在于屏障没有tartget,普通消息有target是因为它需要将消息分发给对应的target,而屏障不需要被分发,它就是用来挡住普通消息来保证异步消息优先处理的。屏障和普通消息一样可以根据时间来插入到消息队列中的适当位置,并且只会挡住它后面的同步消息的分发。我们平时通过handler发送的消息都是同步消息。关于同步屏障的更多信息大家可以看一下Handler同步屏障机制的原理和使用场景。
9:和Handler有关的内存泄漏问题?
关于匿名内部类相关的原因和解决办法这里就不赘述了,网上一搜一大堆,这里只说关于这个CallBack的返回值,当然下边这种写法也存在匿名内部类持有外部类引用可能导致内存泄漏的问题,这里不关注这个。
//你发现这几行代码被编译器提示了--This Handler class should be static or leaks might occur,有人觉得这样的提示不好看又改成下边这样,发现终于不提示了。
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
//1 ...
return false;
}
}){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//2 ...
}
};
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
//这个msg.callback是个Runnable
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我们看else部分, if (mCallback.handleMessage(msg)) {return;}这个if的判断条件就是我们在CallBack的handleMessage中的返回数值。如果return ture下边2处就不执行了。就是起了个拦截作用,至于什么情况下会用到这个CallBack就看具体的功能逻辑了,反正我还没遇到过。当然有人可能会这样写来去除黄色警告⚠️,但是依然存在匿名内部类可能导致的内存泄漏。最常见的处理方式就是静态内部类+弱引用。
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
//1 ...
return false;
}
});
有问题欢迎大家提出,方便改进和讨论!