平台
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上面也同样存在