Android 7.1 多次重啓後BOOT_COMPLETED廣播接收變慢

平臺

RK3368 + android 7.1

問題

在做重啓測試的過程中, 重啓程序的啓動速度越來越慢, 如以下LOG:

//第一天
 [20.354000 s,20.354000 s] 01-18 16:51:21.606 D/ActivityManager(  469): Sending BOOT_COMPLETE user #0
 [20.793000 s,0.439000  s] 01-18 16:51:22.045 I/AlarmClock(  961): AlarmInitReceiver android.intent.action.LOCKED_BOOT_COMPLETED
 [23.543000 s,2.750000  s] 01-18 16:51:24.795 D/HdmiReceiver(  635): hdmi receiver action=android.intent.action.BOOT_COMPLETED
//第二天
 [20.127000 s,20.127000 s] 01-19 18:57:57.513   438   499 D ActivityManager: Sending BOOT_COMPLETE user #0
 [20.354000 s,0.227000  s] 01-19 18:57:57.740   960   960 I AlarmClock: AlarmInitReceiver android.intent.action.LOCKED_BOOT_COMPLETED
 [36.911000 s,16.557000 s] 01-19 18:58:14.297   635   635 D HdmiReceiver: hdmi receiver action=android.intent.action.BOOT_COMPLETED
//第三天
 [23.242000 s,23.242000 s] 01-20 14:10:07.611   436   497 D ActivityManager: Sending BOOT_COMPLETE user #0
 [23.507000 s,0.265000  s] 01-20 14:10:07.876   961   961 I AlarmClock: AlarmInitReceiver android.intent.action.LOCKED_BOOT_COMPLETED
 [47.177000 s,23.670000 s] 01-20 14:10:31.546   629   629 D HdmiReceiver: hdmi receiver action=android.intent.action.BOOT_COMPLETED

每天 10’s + ??

解決

QSB = QuickSearchBox

  • 方案1: 優化Launcher中添加QSB的代碼
diff --git a/packages/apps/Launcher3/src/com/android/launcher3/QsbContainerView.java b/packages/apps/Launcher3/src/com/android/launcher3/QsbContainerView.java
index ffed8fc..811b42a 100755
--- a/packages/apps/Launcher3/src/com/android/launcher3/QsbContainerView.java
+++ b/packages/apps/Launcher3/src/com/android/launcher3/QsbContainerView.java
@@ -103,7 +103,6 @@ public class QsbContainerView extends FrameLayout {
             mWrapper.addView(createQsb(inflater, mWrapper));
             return mWrapper;
         }
