Android学习总结之扩展基础篇(一)

一、IdleHandler工作原理

1. IdleHandler 接口定义

IdleHandlerMessageQueue 类中的一个接口,定义如下:

java 复制代码
public static interface IdleHandler {
    /**
     * 当消息队列空闲时会调用此方法。
     * @return 如果返回 true,则该 IdleHandler 会保留在消息队列中,下次空闲时会再次调用;
     *         如果返回 false,则该 IdleHandler 会从消息队列中移除。
     */
    boolean queueIdle();
}

2. IdleHandler 的添加

要使用 IdleHandler,需要通过 MessageQueueaddIdleHandler() 方法将其添加到消息队列中。以下是 addIdleHandler() 方法的源码:

java 复制代码
// 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 中取出消息时,会调用 MessageQueuenext() 方法。next() 方法会判断消息队列是否空闲,以下是 next() 方法的部分源码:

java 复制代码
// 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 列表中的所有 IdleHandlerqueueIdle() 方法。以下是相关源码:

java 复制代码
// 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 数组中,然后依次调用每个 IdleHandlerqueueIdle() 方法。根据 queueIdle() 方法的返回值决定是否保留该 IdleHandler

  • 如果返回 true,则该 IdleHandler 会保留在 mIdleHandlers 列表中,下次消息队列空闲时会再次调用。
  • 如果返回 false,则该 IdleHandler 会从 mIdleHandlers 列表中移除。

总结

IdleHandler 是 Android 消息机制中的一个重要特性,它允许开发者在消息队列空闲时执行特定任务。其工作原理主要包括:通过 addIdleHandler() 方法将 IdleHandler 添加到 MessageQueuemIdleHandlers 列表中;Looper 在从 MessageQueue 中取出消息时,MessageQueuenext() 方法会判断队列是否空闲;当队列空闲时,会依次调用 mIdleHandlers 列表中的所有 IdleHandlerqueueIdle() 方法,并根据返回值决定是否保留该 IdleHandler。通过这种方式,IdleHandler 可以在不影响主线程正常消息处理的前提下,执行一些低优先级的任务,从而优化应用的性能。

二、IntentService的工作原理

IntentService 是 Android 中的一个特殊服务,继承自 Service 类,它结合了 ServiceHandlerThread 的特性,用于在后台线程执行异步任务,并且在任务完成后自动停止服务。以下详细介绍其工作原理。

1. 继承关系与构造函数

IntentService 继承自 Service,这意味着它拥有 Service 的生命周期方法。当创建 IntentService 的子类时,必须实现一个构造函数并调用父类的构造函数,传入一个用于标识该服务的名称。这个名称主要用于日志记录和调试。

java 复制代码
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 的构造函数被调用后,它会创建一个 HandlerThreadHandlerThread 是一个带有 Looper 的线程,这使得它可以处理消息队列。IntentService 利用这个 HandlerThread 来执行后台任务,避免在主线程中执行耗时操作导致界面卡顿。

java 复制代码
// 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,并使用 HandlerThreadLooper 来处理消息。当有新的 Intent 被发送到 IntentService 时,onStartCommand 方法会将这个 Intent 封装成一个消息发送给 ServiceHandler

java 复制代码
// 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 的线程中,因此可以执行耗时操作。

java 复制代码
@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 :使用 HandlerThreadLooper 创建 ServiceHandler,用于处理消息。
  • 接收 Intent :在 onStartCommand 方法中,将 Intent 封装成消息并通过 ServiceHandler 发送出去。
  • 处理 IntentServiceHandler 接收到消息后,调用 onHandleIntent 方法处理 Intent
  • 停止服务 :在 onHandleIntent 方法处理完成后,调用 stopSelf(int startId) 方法停止服务。
  • 销毁服务 :在 onDestroy 方法中,停止 HandlerThreadLooper

通过这种方式,IntentService 提供了一种简单方便的方式来在后台线程执行异步任务,并且在任务完成后自动清理资源。

相关推荐
小钊(求职中)3 分钟前
ElasticSearch从入门到精通-覆盖DSL操作和Java实战
java·大数据·elasticsearch·搜索引擎·全文检索
Anger重名了25 分钟前
🌟 一篇文章搞懂Kotlin协程:比线程更轻量的并发神器
android
极客智谷28 分钟前
深入理解Java线程池:从原理到实战的完整指南
java·后端
缘来的精彩29 分钟前
adb常用的20个命令
android·adb·kotlin
虾球xz30 分钟前
游戏引擎学习第247天:简化DEBUG_VALUE
c++·学习·游戏引擎
代码不行的搬运工35 分钟前
HTML快速入门-4:HTML <meta> 标签属性详解
java·前端·html
tangweiguo030519871 小时前
Android kotlin通知功能完整实现指南:从基础到高级功能
android·kotlin
KimLiu1 小时前
适合Android开发者的Flutter学习指南 : 一、搭建Flutter环境
android·前端·flutter
mask哥1 小时前
详解最新链路追踪skywalking框架介绍、架构、环境本地部署&配置、整合微服务springcloudalibaba 、日志收集、自定义链路追踪、告警等
java·spring cloud·架构·gateway·springboot·skywalking·链路追踪
XU磊2601 小时前
javaWeb开发---前后端开发全景图解(基础梳理 + 技术体系)
java·idea