如何提升ACTION_SIM_STATE_CHANGED的接收速度?

在Android中,BroadcastReceiver分动态注册和静态注册. 静态注册的一个优势就是:当你的BroadcastReceiver可以接受系统中

某个broadcast时,系统会自动启动你的程序,从而让BroadcastReceiver完成相关处理; 而动态注册则只有在程序运行时且没有

unregisterReceiver才能接收到broadcast.

此时,假设我们要在系统开机后,要对SIM卡联系人进行读取操作,那么我们应该如何注册自己的BroadcastReceiver呢?

方案一:

通过静态注册receiver来处理ACTION_SIM_STATE_CHANGED的broadcast,当icc state为LOADED时,读取SIM卡联系人.

这是一种比较常规的做法. 但是这个方案有一个比较严重的问题,那就是接收到broadcast的时机太晚了。结果就是,可能开机几

分钟过去了,SIM卡联系人却还没加载出来。

通过查看IccCardProxy中broadcastIccStateChangedIntent()函数的代码,我们发现,它发送的就是一个sticky broadcast

ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE, UserHandle.USER_ALL)

按照常理来推断,一个sticky的broadcast不应该需要耗时这么久的。

那问题究竟出在什么地方了呢?

在调查这个问题之前,我们先简单看一下,静态注册的receiver和动态注册的receiver是如何被管理的呢?

静态注册receiver:

简单讲,系统启动时,创建PackageManagerService对象,简称PMS,然后通过scanDirLI函数对各个路径进行扫描,保存receiver等等

main[] // PackageManagerService.java
    PackageManagerService()
        scanDirLI()
            scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user)
                parsePackage() // PackageParser.java
                // scanPackageLI(PackageParser...)调用scanPackageDirtyLI来进一步处理parsePackage()生成的PackageParser.Package对象pkg
                // scanPackageDirtyLI将pkg中的receiver添加到PMS的mReceivers中(具体代码:mReceivers.addActivity(a, "receiver")),
                // 并将pkg添加到PMS的mPackages中(具体代码:mPackages.put(pkg.applicationInfo.packageName, pkg))
                scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags, long currentTime, UserHandle user)  // PackageManagerService.java
                    parseBaseApk(File apkFile, AssetManager assets, int flags) // PackageParser.java
                        parseBaseApk(Resources res, XmlResourceParser parser, int flags, String[] outError)
                            parseBaseApplication() // // 将生成的receiver放到receivers中

} else if (tagName.equals("receiver")) {
    Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
    if (a == null) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
        return false;
    }

    owner.receivers.add(a);

} else if (tagName.equals("service")) {

通过上面的简单分析,我们可以发现所有静态注册的receiver是通过PMS进行管理的.

动态注册的receiver:

相比静态注册,动态注册的流程要简单许多.

registerReceiver(BroadcastReceiver receiver, IntentFilter filter) // ContextImpl.java
    registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)
        registerReceiverInternal()
            registerReceiver() // AMS

在AMS的registerReceiver函数中,receiver和broadcast filter相关信息被放到了mRegisteredReceivers和mReceiverResolver中.

了解了静态注册和动态注册receiver在PMS和AMS中的大致流程后,再来看下IccCardProxy发送sticky broadcast时AMS的大概流程.

broadcastStickyIntent(intent, READ_PHONE_STATE, UserHandle.USER_ALL) // ActivityManagerNative.java
    broadcastIntent() // ActivityManagerService.java 简称AMS
        broadcastIntentLocked()

下面贴一下broadcastIntentLocked中比较重要的处理