-
         private View createQsb(LayoutInflater inflater, ViewGroup container) {
             Launcher launcher = Launcher.getLauncher(getActivity());
             mWidgetInfo = getSearchWidgetProvider(launcher);
@@ -128,7 +127,6 @@ public class QsbContainerView extends FrameLayout {
             AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
             boolean isWidgetBound = (widgetInfo != null) &&
                     widgetInfo.provider.equals(mWidgetInfo.provider);
-
             if (!isWidgetBound) {
                 // widgetId is already bound and its not the correct provider.
                 // Delete the widget id.
@@ -142,6 +140,9 @@ public class QsbContainerView extends FrameLayout {
                 if (!isWidgetBound) {
                     widgetHost.deleteAppWidgetId(widgetId);
                     widgetId = -1;
+                }else{//添加成功後, 保持ID, 避免重複添加
+                    sSavedWidgetId = widgetId;
+                    prefs.edit().putInt(QSB_WIDGET_ID, widgetId).commit();
                 }
             }
  • 方案2: 更換其它Launcher
  • 方案3: 刪除QSB
  • 方案4: 優化系統Widget服務(請自行開發)

分析

首先了解下BOOT_COMPLETED, 絕大部分的應用自啓動所依賴的廣播:

  • 發出的過程如下:
    WindowManagerService.performEnableScreen
    ActivityManagerService.bootAnimationComplete
    ActivityManagerService.finishBooting
    UserController.sendBootCompletedLocked
    UserController.finishUserBoot
    UserController.maybeUnlockUser
    UserController.unlockUserCleared
    UserController.finishUserUnlocking

  • 部分代碼

  • frameworks/base/services/core/java/com/android/server/am/UserController.java

    boolean unlockUser(final int userId, byte[] token, byte[] secret, IProgressListener listener) {
        //...
        try {
            return unlockUserCleared(userId, token, secret, listener);
        } finally {
            Binder.restoreCallingIdentity(binderToken);
        }
    }

    /**
     * Attempt to unlock user without a credential token. This typically
     * succeeds when the device doesn't have credential-encrypted storage, or
     * when the the credential-encrypted storage isn't tied to a user-provided
     * PIN or pattern.
     */
    boolean maybeUnlockUser(final int userId) {
        // Try unlocking storage using empty token
        return unlockUserCleared(userId, null, null, null);
    }

    boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
            IProgressListener listener) {
        //...
        finishUserUnlocking(uss);
	}
    private void finishUserUnlocking(final UserState uss) {
        //...
        if (proceedWithUnlock) {
            uss.mUnlockProgress.start();

            // Prepare app storage before we go any further
            uss.mUnlockProgress.setProgress(5,
                    mService.mContext.getString(R.string.android_start_title));
            mUserManager.onBeforeUnlockUser(userId);
            uss.mUnlockProgress.setProgress(20);

            // Dispatch unlocked to system services; when fully dispatched,
            // that calls through to the next "unlocked" phase
            mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
                    .sendToTarget();
        }
    }
  • frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    final class MainHandler extends Handler {
			//...
            case SYSTEM_USER_UNLOCK_MSG: {
                final int userId = msg.arg1;
                mSystemServiceManager.unlockUser(userId);
                synchronized (ActivityManagerService.this) {
                    mRecentTasks.loadUserRecentsLocked(userId);
                }
                if (userId == UserHandle.USER_SYSTEM) {
                    startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
                }
                installEncryptionUnawareProviders(userId);
                mUserController.finishUserUnlocked((UserState) msg.obj);
                break;
            }
			//...
  • frameworks/base/services/core/java/com/android/server/am/UserController.java
void finishUserUnlocked(final UserState uss) {
        //...
                    new PreBootBroadcaster(mService, userId, null, quiet) {
                        @Override
                        public void onFinished() {
                            finishUserUnlockedCompleted(uss);
                        }
                    }.sendNext();
                } else {
                    finishUserUnlockedCompleted(uss);
                }
        //...
    }
    private void finishUserUnlockedCompleted(UserState uss) {
        //...
		 Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
			//...
            final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
            bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
            bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
            mService.broadcastIntentLocked(null, null, bootIntent, null, null, 0, null, null,
                    new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
                    AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
        //...
    }
  • frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
        intent = new Intent(intent);
		//...
                queue.enqueueOrderedBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
		//...
	}
  • frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
    public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
        mOrderedBroadcasts.add(r);
        r.enqueueClockTime = System.currentTimeMillis();
    }

    public void scheduleBroadcastsLocked() {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);

        if (mBroadcastsScheduled) {
            return;
        }
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

剩下的, 就看BroadcastQueue這個隊列的執行情況了.

是誰拖慢了時間?

UserController中調用ActivityManagerService發出廣播, 的時間其實並沒有太大差距, 真正的差距, 是從加入隊列後到接收的時間長.

//第三天
 [23.242000 s,23.242000 s] 01-20 14:10:07.611   436   497 D ActivityManager: Sending BOOT_COMPLETE user #0
 [23.507000 s,0.265000  s] 01-20 14:10:07.876   961   961 I AlarmClock: AlarmInitReceiver android.intent.action.LOCKED_BOOT_COMPLETED
 [47.177000 s,23.670000 s] 01-20 14:10:31.546   629   629 D HdmiReceiver: hdmi receiver action=android.intent.action.BOOT_COMPLETED

如上, 從 Sending 到 Receiver接收到, 用了24’s

從當前的LOG上, 沒有更多的有效信息, 瞭解了廣播的發送流程, 嘗試打開調試開關

  • frameworks/base/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -49,7 +49,7 @@ class ActivityManagerDebugConfig {
     static final boolean DEBUG_ANR = false;
     static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_BACKUP = DEBUG_ALL || false;
-    static final boolean DEBUG_BROADCAST = DEBUG_ALL || false;
+    static final boolean DEBUG_BROADCAST = DEBUG_ALL || true;
     static final boolean DEBUG_BROADCAST_BACKGROUND = DEBUG_BROADCAST || false;
     static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
     static final boolean DEBUG_CLEANUP = DEBUG_ALL || false;

得到了一個很有趣的LOG信息:

01-18 17:34:11.199 D/ActivityManager(  444): Sending BOOT_COMPLETE user #0
01-18 17:34:11.199 V/ActivityManager(  444): Broadcast: Intent { act=android.intent.action.BOOT_COMPLETED flg=0x9000010 (has extras) } ordered=true userid=0
01-18 17:34:11.201 V/ActivityManager(  444): Enqueing broadcast: android.intent.action.BOOT_COMPLETED replacePending=false
01-18 17:34:11.201 I/ActivityManager(  444): Broadcast intent Intent { act=android.intent.action.BOOT_COMPLETED flg=0x9000010 (has extras) } on background queue
01-18 17:34:11.201 V/ActivityManager(  444): Enqueueing ordered broadcast BroadcastRecord{1c9d3ec u0 android.intent.action.BOOT_COMPLETED}: prev had 4
01-18 17:34:11.201 I/ActivityManager(  444): Enqueueing broadcast android.intent.action.BOOT_COMPLETED

01-18 17:34:38.115 V/BroadcastQueue(  444): Processing ordered broadcast [background] BroadcastRecord{1c9d3ec u0 android.intent.action.BOOT_COMPLETED}
01-18 17:34:38.115 V/BroadcastQueue(  444): Submitting BROADCAST_TIMEOUT_MSG [background] for BroadcastRecord{1c9d3ec u0 android.intent.action.BOOT_COMPLETED} at 108628
01-18 17:34:38.116 V/BroadcastQueue(  444): Delivering ordered [background] to registered BroadcastFilter{84ddfd0 u-1 ReceiverList{cf4e393 444 system/1000/u-1 local:5386082}}: BroadcastRecord{1c9d3ec u0 android.intent.action.BOOT_COMPLETED}
01-18 17:34:38.116 I/BroadcastQueue(  444): Delivering to BroadcastFilter{84ddfd0 u-1 ReceiverList{cf4e393 444 system/1000/u-1 local:5386082}} : BroadcastRecord{1c9d3ec u0 android.intent.action.BOOT_COMPLETED}

在BOOT_COMPLETED前有一個廣播:

01-18 17:34:12.715 V/ActivityManager(  444): Broadcast: Intent { act=android.appwidget.action.APPWIDGET_UPDATE flg=0x10 cmp=com.android.quicksearchbox/.SearchWidgetProvider (has extras) } ordered=false userid=0
01-18 17:34:12.715 V/ActivityManager(  444): Enqueing broadcast: android.appwidget.action.APPWIDGET_UPDATE replacePending=false
01-18 17:34:12.715 I/ActivityManager(  444): Broadcast intent Intent { act=android.appwidget.action.APPWIDGET_UPDATE flg=0x10 cmp=com.android.quicksearchbox/.SearchWidgetProvider (has extras) } on background queue
01-18 17:34:12.715 V/ActivityManager(  444): Enqueueing ordered broadcast BroadcastRecord{afaca5f u0 android.appwidget.action.APPWIDGET_UPDATE}: prev had 5
01-18 17:34:12.715 I/ActivityManager(  444): Enqueueing broadcast android.appwidget.action.APPWIDGET_UPDATE

01-18 17:34:38.115 V/BroadcastQueue(  444): Finished with ordered broadcast BroadcastRecord{a4ae1fd u0 android.appwidget.action.APPWIDGET_UPDATE}
  • 從LOG中, com.android.quicksearchbox出現了, 直觀讓我把QuickSearchBox刪除了再試試, 問題真的解決了, 重啓一天也沒出現類似問題, 所以肯定跟QuickSearchBox有關.
  • frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java(輸出更多的LOG)
    final void processNextBroadcast(boolean fromMsg) {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast start");
        synchronized(mService) {
            BroadcastRecord r;
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
                    + mQueueName + "]: "
                    + mParallelBroadcasts.size() + " broadcasts, "
                    + mOrderedBroadcasts.size() + " ordered broadcasts");

            mService.updateCpuStats();

            if (fromMsg) {
                mBroadcastsScheduled = false;
            }

            // First, deliver any non-serialized broadcasts right away.
            while (mParallelBroadcasts.size() > 0) {
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast 0");
                r = mParallelBroadcasts.remove(0);
                r.dispatchTime = SystemClock.uptimeMillis();
                r.dispatchClockTime = System.currentTimeMillis();
                final int N = r.receivers.size();
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
                        + mQueueName + "] " + r);
                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                    if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                            "Delivering non-ordered on [" + mQueueName + "] to registered "
                            + target + ": " + r);
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }
                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                        + mQueueName + "] " + r);
            }

            // Now take care of the next serialized one...

            // If we are waiting for a process to come up to handle the next
            // broadcast, then do nothing at this point.  Just in case, we
            // check that the process we're waiting for still exists.
            if (mPendingBroadcast != null) {
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                        "processNextBroadcast [" + mQueueName + "]: waiting for "
                        + mPendingBroadcast.curApp);

                boolean isDead;
                synchronized (mService.mPidsSelfLocked) {
                    ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
                    isDead = proc == null || proc.crashing;
                }
                if (!isDead) {
                    // It's still alive, so keep waiting
                    return;
                } else {
                    Slog.w(TAG, "pending app  ["
                            + mQueueName + "]" + mPendingBroadcast.curApp
                            + " died before responding to broadcast");
                    mPendingBroadcast.state = BroadcastRecord.IDLE;
                    mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
                    mPendingBroadcast = null;
                }
            }

            boolean looped = false;
            
            do {
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast 1");
                if (mOrderedBroadcasts.size() == 0) {
                    // No more broadcasts pending, so all done!
                    mService.scheduleAppGcsLocked();
                    if (looped) {
                        // If we had finished the last ordered broadcast, then
                        // make sure all processes have correct oom and sched
                        // adjustments.
                        mService.updateOomAdjLocked();
                    }
                    return;
                }
                r = mOrderedBroadcasts.get(0);
                boolean forceReceive = false;

                // Ensure that even if something goes awry with the timeout
                // detection, we catch "hung" broadcasts here, discard them,
                // and continue to make progress.
                //
                // This is only done if the system is ready so that PRE_BOOT_COMPLETED
                // receivers don't get executed with timeouts. They're intended for
                // one time heavy lifting after system upgrades and can take
                // significant amounts of time.
                int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
                if (mService.mProcessesReady && r.dispatchTime > 0) {
                    long now = SystemClock.uptimeMillis();
                    if ((numReceivers > 0) &&
                            (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                        Slog.w(TAG, "Hung broadcast ["
                                + mQueueName + "] discarded after timeout failure:"
                                + " now=" + now
                                + " dispatchTime=" + r.dispatchTime
                                + " startTime=" + r.receiverTime
                                + " intent=" + r.intent
                                + " numReceivers=" + numReceivers
                                + " nextReceiver=" + r.nextReceiver
                                + " state=" + r.state);
                        broadcastTimeoutLocked(false); // forcibly finish this broadcast
                        forceReceive = true;
                        r.state = BroadcastRecord.IDLE;
                    }
                }

                if (r.state != BroadcastRecord.IDLE) {
                    if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
                            "processNextBroadcast("
                            + mQueueName + ") called when not idle (state="
                            + r.state + ")");
                    return;
                }

                if (r.receivers == null || r.nextReceiver >= numReceivers
                        || r.resultAbort || forceReceive) {
                    // No more receivers for this broadcast!  Send the final
                    // result if requested...
                    if (r.resultTo != null) {
                        try {
                            if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
                                    "Finishing broadcast [" + mQueueName + "] "
                                    + r.intent.getAction() + " app=" + r.callerApp);
                            performReceiveLocked(r.callerApp, r.resultTo,
                                new Intent(r.intent), r.resultCode,
                                r.resultData, r.resultExtras, false, false, r.userId);
                            // Set this to null so that the reference
                            // (local and remote) isn't kept in the mBroadcastHistory.
                            r.resultTo = null;
                        } catch (RemoteException e) {
                            r.resultTo = null;
                            Slog.w(TAG, "Failure ["
                                    + mQueueName + "] sending broadcast result of "
                                    + r.intent, e);

                        }
                    }

                    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
                    cancelBroadcastTimeoutLocked();

                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                            "Finished with ordered broadcast " + r);

                    // ... and on to the next...
                    addBroadcastToHistoryLocked(r);
                    if (r.intent.getComponent() == null && r.intent.getPackage() == null
                            && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
                        // This was an implicit broadcast... let's record it for posterity.
                        mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
                                r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
                    }
                    mOrderedBroadcasts.remove(0);
                    r = null;
                    looped = true;
                    continue;
                }
            } while (r == null);

            // Get the next receiver...
            int recIdx = r.nextReceiver++;

            // Keep track of when this receiver started, and make sure there
            // is a timeout message pending to kill it if need be.
            r.receiverTime = SystemClock.uptimeMillis();
            if (recIdx == 0) {
                r.dispatchTime = r.receiverTime;
                r.dispatchClockTime = System.currentTimeMillis();
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
                        + mQueueName + "] " + r);
            }
            if (! mPendingBroadcastTimeoutMessage) {
                long timeoutTime = r.receiverTime + mTimeoutPeriod;
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                        "Submitting BROADCAST_TIMEOUT_MSG ["
                        + mQueueName + "] for " + r + " at " + timeoutTime);
                setBroadcastTimeoutLocked(timeoutTime);
            }

            final BroadcastOptions brOptions = r.options;
            final Object nextReceiver = r.receivers.get(recIdx);

            if (nextReceiver instanceof BroadcastFilter) {
                // Simple case: this is a registered receiver who gets
                // a direct call.
                BroadcastFilter filter = (BroadcastFilter)nextReceiver;
                if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                        "Delivering ordered ["
                        + mQueueName + "] to registered "
                        + filter + ": " + r);
                deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
                if (r.receiver == null || !r.ordered) {
                    // The receiver has already finished, so schedule to
                    // process the next one.
                    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
                            + mQueueName + "]: ordered="
                            + r.ordered + " receiver=" + r.receiver);
                    r.state = BroadcastRecord.IDLE;
                    scheduleBroadcastsLocked();
                } else {
                    if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
                        scheduleTempWhitelistLocked(filter.owningUid,
                                brOptions.getTemporaryAppWhitelistDuration(), r);
                    }
                }
                return;
            }
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast 2");

            // Hard case: need to instantiate the receiver, possibly
            // starting its application process to host it.

            ResolveInfo info =
                (ResolveInfo)nextReceiver;
            ComponentName component = new ComponentName(
                    info.activityInfo.applicationInfo.packageName,
                    info.activityInfo.name);

            boolean skip = false;
            if (brOptions != null &&
                    (info.activityInfo.applicationInfo.targetSdkVersion
                            < brOptions.getMinManifestReceiverApiLevel() ||
                    info.activityInfo.applicationInfo.targetSdkVersion
                            > brOptions.getMaxManifestReceiverApiLevel())) {
                skip = true;
            }
            int perm = mService.checkComponentPermission(info.activityInfo.permission,
                    r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
                    info.activityInfo.exported);
            if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
                if (!info.activityInfo.exported) {
                    Slog.w(TAG, "Permission Denial: broadcasting "
                            + r.intent.toString()
                            + " from " + r.callerPackage + " (pid=" + r.callingPid
                            + ", uid=" + r.callingUid + ")"
                            + " is not exported from uid " + info.activityInfo.applicationInfo.uid
                            + " due to receiver " + component.flattenToShortString());
                } else {
                    Slog.w(TAG, "Permission Denial: broadcasting "
                            + r.intent.toString()
                            + " from " + r.callerPackage + " (pid=" + r.callingPid
                            + ", uid=" + r.callingUid + ")"
                            + " requires " + info.activityInfo.permission
                            + " due to receiver " + component.flattenToShortString());
                }
                skip = true;
            } else if (!skip && info.activityInfo.permission != null) {
                final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
                if (opCode != AppOpsManager.OP_NONE
                        && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
                                r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
                    Slog.w(TAG, "Appop Denial: broadcasting "
                            + r.intent.toString()
                            + " from " + r.callerPackage + " (pid="
                            + r.callingPid + ", uid=" + r.callingUid + ")"
                            + " requires appop " + AppOpsManager.permissionToOp(
                                    info.activityInfo.permission)
                            + " due to registered receiver "
                            + component.flattenToShortString());
                    skip = true;
                }
            }
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast 3");
            if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
                r.requiredPermissions != null && r.requiredPermissions.length > 0) {
                for (int i = 0; i < r.requiredPermissions.length; i++) {
                    String requiredPermission = r.requiredPermissions[i];
                    try {
                        perm = AppGlobals.getPackageManager().
                                checkPermission(requiredPermission,
                                        info.activityInfo.applicationInfo.packageName,
                                        UserHandle
                                                .getUserId(info.activityInfo.applicationInfo.uid));
                    } catch (RemoteException e) {
                        perm = PackageManager.PERMISSION_DENIED;
                    }
                    if (perm != PackageManager.PERMISSION_GRANTED) {
                        Slog.w(TAG, "Permission Denial: receiving "
                                + r.intent + " to "
                                + component.flattenToShortString()
                                + " requires " + requiredPermission
                                + " due to sender " + r.callerPackage
                                + " (uid " + r.callingUid + ")");
                        skip = true;
                        break;
                    }
                    int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
                    if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
                            && mService.mAppOpsService.noteOperation(appOp,
                            info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
                            != AppOpsManager.MODE_ALLOWED) {
                        Slog.w(TAG, "Appop Denial: receiving "
                                + r.intent + " to "
                                + component.flattenToShortString()
                                + " requires appop " + AppOpsManager.permissionToOp(
                                requiredPermission)
                                + " due to sender " + r.callerPackage
                                + " (uid " + r.callingUid + ")");
                        skip = true;
                        break;
                    }
                }
            }
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast 4");
            if (!skip && r.appOp != AppOpsManager.OP_NONE
                    && mService.mAppOpsService.noteOperation(r.appOp,
                    info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
                    != AppOpsManager.MODE_ALLOWED) {
                Slog.w(TAG, "Appop Denial: receiving "
                        + r.intent + " to "
                        + component.flattenToShortString()
                        + " requires appop " + AppOpsManager.opToName(r.appOp)
                        + " due to sender " + r.callerPackage
                        + " (uid " + r.callingUid + ")");
                skip = true;
            }
            if (!skip) {
                skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
                        r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
            }
            boolean isSingleton = false;
            try {
                isSingleton = mService.isSingleton(info.activityInfo.processName,
                        info.activityInfo.applicationInfo,
                        info.activityInfo.name, info.activityInfo.flags);
            } catch (SecurityException e) {
                Slog.w(TAG, e.getMessage());
                skip = true;
            }
            if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
                if (ActivityManager.checkUidPermission(
                        android.Manifest.permission.INTERACT_ACROSS_USERS,
                        info.activityInfo.applicationInfo.uid)
                                != PackageManager.PERMISSION_GRANTED) {
                    Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
                            + " requests FLAG_SINGLE_USER, but app does not hold "
                            + android.Manifest.permission.INTERACT_ACROSS_USERS);
                    skip = true;
                }
            }
            if (!skip) {
                r.manifestCount++;
            } else {
                r.manifestSkipCount++;
            }
            if (r.curApp != null && r.curApp.crashing) {
                // If the target process is crashing, just skip it.
                Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
                        + " to " + r.curApp + ": process crashing");
                skip = true;
            }
            if (!skip) {
                boolean isAvailable = false;
                try {
                    isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
                            info.activityInfo.packageName,
                            UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
                } catch (Exception e) {
                    // all such failures mean we skip this receiver
                    Slog.w(TAG, "Exception getting recipient info for "
                            + info.activityInfo.packageName, e);
                }
                if (!isAvailable) {
                    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                            "Skipping delivery to " + info.activityInfo.packageName + " / "
                            + info.activityInfo.applicationInfo.uid
                            + " : package no longer available");
                    skip = true;
                }
            }

            // If permissions need a review before any of the app components can run, we drop
            // the broadcast and if the calling app is in the foreground and the broadcast is
            // explicit we launch the review UI passing it a pending intent to send the skipped
            // broadcast.
            if ((mService.mPermissionReviewRequired
                    || Build.PERMISSIONS_REVIEW_REQUIRED) && !skip) {
                if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
                        info.activityInfo.packageName, UserHandle.getUserId(
                                info.activityInfo.applicationInfo.uid))) {
                    skip = true;
                }
            }

            // This is safe to do even if we are skipping the broadcast, and we need
            // this information now to evaluate whether it is going to be allowed to run.
            final int receiverUid = info.activityInfo.applicationInfo.uid;
            // If it's a singleton, it needs to be the same app or a special app
            if (r.callingUid != Process.SYSTEM_UID && isSingleton
                    && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
                info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
            }
            String targetProcess = info.activityInfo.processName;
            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
                    info.activityInfo.applicationInfo.uid, false);

            if (!skip) {
                final int allowed = mService.checkAllowBackgroundLocked(
                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1,
                        false);
                if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                    // We won't allow this receiver to be launched if the app has been
                    // completely disabled from launches, or it was not explicitly sent
                    // to it and the app is in a state that should not receive it
                    // (depending on how checkAllowBackgroundLocked has determined that).
                    if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
                        Slog.w(TAG, "Background execution disabled: receiving "
                                + r.intent + " to "
                                + component.flattenToShortString());
                        skip = true;
                    } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
                            || (r.intent.getComponent() == null
                                && r.intent.getPackage() == null
                                && ((r.intent.getFlags()
                                        & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0))) {
                        Slog.w(TAG, "Background execution not allowed: receiving "
                                + r.intent + " to "
                                + component.flattenToShortString());
                        skip = true;
                    }
                }
            }

            if (skip) {
                if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                        "Skipping delivery of ordered [" + mQueueName + "] "
                        + r + " for whatever reason");
                r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
                r.receiver = null;
                r.curFilter = null;
                r.state = BroadcastRecord.IDLE;
                scheduleBroadcastsLocked();
                return;
            }
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast 5");
            r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
            r.state = BroadcastRecord.APP_RECEIVE;
            r.curComponent = component;
            r.curReceiver = info.activityInfo;
            if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
                Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
                        + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
                        + info.activityInfo.applicationInfo.uid);
            }

            if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
                scheduleTempWhitelistLocked(receiverUid,
                        brOptions.getTemporaryAppWhitelistDuration(), r);
            }

            // Broadcast is being executed, its package can't be stopped.
            try {
                AppGlobals.getPackageManager().setPackageStoppedState(
                        r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
            } catch (RemoteException e) {
            } catch (IllegalArgumentException e) {
                Slog.w(TAG, "Failed trying to unstop package "
                        + r.curComponent.getPackageName() + ": " + e);
            }

            // Is this receiver's application already running?
            if (app != null && app.thread != null) {
                try {
                    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast app already running");
                    app.addPackage(info.activityInfo.packageName,
                            info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
                    processCurBroadcastLocked(r, app);
                    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast 1230");
                    return;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when sending broadcast to "
                          + r.curComponent, e);
                } catch (RuntimeException e) {
                    Slog.wtf(TAG, "Failed sending broadcast to "
                            + r.curComponent + " with " + r.intent, e);
                    // If some unexpected exception happened, just skip
                    // this broadcast.  At this point we are not in the call
                    // from a client, so throwing an exception out from here
                    // will crash the entire system instead of just whoever
                    // sent the broadcast.
                    logBroadcastReceiverDiscardLocked(r);
                    finishReceiverLocked(r, r.resultCode, r.resultData,
                            r.resultExtras, r.resultAbort, false);
                    scheduleBroadcastsLocked();
                    // We need to reset the state if we failed to start the receiver.
                    r.state = BroadcastRecord.IDLE;
                    return;
                }

                // If a dead object exception was thrown -- fall through to
                // restart the application.
            }
			//....
		}
	}

