應用的狀態變化,包括安裝、卸載、更新,是android系統上重要的事件。如何偵聽到?有兩種方法,一是通過偵聽廣播,一是實現PackageMonitor。
偵聽廣播
偵聽廣播
當Package狀態發生變化時,系統會廣播如下一些Action的Intent:
應用安裝:
public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
應用更新:
public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
應用的新版本替代舊版本被安裝
public static final String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED";
應用的新版本替代舊版本被安裝,只發給被更新的應用自己
public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
應用被改變,譬如某些組件被disable/enable
應用卸載:
public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
應用被卸載時發出,正在被卸載的應用自身不會收到
public static final String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED";
應用被完全卸載時發出(數據被刪除)
應用安裝:
public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
應用更新:
public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
應用的新版本替代舊版本被安裝
public static final String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED";
應用的新版本替代舊版本被安裝,只發給被更新的應用自己
public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
應用被改變,譬如某些組件被disable/enable
應用卸載:
public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
應用被卸載時發出,正在被卸載的應用自身不會收到
public static final String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED";
應用被完全卸載時發出(數據被刪除)
上述Intent都為保護型,只能夠由系統發出。
針對上述定義,結合android源代碼,研究幾個問題。Android對於Package的管理主要邏輯在PackageManagerService(PMS)中,主要在這個類中研究上述問題:
(1)系統如何實現只發給某個應用?
ACTION_MY_PACKAGE_REPLACED是如何處理的?
private void sendSystemPackageUpdatedBroadcastsInternal() { Bundle extras = new Bundle(2); extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); extras.putBoolean(Intent.EXTRA_REPLACING, true); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras, 0, null, null, null); sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage, extras, 0, null, null, null); sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0, removedPackage, null, null); } final void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, final int flags, final String targetPkg, final IIntentReceiver finishedReceiver, final int[] userIds) { mHandler.post(new Runnable() { @Override public void run() { try { final IActivityManager am = ActivityManagerNative.getDefault(); if (am == null) return; final int[] resolvedUserIds; if (userIds == null) { resolvedUserIds = am.getRunningUserIds(); } else { resolvedUserIds = userIds; } for (int id : resolvedUserIds) { final Intent intent = new Intent(action, pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null); if (extras != null) { intent.putExtras(extras); } if (targetPkg != null) { intent.setPackage(targetPkg); } // Modify the UID when posting to other users int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); if (uid > 0 && UserHandle.getUserId(uid) != id) { uid = UserHandle.getUid(id, UserHandle.getAppId(uid)); intent.putExtra(Intent.EXTRA_UID, uid); } intent.putExtra(Intent.EXTRA_USER_HANDLE, id); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags); if (DEBUG_BROADCASTS) { RuntimeException here = new RuntimeException("here"); here.fillInStackTrace(); Slog.d(TAG, "Sending to user " + id + ": " + intent.toShortString(false, true, false, false) + " " + intent.getExtras(), here); } am.broadcastIntent(null, intent, null, finishedReceiver, 0, null, null, null, android.app.AppOpsManager.OP_NONE, null, finishedReceiver != null, false, id); } } catch (RemoteException ex) { } } }); }
看到在廣播ACTION_MY_PACKAGE_REPLACED的時候,是通過Intent.setPackage(String packageName)實現定向發送。
(2)ACTION_PACKAGE_CHANGED在什么場景下使用?
ACTION_PACKAGE_CHANGED在PMS中只有一處使用入口:
private void sendPackageChangedBroadcast(String packageName, boolean killFlag, ArrayList<String> componentNames, int packageUid) { if (DEBUG_INSTALL) Log.v(TAG, "Sending package changed: package=" + packageName + " components=" + componentNames); Bundle extras = new Bundle(4); extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0)); String nameList[] = new String[componentNames.size()]; componentNames.toArray(nameList); extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList); extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag); extras.putInt(Intent.EXTRA_UID, packageUid); // If this is not reporting a change of the overall package, then only send it // to registered receivers. We don't want to launch a swath of apps for every // little component state change. final int flags = !componentNames.contains(packageName) ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0; sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null, new int[] {UserHandle.getUserId(packageUid)}); }
追蹤sendPackageChangedBroadcast()的調用,來自setComponentEnabledSetting()。
這是實現PackageManager的對外公開API,看一下PackageManager中對此的定義:
/** * Set the enabled setting for a package component (activity, receiver, service, provider). * This setting will override any enabled state which may have been set by the component in its * manifest. * * @param componentName The component to enable * @param newState The new enabled state for the component. The legal values for this state * are: * {@link #COMPONENT_ENABLED_STATE_ENABLED}, * {@link #COMPONENT_ENABLED_STATE_DISABLED} * and * {@link #COMPONENT_ENABLED_STATE_DEFAULT} * The last one removes the setting, thereby restoring the component's state to * whatever was set in it's manifest (or enabled, by default). * @param flags Optional behavior flags: {@link #DONT_KILL_APP} or 0. */ public abstract void setComponentEnabledSetting(ComponentName componentName, int newState, int flags);
這是PackageManager提供的修改四大控件enable/disable的API,當然調用是需要檢查權限的,此處不展開介紹。
(3)ACTION_PACKAGE_REMOVED和ACTION_PACKAGE_FULLY_REMOVED的邏輯關系
查看這兩個Action在PMS中的使用,發現只有一處:
private void sendPackageRemovedBroadcastInternal(boolean killApp) { Bundle extras = new Bundle(2); extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved); extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp); if (isUpdate || isRemovedPackageSystemUpdate) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers); if (removedPackage != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras, 0, null, null, removedUsers); if (dataRemoved && !isRemovedPackageSystemUpdate) { sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage, extras, 0, null, null, removedUsers); } } if (removedAppId >= 0) { sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, 0, null, null, removedUsers); } }
看到Package removed發生時,會發ACTION_PACKAGE_REMOVED;並且只有在數據被刪除且非刪除系統更新的app時,才會發送ACTION_PACKAGE_FULLY_REMOVED。在PackageManager中有一個flag常量定義PackageManager.DELETE_KEEP_DATA,用以決定在刪除app時是否保留數據。
