Android侦听应用(Package)变化的方法(1)侦听广播

应用的状态变化,包括安装、卸载、更新,是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, 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时是否保留数据。


上述参看代码版本为android7.1。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值