01-18 17:01:31.946 V/BroadcastQueue(  436): Processing ordered broadcast [background] BroadcastRecord{cca3571 u0 android.appwidget.action.APPWIDGET_UPDATE}
01-18 17:01:31.946 V/BroadcastQueue(  436): Submitting BROADCAST_TIMEOUT_MSG [background] for BroadcastRecord{cca3571 u0 android.appwidget.action.APPWIDGET_UPDATE} at 90739
01-18 17:01:31.946 V/BroadcastQueue(  436): processNextBroadcast 2
01-18 17:01:31.946 V/BroadcastQueue(  436): processNextBroadcast 3
01-18 17:01:31.946 V/BroadcastQueue(  436): processNextBroadcast 4
01-18 17:01:31.950 V/BroadcastQueue(  436): processNextBroadcast 5
01-18 17:01:31.951 V/BroadcastQueue(  436): processNextBroadcast app already running
01-18 17:01:31.952 V/BroadcastQueue(  436): Process cur broadcast BroadcastRecord{cca3571 u0 android.appwidget.action.APPWIDGET_UPDATE} for app ProcessRecord{46e1eb2 1154:com.android.quicksearchbox/u0a49}
01-18 17:01:31.954 V/BroadcastQueue(  436): Delivering to component ComponentInfo{com.android.quicksearchbox/com.android.quicksearchbox.SearchWidgetProvider}: BroadcastRecord{cca3571 u0 android.appwidget.action.APPWIDGET_UPDATE}
01-18 17:01:31.958 V/BroadcastQueue(  436): Process cur broadcast BroadcastRecord{cca3571 u0 android.appwidget.action.APPWIDGET_UPDATE} DELIVERED for app ProcessRecord{46e1eb2 1154:com.android.quicksearchbox/u0a49}
01-18 17:01:31.958 V/BroadcastQueue(  436): processCurBroadcastLocked finish
01-18 17:01:31.958 V/BroadcastQueue(  436): processNextBroadcast 1230