private final int broadcastIntentLocked(ProcessRecord callerApp,
        String callerPackage, Intent intent, String resolvedType,
        IIntentReceiver resultTo, int resultCode, String resultData,
        Bundle map, String requiredPermission, int appOp,
        boolean ordered, boolean sticky, int callingPid, int callingUid,
        int userId) {
    intent = new Intent(intent);

    // By default broadcasts do not go to stopped apps.
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

    ......

    userId = handleIncomingUser(callingPid, callingUid, userId,
            true, ALLOW_NON_FULL, "broadcast", callerPackage);
    ......
    /*
     * Prevent non-system code (defined here to be non-persistent
     * processes) from sending protected broadcasts.
     */
    int callingAppId = UserHandle.getAppId(callingUid);

    ......

    // Figure out who all will receive this broadcast.
    List receivers = null; // PMS中的结果
    List<BroadcastFilter> registeredReceivers = null; // AMS中的结果
    // Need to resolve the intent to interested receivers...
    if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
             == 0) {
        // 向PMS查询符合条件的receiver
        receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
    }
    if (intent.getComponent() == null) {
        if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
            ......
        } else {            // 向AMS查询符合条件的receiver
            registeredReceivers = mReceiverResolver.queryIntent(intent,
                    resolvedType, false, userId);
        }
    }

    final boolean replacePending =
            (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

    ......

    // 注意,这里用的是从AMS中查询出来的符合条件的receiver
    int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
    if (!ordered && NR > 0) {
        // If we are not serializing this broadcast, then send the
        // registered receivers separately so they don‘t wait for the
        // components to be launched.
        final BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
                appOp, registeredReceivers, resultTo, resultCode, resultData, map,
                ordered, sticky, false, userId);
        if (DEBUG_BROADCAST) Slog.v(
                TAG, "Enqueueing parallel broadcast " + r);
        final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
        if (!replaced) {
            queue.enqueueParallelBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
        registeredReceivers = null;
        NR = 0;
    }

    ......

    if ((receivers != null && receivers.size() > 0)
            || resultTo != null) {
        BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                callerPackage, callingPid, callingUid, resolvedType,
                requiredPermission, appOp, receivers, resultTo, resultCode,
                resultData, map, ordered, sticky, false, userId);
        if (DEBUG_BROADCAST) Slog.v(
                TAG, "Enqueueing ordered broadcast " + r
                + ": prev had " + queue.mOrderedBroadcasts.size());
        if (DEBUG_BROADCAST) {
            int seq = r.intent.getIntExtra("seq", -1);
            Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
        }
        boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);

        if (!replaced) {
            // 注意,被放到ordered broadcast中了!
            queue.enqueueOrderedBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
    }

    return ActivityManager.BROADCAST_SUCCESS;
}

collectReceiverComponents()和mReceiverResolver.queryIntent()是broadcastIntentLocked()中两个重要的函数调用.分别初始化了broadcastIntentLocked()中的receivers和registeredReceivers变量.

collectReceiverComponents()基本是通过调用AppGlobals.getPackageManager().queryIntentReceivers()来查询符合条件的receiver,

也就是PMS的queryIntentReceivers()函数进行查询,进而通过PMS中mReceivers变量的queryIntent来执行查询操作.

可见,collectReceiverComponents()最终查询到的是静态注册的符合条件的receiver.

再来简单看下mReceiverResolver.queryIntent()方法的查询结果. 我们在执行AMS的registerReceiver()时,将broadcast以及broadcast filter相关的

信息放到了mReceiverResolver中,因此,这里查询到的是所有符合条件的动态注册的receiver.

最终,符合条件的静态注册的receiver被保存在broadcastIntentLocked函数中receivers变量中;符合条件的动态注册的receiver被保存在registeredReceivers变量中。

在接下来enqueueParallelBroadcastLocked时,却只使用了registeredReceivers,即动态注册的receiver. 而静态注册的receiver却被enqueueOrderedBroadcastLocked

放到了ordered broadcast队列中!

下面再来简单看下BroadcastQueue对broadcast的处理流程:

scheduleBroadcastsLocked()
    handleMessage() // BroadcastQueue$BroadcastHandler
        processNextBroadcast()
            deliverToRegisteredReceiverLocked() // non-ordered
                performReceiveLocked()
                    scheduleRegisteredReceiver() // ActivityThread$ApplicationThread
                        performReceive() // LoadedApk$ReceiverDispatcher
                        mActivityThread.post(args)
                            run() // LoadedApk$Args
                                receiver.onReceive(mContext, intent) // 执行BroadcastReceiver的onReceive方法
            processCurBroadcastLocked() // ordered
                scheduleReceiver() // ActivityThread$ApplicationThread
                    handleMessage() // ActivityThread$H
                        handleReceiver() // ActivityThread
                            receiver.onReceive(context.getReceiverRestrictedContext(), data.intent) // 执行BroadcastReceiver的onReceive方法

由于ordered broadcast是一条一条来处理,也就不难发现为什么我们静态注册的接收ACTION_SIM_STATE_CHANGED的broadcast receiver很晚才能被启动了.

既然静态注册的receiver只能接受ordered broadcast后,如果想提升接收broadcast的速度,那我们只能使用动态注册receiver了。也就引出了我们的第二个方案.

方案二:

通过自定义Application并在自定义Application中动态注册receiver来处理ACTION_SIM_STATE_CHANGED的broadcast,在AndroidManifest.xml中声明时为自定义

的application添加android:persistent="true"属性. 这样就可以大幅提升接收ACTION_SIM_STATE_CHANGED的速度,但这是为什么呢?

在AMS的systemReady()中将通过PMS的getPersistentApplications()获得所有persistent属性为true的程序,并将他们启动起来. 即在进入launcher之前就被启动了.

而我们在自定义的Application的onCreate()中注册receiver, 来接收ACTION_SIM_STATE_CHANGED的broadcast,我们的receiver将被AMS管理,进而我们就可以更

快的接收到broadcast了(参考上文对AMS中broadcastIntentLocked()的分析). 但是这个方案也不是完美的,因为它声明了persistent=“true”,会占用内存以及增加开机时间.

自定义Application的代码:

public class MyApp extends Application {

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
        intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
        registerReceiver(mReceiver, intentFilter);
    }

}

在AndroidManifest中的声明:

<application
    android:name="MyApp"
    android:label="@string/app_name"
    android:persistent="true">
...
</application>

不止ACTION_SIM_STATE_CHANGED,其它的broadcast receiver也可以采用方案二的做法来更快速的接收broadcast,就看自己有没有这个需要了.

写的比较仓促,有纰漏之处,敬请批评指正!

时间: 2024-07-30 10:11:51

如何提升ACTION_SIM_STATE_CHANGED的接收速度?的相关文章

如何通过预加载器提升网页加载速度

预加载器(Pre-loader)可以说是提高浏览器性能最重要的举措.Mozilla 官方发布数据,通过预加载器技术网页的加载性能提升了19%,Chrome测试了 Alexa 排名前2000名网站,性能有20%的提升. 它并不是一门新技术,有人认为只有 Chrome 才具备这个功能.也有人认为它是有史以来提升浏览器性能最有效的方法.如果你第一次接触预加载器,也许心中已经有了无数个问号.什么是预加载器?它是如何提升浏览器性能的? 首先需要了解浏览器是如何加载网页的 一个网页的加载依赖于脚本文件.CS

webpack 提升90%的构建速度 HardSourceWebpackPlugin

HardSourceWebpackPlugin 插件 不能提升第一次构建的速度,但对于第二次构建能提升99%的构建速度 第一次构建: 第二次: 提升了..,算不出来,反正就是很多啦~~~ npm install --save hard-source-webpack-plugin // webpack.config.js var HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); new HardSourceWebpac

如何提升页面加载速度,并简述原理

页面的加载过程主要分为下载.解析.渲染三个步骤,下面从这三个方面阐述提升加载速度的方法: 1.加快文件下载速度,减小资源文件下载对页面解析的阻塞.页面加载过程首先会下载 HTML 文件,然后自上而下开始解析,解析过程中如果遇到外部资源则会开始下载,直至下载完成才会继续解析.所以,加快文件下载速度方式是有效的提升页面加载速度的方法.具体可以是 1)通过设置 CDN.HTTP 缓存等方式,减少 HTTP 传输时间: 2)对文件进行压缩,减小文件体积: 3)对 script.CSS 文件引用标签设置异

