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

news2025/4/25 16:51:59

一、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 的构造函数被调用后,它会创建一个 HandlerThreadHandlerThread 是一个带有 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 发送出去。
  • 处理 IntentServiceHandler 接收到消息后,调用 onHandleIntent 方法处理 Intent
  • 停止服务:在 onHandleIntent 方法处理完成后,调用 stopSelf(int startId) 方法停止服务。
  • 销毁服务:在 onDestroy 方法中,停止 HandlerThread 的 Looper

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2342568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Godot开发2D冒险游戏——第二节:主角光环整起来!

变量的作用域 全局变量&#xff0c;局部变量&#xff0c;导出变量&#xff08;可以在检查器当中快速查看&#xff09; 为玩家添加移动动画 现在游戏的玩家还只是在滑行&#xff0c;我们需要再添加玩家每个方向上的移动效果 删除原先的Item节点&#xff0c;创建一个动画精灵…

.NETCore部署流程

资料下载&#xff1a;https://download.csdn.net/download/ly1h1/90684992 1.下载托管包托管捆绑包 | Microsoft Learn&#xff0c;下载后点击安装即可。 2.安装IIS 3.打开VS2022&#xff0c;新建项目&#xff0c;选择ASP.NET Core Web API 5.Program修改启动项&#xff0c;取…

数据结构——二叉树,堆

目录 1.树 1.1树的概念 1.2树的结构 2.二叉树 2.1二叉树的概念 2.2特殊的二叉树 2.3二叉树的性质 2.4二叉树的存储结构 2.4.1顺序结构 2.4.2链式结构 3.堆 3.1堆的概念 3.2堆的分类 3.3堆的实现 3.3.1初始化 3.3.2堆的构建 3.3.3堆的销毁 3.3.4堆的插入 3.3.5…

龙虎榜——20250424

指数依然是震荡走势&#xff0c;接下来两天调整的概率较大 2025年4月24日龙虎榜行业方向分析 一、核心主线方向 化工&#xff08;新能源材料产能集中&#xff09; • 代表标的&#xff1a;红宝丽&#xff08;环氧丙烷/锂电材料&#xff09;、中欣氟材&#xff08;氟化工&…

CentOS 7 安装教程

准备&#xff1a; 软件&#xff1a;VMware Workstation 镜像文件&#xff1a;CentOS-7-x86_64-bin-DVD1.iso &#xff08;附&#xff1a;教程较为详细&#xff0c;注释较多&#xff0c;故将操作的选项进行了加粗字体显示。&#xff09; 1、文件–新建虚拟机–自定义 2、硬盘…

Python+AI提示词出租车出行轨迹预测:梯度提升GBR、KNN、LR回归、随机森林融合及贝叶斯概率异常检测研究

原文链接&#xff1a;tecdat.cn/?p41693 在当今数字化浪潮席卷全球的时代&#xff0c;城市交通领域的海量数据如同蕴藏着无限价值的宝藏等待挖掘。作为数据科学家&#xff0c;我们肩负着从复杂数据中提取关键信息、构建有效模型以助力决策的使命&#xff08;点击文末“阅读原文…

直接偏好优化(Direct Preference Optimization,DPO):论文与源码解析

简介 虽然大规模无监督语言模型&#xff08;LMs&#xff09;学习了广泛的世界知识和一些推理技能&#xff0c;但由于它们是基于完全无监督训练&#xff0c;仍很难控制其行为。 微调无监督LM使其对齐偏好&#xff0c;尽管大规模无监督的语言模型&#xff08;LMs&#xff09;能…

UE5.2+VarjoXR3,Lumen、GI、Nanite无效的两种解决方案

一、问题描述 最近在做一个基于VarjoXR3的VR项目开发&#xff0c;UE版本使用的是5.2&#xff0c;效果采用Lumen。首先在PC版本中调整了一个效果&#xff0c;但是当切换到VR运行后&#xff0c;就发现Lumen效果就丢失了。但是测试的其他的头显就没有问题&#xff0c;比如Quest。…

PH热榜 | 2025-04-24

1. Peek 标语&#xff1a;AI个人财务教练&#xff0c;帮你做出明智的财务决策。 介绍&#xff1a;Peek的人工智能助手能够主动进行财务检查&#xff0c;分析你的消费模式&#xff0c;并以一种细腻而积极的方式帮助你改善习惯。完全没有评判&#xff0c;也没有负罪感。就像为你…

相机雷达外参标定算法调研

