如何提高ACTION_SIM_STATE_CHANGED的接收速度?

在Android中,BroadcastReceiver分動態註冊和靜態註冊. 靜態註冊的一個優點就是:當你的BroadcastReceiver能夠接受系統中java

某個broadcast時,系統會自動啓動你的程序,從而讓BroadcastReceiver完成相關處理; 而動態註冊則只有在程序運行時且沒有android

unregisterReceiver才能接收到broadcast.app

 此時,假設咱們要在系統開機後,要對SIM卡聯繫人進行讀取操做,那麼咱們應該如何註冊本身的BroadcastReceiver呢?ide

方案一:函數

經過靜態註冊receiver來處理ACTION_SIM_STATE_CHANGED的broadcast,當icc state爲LOADED時,讀取SIM卡聯繫人.post

這是一種比較常規的作法. 可是這個方案有一個比較嚴重的問題,那就是接收到broadcast的時機太晚了。結果就是,可能開機幾ui

分鐘過去了,SIM卡聯繫人卻還沒加載出來。this

經過查看IccCardProxy中broadcastIccStateChangedIntent()函數的代碼,咱們發現,它發送的就是一個sticky broadcastspa

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

按照常理來推斷,一個sticky的broadcast不該該須要耗時這麼久的。rest

那問題究竟出在什麼地方了呢?

在調查這個問題以前,咱們先簡單看一下,靜態註冊的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,就看本身有沒有這個須要了.

寫的比較倉促,有紕漏之處,敬請批評指正!

相關文章
相關標籤/搜索