优化JavaScripe 提升首页加载速度的几种方案解析

优化目的: 1. 减少load量. 2. 优化js,加快页面加载速度. 网站中最影响网站打开速度的是什么?我会告诉是网站中的javascript,简称JS.模板中引用的JS文件越多,打开速度越慢,细读完这篇文章,够你优化大半天的了,关于JS优化方法大都脱离不了这三种方法. 方法一:把不重要的JS放在页面最底部 这是最简单,也是效果最好的优化方法,把不重要的JS通通放到页面的最下面body的上面,实现异步加载,也就是等网页都加载完了,最后在加载这些不重要的JS,这样就不影响网页速度了.如公共的co

SSE再学习:灵活运用SIMD指令16倍提升Sobel边缘检测的速度(4000*3000的24位图像时间由480ms降低到30ms)。

这半年多时间,基本都在折腾一些基本的优化,有很多都是十几年前的技术了,从随大流的角度来考虑,研究这些东西在很多人看来是浪费时间了,即不能赚钱,也对工作能力提升无啥帮助.可我觉得人类所谓的幸福,可以分为物质档次的享受,还有更为复杂的精神上的富有,哪怕这种富有只是存在于短暂的自我满足中也是值得的. 闲话少说, SIMD指令集,这个古老的东西,从第一代开始算起,也快有近20年的历史了,从最开始的MMX技术,到SSE,以及后来的SSE2.SSE3.SSE4.AVX以及11年以后的AVX2,逐渐的成熟和丰

提升网站页面打开速度的12个建议

我们知道用户都喜欢浏览速度快的网站,不喜欢花费太多的时间等待网页的打开,等待的时间过长,会让用户失去耐心,甚至烦躁时会直接关闭网页,这样就会失去一些潜在的客户了. 其次,从SEO的角度来说,关键字的排名与网页的打开速度也有关系,Google的Web搜索团队曾在官方博客上宣布,将把网站的速度作为PR(PageRank)算法的一个因子,在所有因素都相等的情况下,速度快的网站将排在速度慢的网站前面.同时指出很多网站都没有利用最佳的页面优化技术,页面加载速度都存在很大缺陷.那么,我们该如何补救并提高网站

如何提升代码编译的速度 iOS

前阵子有遇到代码编译速度慢的问题,特别是在swift和object-c混编的过程中问题很突显. 网上找到一篇蛮好的文章里面又一些解决方法 推荐一下 http://www.open-open.com/lib/view/open1475993635756.html 当然除了这篇推荐外,自己还做了一些理解. 代码编译速度慢.首先我们应该想到的是 编译速度慢是跟你的编译器有关系的. 一下列出几个方向供参考 1.swift 是类型安全的,所以我们在经常定义变量的时候由于使用方便会直接 var x = a这

提升页面加载速度的优化方法

1.CSS精灵图片技术 将一个页面涉及的所有零星图片都包含到一张大的图片中,然后利用CSS技术展现出来,可以减少http请求,减少整个网页的图片大小,提高页面性能. 2.合并js文件和css文件 减少GET请求次数,提高加载速度. 3.延迟可见区域外的内容 4.压缩文本和图片 如gzip压缩技术,可以减少页面加载时间 5.确保功能图片优先加载 6.图片格式优化 如png格式 7.使用高级JPEG格式图片 8.精简代码 9.使用AJAX 在不加载整个页面的情况下对网页的某部分进行更新,即实现异步更

oracle海量数据中提升创建索引的速度

基本信息情况: 数据库版本:Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production 操作系统版本:CentOS release 5.6 加快创建索引速度主要从一下角度考虑: 使用nologging 参数 使用parallel 并行参数 在session级别使用manual pga,手动调整sort_area_size 修改其他参数 注意:我们这里不手动调整hash_area_size,hash_ar