Android 偵聽應用(Package)變化的方法偵聽廣播


應用的狀態變化,包括安裝、卸載、更新,是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時是否保留數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM