应用的状态变化,包括安装、卸载、更新,是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"; 应用被完全卸载时发出(数据被删除)
上述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, ArrayListcomponentNames, 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时是否保留数据。