一、IdleHandler工作原理
1. IdleHandler
接口定义
IdleHandler
是 MessageQueue
类中的一个接口,定义如下:
public static interface IdleHandler {
/**
* 当消息队列空闲时会调用此方法。
* @return 如果返回 true,则该 IdleHandler 会保留在消息队列中,下次空闲时会再次调用;
* 如果返回 false,则该 IdleHandler 会从消息队列中移除。
*/
boolean queueIdle();
}
2. IdleHandler
的添加
要使用 IdleHandler
,需要通过 MessageQueue
的 addIdleHandler()
方法将其添加到消息队列中。以下是 addIdleHandler()
方法的源码:
// MessageQueue.java
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
从源码可以看出,addIdleHandler()
方法会将传入的 IdleHandler
实例添加到 mIdleHandlers
列表中。mIdleHandlers
是一个 ArrayList<IdleHandler>
类型的列表,用于存储所有添加的 IdleHandler
。
3. 消息队列空闲状态的判断
Looper
在从 MessageQueue
中取出消息时,会调用 MessageQueue
的 next()
方法。next()
方法会判断消息队列是否空闲,以下是 next()
方法的部分源码:
// MessageQueue.java
Message next() {
// ... 其他代码 ...
int nextPollTimeoutMillis = 0;
for (;;) {
// ... 其他代码 ...
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// ... 其他代码 ...
// 获取队列中的下一条消息
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 如果消息没有目标 Handler,继续查找下一条消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 如果消息还未到执行时间,计算需要等待的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 消息可以执行,从队列中移除该消息
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 {
// 队列中没有消息,设置为无限期等待
nextPollTimeoutMillis = -1;
}
// ... 其他代码 ...
// 如果队列中没有消息或者下一条消息还未到执行时间,认为队列空闲
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 没有 IdleHandler 或者队列不空闲,继续等待
mBlocked = true;
continue;
}
// ... 其他代码 ...
}
// ... 其他代码 ...
}
}
从源码可以看出,当队列中没有消息或者下一条消息的执行时间还未到时,MessageQueue
会认为队列处于空闲状态,并开始处理 IdleHandler
。
4. IdleHandler
的执行
当 MessageQueue
处于空闲状态时,会在 next()
方法中依次调用存储在 mIdleHandlers
列表中的所有 IdleHandler
的 queueIdle()
方法。以下是相关源码:
// MessageQueue.java
Message next() {
// ... 其他代码 ...
int pendingIdleHandlerCount = -1;
for (;;) {
// ... 其他代码 ...
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 没有 IdleHandler 或者队列不空闲,继续等待
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 执行 IdleHandler
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 防止内存泄漏
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// ... 其他代码 ...
return null;
}
从源码可以看出,MessageQueue
会将 mIdleHandlers
列表中的 IdleHandler
复制到 mPendingIdleHandlers
数组中,然后依次调用每个 IdleHandler
的 queueIdle()
方法。根据 queueIdle()
方法的返回值决定是否保留该 IdleHandler
:
- 如果返回
true
,则该IdleHandler
会保留在mIdleHandlers
列表中,下次消息队列空闲时会再次调用。 - 如果返回
false
,则该IdleHandler
会从mIdleHandlers
列表中移除。
总结
IdleHandler
是 Android 消息机制中的一个重要特性,它允许开发者在消息队列空闲时执行特定任务。其工作原理主要包括:通过 addIdleHandler()
方法将 IdleHandler
添加到 MessageQueue
的 mIdleHandlers
列表中;Looper
在从 MessageQueue
中取出消息时,MessageQueue
的 next()
方法会判断队列是否空闲;当队列空闲时,会依次调用 mIdleHandlers
列表中的所有 IdleHandler
的 queueIdle()
方法,并根据返回值决定是否保留该 IdleHandler
。通过这种方式,IdleHandler
可以在不影响主线程正常消息处理的前提下,执行一些低优先级的任务,从而优化应用的性能。
二、IntentService的工作原理
IntentService
是 Android 中的一个特殊服务,继承自 Service
类,它结合了 Service
和 HandlerThread
的特性,用于在后台线程执行异步任务,并且在任务完成后自动停止服务。以下详细介绍其工作原理。
1. 继承关系与构造函数
IntentService
继承自 Service
,这意味着它拥有 Service
的生命周期方法。当创建 IntentService
的子类时,必须实现一个构造函数并调用父类的构造函数,传入一个用于标识该服务的名称。这个名称主要用于日志记录和调试。
import android.app.IntentService;
import android.content.Intent;
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
// 处理传入的 Intent
}
}
2. 内部的 HandlerThread
在 IntentService
的构造函数被调用后,它会创建一个 HandlerThread
。HandlerThread
是一个带有 Looper
的线程,这使得它可以处理消息队列。IntentService
利用这个 HandlerThread
来执行后台任务,避免在主线程中执行耗时操作导致界面卡顿。
// IntentService.java 部分源码
public IntentService(String name) {
super();
mName = name;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
3. ServiceHandler
与消息处理
IntentService
创建了一个 ServiceHandler
类,它继承自 Handler
,并使用 HandlerThread
的 Looper
来处理消息。当有新的 Intent
被发送到 IntentService
时,onStartCommand
方法会将这个 Intent
封装成一个消息发送给 ServiceHandler
。
// IntentService.java 部分源码
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
4. onHandleIntent
方法
ServiceHandler
接收到消息后,会调用 onHandleIntent
方法,并将 Intent
作为参数传递进去。开发者需要在 onHandleIntent
方法中实现具体的异步任务逻辑。这个方法运行在 HandlerThread
的线程中,因此可以执行耗时操作。
@Override
protected void onHandleIntent(Intent intent) {
// 执行耗时任务,例如网络请求、文件读写等
if (intent != null) {
String action = intent.getAction();
if (ACTION_FOO.equals(action)) {
// 处理特定的操作
}
}
}
5. 任务完成后自动停止服务
当 onHandleIntent
方法执行完毕后,ServiceHandler
会调用 stopSelf(int startId)
方法来停止服务。startId
是一个唯一标识,用于确保只有当所有的 Intent
都被处理完毕后才停止服务。如果在处理过程中有新的 Intent
被发送进来,IntentService
会继续处理这些新的 Intent
,直到所有任务都完成。
6. 总结
- 创建
HandlerThread
:在onCreate
方法中创建一个HandlerThread
,并启动它,获取其Looper
。 - 创建
ServiceHandler
:使用HandlerThread
的Looper
创建ServiceHandler
,用于处理消息。 - 接收
Intent
:在onStartCommand
方法中,将Intent
封装成消息并通过ServiceHandler
发送出去。 - 处理
Intent
:ServiceHandler
接收到消息后,调用onHandleIntent
方法处理Intent
。 - 停止服务:在
onHandleIntent
方法处理完成后,调用stopSelf(int startId)
方法停止服务。 - 销毁服务:在
onDestroy
方法中,停止HandlerThread
的Looper
。
通过这种方式,IntentService
提供了一种简单方便的方式来在后台线程执行异步任务,并且在任务完成后自动清理资源。