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 提供了一种简单方便的方式来在后台线程执行异步任务,并且在任务完成后自动清理资源。

相关推荐
tmacfrank40 分钟前
网络编程中的直接内存与零拷贝
java·linux·网络
weixin_472339462 小时前
Maven 下载安装与配置教程
java·maven
向上的车轮2 小时前
MATLAB学习笔记(七):MATLAB建模城市的雨季防洪排污的问题
笔记·学习·matlab
Magnum Lehar3 小时前
3d游戏引擎EngineTest的系统实现3
java·开发语言·游戏引擎
就叫飞六吧3 小时前
Spring Security 集成指南:避免 CORS 跨域问题
java·后端·spring
Mcworld8573 小时前
java集合
java·开发语言·windows
天黑请闭眼3 小时前
IDEA:程序编译报错:java: Compilation failed: internal java compiler error
java·intellij-idea
前端小崔3 小时前
从零开始学习three.js(18):一文详解three.js中的着色器Shader
前端·javascript·学习·3d·webgl·数据可视化·着色器
龙湾开发4 小时前
计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 10.增强表面细节(二)法线贴图
c++·笔记·学习·图形渲染·贴图
苍煜4 小时前
Maven构建流程详解:如何正确管理微服务间的依赖关系-当依赖的模块更新后,我应该如何重新构建主项目
java·微服务·maven