0. 简介 相机与激光雷达的外参标定是自动驾驶、机器人等领域的基础工作。精准的标定不仅有助于提高数据融合的效果&#xff0c;还能提升算法的整体性能。随着技术的发展&#xff0c;许多研究者和公司致力于开发高效的标定工具和算法&#xff0c;本文将对无目标标定和有目标标定…

网络原理 - 7(TCP - 4)

目录 6. 拥塞控制 7. 延时应答 8. 捎带应答 9. 面向字节流 10. 异常情况 总结&#xff1a; 6. 拥塞控制 虽然 TCP 有了滑动窗口这个大杀器&#xff0c;就能够高效可靠的发送大量的数据&#xff0c;但是如果在刚开始阶段就发送大量的数据&#xff0c;仍然可能引起大量的…

idea连接远程服务器kafka

一、idea插件安装 首先idea插件市场搜索“kafka”进行插件安装 二、kafka链接配置 1、检查服务器kafka配置 配置链接前需要保证远程服务器的kafka配置里边有配置好服务器IP&#xff0c;以及开放好kafka端口9092&#xff08;如果有修改 过端口的开放对应端口就好&#xff09; …

Linux操作系统--基础I/O(上)

目录 1.回顾C文件接口 stdin、stdout、stderr 2.系统文件I/O 3.接口介绍 4.open函数返回值 5.文件描述符fd 5.1 0&1&2 1.回顾C文件接口 hello.c写文件 #include<stdio.h> #include<string.h>int main() {FILE *fp fopen("myfile","…

weibo_har鸿蒙微博分享,单例二次封装,鸿蒙微博,微博登录

weibo_har鸿蒙微博分享&#xff0c;单例二次封装&#xff0c;鸿蒙微博 HarmonyOS 5.0.3 Beta2 SDK&#xff0c;原样包含OpenHarmony SDK Ohos_sdk_public 5.0.3.131 (API Version 15 Beta2) &#x1f3c6;简介 zyl/weibo_har是微博封装使用&#xff0c;支持原生core使用 &a…

【MySQL数据库入门到精通-06 DCL操作】

一、DCL DCL英文全称是Data Control Language(数据控制语言)&#xff0c;用来管理数据库用户、控制数据库的访 问权限。 二、管理用户 1.查询与创建用户 代码如下&#xff08;示例&#xff09;&#xff1a; -- DCL 管理用户 -- 1.查询用户 use mysql; select *from user;-…

无感字符编码原址转换术——系统内存(Mermaid文本图表版/DeepSeek)

安全便捷无依赖&#xff0c;不学就会无感觉。 笔记模板由python脚本于2025-04-24 20:00:05创建&#xff0c;本篇笔记适合正在研究字符串编码制式的coder翻阅。 学习的细节是欢悦的历程 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述。 P…

第七部分:向量数据库和索引策略

什么是矢量数据库&#xff1f; 简单来说&#xff0c;向量数据库是一种专门化的数据库&#xff0c;旨在优化存储和检索以高维向量形式表示的文本。 为什么这些数据库对RAG至关重要&#xff1f;因为向量表示能够在大规模文档库中进行高效的基于相似性的搜索&#xff0c;根据用户…

查看MAC 地址以及简单了解

MAC地址 简介 MAC 地址&#xff08;Media Access Control Address&#xff09;&#xff0c;直译为媒体访问控制地址&#xff0c;又称局域网地址&#xff08;LAN Address&#xff09;、MAC 地址、以太网地址&#xff08;Ethernet Address&#xff09;、硬件地址&#xff08;Ha…

《100天精通Python——基础篇 2025 第2天:Python解释器安装与基础语法入门》

目录 一、Windows安装Python1.1 下载并安装 Python1.2 测试安装是否成功 二、Linux系统安装Python(新手可以跳过)2.1 基于RockyLinux系统安装Python(编译安装)2.2 基于Ubuntu系统安装Python(编译安装)2.3 macOS 安装python解释器 三、如何运行Python程序&#xff1f;3.1 Python…

MyBatis 和 MyBatis-Plus 在 Spring Boot 中的配置、功能对比及 SQL 日志输出的详细说明,重点对比日志输出的配置差异

以下是 MyBatis 和 MyBatis-Plus 在 Spring Boot 中的配置、功能对比及 SQL 日志输出的详细说明&#xff0c;重点对比日志输出的配置差异&#xff1a; 1. MyBatis 和 MyBatis-Plus 核心对比 特性MyBatisMyBatis-Plus定位基础持久层框架MyBatis 的增强版&#xff0c;提供代码生…