//...
01-18 17:02:18.655 V/BroadcastQueue(  436): Finished with ordered broadcast BroadcastRecord{cca3571 u0 android.appwidget.action.APPWIDGET_UPDATE}

處理APPWIDGET_UPDATE這個廣播花了47’s, 爲什麼花這麼長, 後面再講.

其實 processNextBroadcast 這個函數在 processNextBroadcast 1230的時候已執行完, 沒什麼乾的了, 接下來, BroadcastQueue在等一個信號, 讓他再次進入processNextBroadcast.

processCurBroadcastLocked(r, app) 做了以下工作:

  • frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
			//...
            app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                    mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                    r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                    app.repProcState);
			//...
  • frameworks/base/core/java/android/app/ActivityThread.java
    private class ApplicationThread extends ApplicationThreadNative {
		//...
        public final void scheduleReceiver(Intent intent, ActivityInfo info,
                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                boolean sync, int sendingUser, int processState) {
            android.util.Log.d("BroadcastQueue-ActivityThread", "scheduleReceiver");
            updateProcessState(processState, false);
            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                    sync, false, mAppThread.asBinder(), sendingUser);
            r.info = info;
            r.compatInfo = compatInfo;
            sendMessage(H.RECEIVER, r);
        }
		//...
	}

	//消息處理後調用: 
	private void handleReceiver(ReceiverData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        String component = data.intent.getComponent().getClassName();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);

        IActivityManager mgr = ActivityManagerNative.getDefault();

        BroadcastReceiver receiver;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
        } catch (Exception e) {
            if (DEBUG_BROADCAST) Slog.i(TAG,
                    "Finishing failed broadcast to " + data.intent.getComponent());
            data.sendFinished(mgr);
            throw new RuntimeException(
                "Unable to instantiate receiver " + component
                + ": " + e.toString(), e);
        }
        try {
            Application app = packageInfo.makeApplication(false, mInstrumentation);

            if (localLOGV) Slog.v(
                TAG, "Performing receive of " + data.intent
                + ": app=" + app
                + ", appName=" + app.getPackageName()
                + ", pkg=" + packageInfo.getPackageName()
                + ", comp=" + data.intent.getComponent().toShortString()
                + ", dir=" + packageInfo.getAppDir());

            ContextImpl context = (ContextImpl)app.getBaseContext();
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            Slog.i("BroadcastQueue-ActivityThread", "handleReceiver 3 " + receiver.getClass().getSimpleName());
            receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);
        } catch (Exception e) {
            if (DEBUG_BROADCAST) Slog.i(TAG,
                    "Finishing failed broadcast to " + data.intent.getComponent());
            data.sendFinished(mgr);
            if (!mInstrumentation.onException(receiver, e)) {
                throw new RuntimeException(
                    "Unable to start receiver " + component
                    + ": " + e.toString(), e);
            }
        } finally {
            sCurrentBroadcastIntent.set(null);
        }

        if (receiver.getPendingResult() != null) {
            data.finish();//PendingResult.finish
        }
    }

執行時間最長的就是receiver.onReceive(context.getReceiverRestrictedContext(), data.intent);, 也就是QuickSearchBox.SearchWidgetProvider中的onReceive執行了相當長的時間.

data.finish() 的後續工作:

  • frameworks/base/core/java/android/content/BroadcastReceiver.java
public abstract class BroadcastReceiver {
    //...
    /**
     * State for a result that is pending for a broadcast receiver.  Returned
     * by {@link BroadcastReceiver#goAsync() goAsync()}
     * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}.
     * This allows you to return from onReceive() without having the broadcast
     * terminate; you must call {@link #finish()} once you are done with the
     * broadcast.  This allows you to process the broadcast off of the main
     * thread of your app.
     *
     * <p>Note on threading: the state inside of this class is not itself
     * thread-safe, however you can use it from any thread if you properly
     * sure that you do not have races.  Typically this means you will hand
     * the entire object to another thread, which will be solely responsible
     * for setting any results and finally calling {@link #finish()}.
     */
    public static class PendingResult {
		//....
        /**
         * Finish the broadcast.  The current result will be sent and the
         * next broadcast will proceed.
         */
        public final void finish() {
            if (mType == TYPE_COMPONENT) {
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                if (QueuedWork.hasPendingWork()) {
                    // If this is a broadcast component, we need to make sure any
                    // queued work is complete before telling AM we are done, so
                    // we don't have our process killed before that.  We now know
                    // there is pending work; put another piece of work at the end
                    // of the list to finish the broadcast, so we don't block this
                    // thread (which may be the main thread) to have it finished.
                    //
                    // Note that we don't need to use QueuedWork.add() with the
                    // runnable, since we know the AM is waiting for us until the
                    // executor gets to it.
                    QueuedWork.singleThreadExecutor().execute( new Runnable() {
                        @Override public void run() {
                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                    "Finishing broadcast after work to component " + mToken);
                            sendFinished(mgr);
                        }
                    });
                } else {
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing broadcast to component " + mToken);
                    sendFinished(mgr);
                }
            } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing broadcast to " + mToken);
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                sendFinished(mgr);
            }
        }

		/** @hide */
        public void sendFinished(IActivityManager am) {
            synchronized (this) {
                if (mFinished) {
                    throw new IllegalStateException("Broadcast already finished");
                }
                mFinished = true;

                try {
                    if (mResultExtras != null) {
                        mResultExtras.setAllowFds(false);
                    }
                    if (mOrderedHint) {
                        am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                                mAbortBroadcast, mFlags);
                    } else {
                        // This broadcast was sent to a component; it is not ordered,
                        // but we still need to tell the activity manager we are done.
                        am.finishReceiver(mToken, 0, null, null, false, mFlags);
                    }
                } catch (RemoteException ex) {
                }
            }
        }

	}
}
  • frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    public void finishReceiver(IBinder who, int resultCode, String resultData,
            Bundle resultExtras, boolean resultAbort, int flags) {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + who);

        // Refuse possible leaked file descriptors
        if (resultExtras != null && resultExtras.hasFileDescriptors()) {
            throw new IllegalArgumentException("File descriptors passed in Bundle");
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            boolean doNext = false;
            BroadcastRecord r;

            synchronized(this) {
                BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
                        ? mFgBroadcastQueue : mBgBroadcastQueue;
                r = queue.getMatchingOrderedReceiver(who);
                if (r != null) {
                    doNext = r.queue.finishReceiverLocked(r, resultCode,
                        resultData, resultExtras, resultAbort, true);
                }
            }

            if (doNext) {
                r.queue.processNextBroadcast(false);//處理下一個
            }
            trimApplications();
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

最終調回BroadcastQueue.processNextBroadcast繼續執行發送下一個廣播(BOOT_COMPLETED).

爲什麼跟QuickSearchBox有關, 在代碼中加了LOG:

//diff --git a/packages/apps/QuickSearchBox/src/com/android/quicksearchbox/SearchWidgetProvider.java b/packages/apps/QuickSearchBox/src/com/android/quicksearchbox/SearchWidgetProvider.java
//old mode 100644
//new mode 100755
index 205e7cc..4ee4dbb
--- a/packages/apps/QuickSearchBox/src/com/android/quicksearchbox/SearchWidgetProvider.java
+++ b/packages/apps/QuickSearchBox/src/com/android/quicksearchbox/SearchWidgetProvider.java
@@ -51,7 +51,7 @@ import java.util.Random;
  */
 public class SearchWidgetProvider extends BroadcastReceiver {
 
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
     private static final String TAG = "QSB.SearchWidgetProvider";
 
     /**/
@@ -73,12 +73,15 @@ public class SearchWidgetProvider extends BroadcastReceiver {
     }
 
     private static SearchWidgetState[] getSearchWidgetStates(Context context) {
+        if (DBG) Log.d(TAG, "getSearchWidgetStates");
         AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
         int[] appWidgetIds = appWidgetManager.getAppWidgetIds(myComponentName(context));
+        if (DBG) Log.d(TAG, "getSearchWidgetStates appWidgetManager.getAppWidgetIds [F]");
         SearchWidgetState[] states = new SearchWidgetState[appWidgetIds.length];
         for (int i = 0; i<appWidgetIds.length; ++i) {
             states[i] = getSearchWidgetState(context, appWidgetIds[i]);
         }
+        if (DBG) Log.d(TAG, "getSearchWidgetStates finish");
         return states;
     }
 
@@ -93,6 +96,7 @@ public class SearchWidgetProvider extends BroadcastReceiver {
         for (SearchWidgetState state : states) {
             state.updateWidget(context, AppWidgetManager.getInstance(context));
         }
+        if (DBG) Log.d(TAG, "updateSearchWidgets finish");
     }
 
     /**
  • 輸出的LOG
01-18 17:02:21.106 D/QSB.SearchWidgetProvider( 1154): onReceive(#Intent;action=android.appwidget.action.APPWIDGET_UPDATE;launchFlags=0x10;component=com.android.quicksearchbox/.SearchWidgetProvider;end)
01-18 17:02:21.106 D/QSB.SearchWidgetProvider( 1154): updateSearchWidgets
01-18 17:02:21.106 D/QSB.SearchWidgetProvider( 1154): getSearchWidgetStates
01-18 17:02:21.114 D/QSB.SearchWidgetProvider( 1154): getSearchWidgetStates appWidgetManager.getAppWidgetIds [F]
01-18 17:02:21.114 D/QSB.SearchWidgetProvider( 1154): Creating appwidget state 11960
//省略 N 行
01-18 17:02:28.723 D/QSB.SearchWidgetProvider( 1154): Creating appwidget state 11962
01-18 17:02:28.724 D/QSB.SearchWidgetProvider( 1154): getSearchWidgetStates finish
01-18 17:02:28.724 D/QSB.SearchWidgetProvider( 1154): Updating appwidget 11960
//省略 N 行
01-18 17:02:59.096 D/QSB.SearchWidgetProvider( 1154): Updating appwidget 9262
01-18 17:03:04.446 D/QSB.SearchWidgetProvider( 1154): updateSearchWidgets finish

**int[] appWidgetIds = appWidgetManager.getAppWidgetIds(myComponentName(context));**返回了很多ID
而這個ID則由系統服務維護處理:

  • frameworks/base/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
    具體不細講, 這些ID保存在/data下的一個文件: system/users/0/appwidgets.xml

罪魁禍首

問題的癥結在於 appwidgets.xml 中的內容會每重啓一次自增一行. 如:

  • 第一次啓動內容
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<gs version="1">
<p pkg="com.android.deskclock" cl="com.android.alarmclock.AnalogAppWidgetProvider" tag="2" />
<p pkg="com.android.quicksearchbox" cl="com.android.quicksearchbox.SearchWidgetProvider" tag="7" />
<h pkg="com.android.launcher3" id="400" tag="0" />
<g id="2ebc" rid="0" h="0" p="7" min_width="3a2" min_height="ae" max_width="672" max_height="ce" host_category="1" />
<g id="2ebe" rid="0" h="0" p="2" min_width="11e" min_height="144" max_width="20e" max_height="184" host_category="1" />
<g id="2ec0" rid="0" h="0" p="7" min_width="3a2" min_height="ae" max_width="672" max_height="ce" host_category="1" />
</gs>
  • 第二次啓動內容
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<gs version="1">

<!-- 小部件的描述, 如組件信息包類名等 -->
<p pkg="com.android.deskclock" cl="com.android.alarmclock.AnalogAppWidgetProvider" tag="2" />
<p pkg="com.android.quicksearchbox" cl="com.android.quicksearchbox.SearchWidgetProvider" tag="7" />

<!-- 持有者信息, 如當前是Launcher3中調用顯示了小部件 -->
<h pkg="com.android.launcher3" id="400" tag="0" />

<!-- 小部件信息, id, 長寬, 持有者等 -->
<g id="2ec0" rid="0" h="0" p="7" min_width="3a2" min_height="ae" max_width="672" max_height="ce" host_category="1" />
<g id="2ebe" rid="0" h="0" p="2" min_width="11e" min_height="144" max_width="20e" max_height="184" host_category="1" />
<g id="2ebc" rid="0" h="0" p="7" min_width="3a2" min_height="ae" max_width="672" max_height="ce" host_category="1" />
<g id="2ec2" rid="0" h="0" p="7" min_width="3a2" min_height="ae" max_width="672" max_height="ce" host_category="1" />
</gs>
  • 當前重啓了5000+後, cat system/users/0/appwidgets.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<gs version="1">
<p pkg="com.android.quicksearchbox" cl="com.android.quicksearchbox.SearchWidgetProvider" tag="7" />
<h pkg="com.android.launcher3" id="400" tag="0" />

<g id="22c8" rid="0" h="0" p="7" min_width="3a2" min_height="ae" max_width="672" max_height="ce" host_category="1" />
<!-- 此處省略5000行 -->
</gs>

在後續測試中, 發現, 手動往Launcher3添加時鐘小部件, 不管重啓幾次都不會自增一行, 而搜索的小部件, 是由Launcher3自動添進去的, 它是個特殊的存在

  • 於是, 查了下Launcher3的layout如圖:
    在這裏插入圖片描述
  • packages/apps/Launcher3/src/com/android/launcher3/QsbContainerView.java
		private View createQsb(LayoutInflater inflater, ViewGroup container) {
            Launcher launcher = Launcher.getLauncher(getActivity());
            mWidgetInfo = getSearchWidgetProvider(launcher);
            if (mWidgetInfo == null) {
                // There is no search provider, just show the default widget.
                return getDefaultView(inflater, container, false);
            }

            SharedPreferences prefs = Utilities.getPrefs(launcher);
            AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(launcher);
            LauncherAppWidgetHost widgetHost = launcher.getAppWidgetHost();
            InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();

            Bundle opts = new Bundle();
            Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(launcher, idp.numColumns, 1, null);
            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);

            int widgetId = prefs.getInt(QSB_WIDGET_ID, -1);
            AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
            boolean isWidgetBound = (widgetInfo != null) &&
                    widgetInfo.provider.equals(mWidgetInfo.provider);

            if (!isWidgetBound) {
                // widgetId is already bound and its not the correct provider.
                // Delete the widget id.
                if (widgetId > -1) {
                    widgetHost.deleteAppWidgetId(widgetId);
                    widgetId = -1;
                }

                widgetId = widgetHost.allocateAppWidgetId();
                isWidgetBound = widgetManager.bindAppWidgetIdSkipBindPermission(widgetId, mWidgetInfo, opts);
                if (!isWidgetBound) {
                    widgetHost.deleteAppWidgetId(widgetId);
                    widgetId = -1;
                }
            }

            if (isWidgetBound) {
                mQsb = (LauncherAppWidgetHostView)
                        widgetHost.createView(launcher, widgetId, mWidgetInfo);
                mQsb.setId(R.id.qsb_widget);
                mQsb.mErrorViewId = R.layout.qsb_default_view;

                if (!Utilities.containsAll(AppWidgetManager.getInstance(launcher)
                        .getAppWidgetOptions(widgetId), opts)) {
                try{
                    mQsb.updateAppWidgetOptions(opts);
                }catch(Exception e){
                   Log.v("QsbContainerView", "getQsbBar error "+e.getMessage());
                   return null; 
                }
                }
                    mQsb.setPadding(0, 0, 0, 0);
                return mQsb;
            }

            // Return a default widget with setup icon.
            return getDefaultView(inflater, container, true);
        }

這段代碼的問題在於int widgetId = prefs.getInt(QSB_WIDGET_ID, -1);, widgetId一直是 -1.
widgetId = widgetHost.allocateAppWidgetId();申請了一個ID, 並調用bindAppWidgetIdSkipBindPermission, 在這個操作完成後, system/users/0/appwidgets.xml 就增加了一行.
而後, 在SearchWidgetProvider就得處理多一次, 在幾千次後, 時間就不是那麼容易忽略的問題了.

後話

  • 這個問題在RK3288上面也同樣存在
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章