forceStopPackage与killBackgroundProcesses方法

本文详细介绍了Android中实现一键清理功能的两个关键方法:killBackgroundProcesses()和forceStopPackage()。killBackgroundProcesses()用于杀死指定包的后台进程,而forceStopPackage()则更彻底,不仅杀死进程,还会停止服务、移除活动、取消定时器和通知,并阻止接收大部分广播。forceStopPackage()的使用应注意其权限限制和级联影响。
摘要由CSDN通过智能技术生成

最近了解一键清理功能,需要实现强制关闭进程的功能。下面介绍下killBackgroundProcesses()方法和forceStopPackage()方法。

killBackgroundProcesses()

ActivityManager的killBackgroundProcesses方法,可以立即杀死与指定包相关联的所有后台进程,这与内核杀死那些进程回收内存是一样的,但这些进程如果在将来某一时刻需要使用,会重新启动。该方法需要权限android.permission.KILL_BACKGROUND_PROCESSES。源码解释如下:

    /**
     * Have the system immediately kill all background processes associated
     * with the given package.  This is the same as the kernel killing those
     * processes to reclaim memory; the system will take care of restarting
     * these processes in the future as needed.
     *
     * <p>You must hold the permission
     * {@link android.Manifest.permission#KILL_BACKGROUND_PROCESSES} to be able to
     * call this method.
     *
     * @param packageName The name of the package whose processes are to
     * be killed.
     */
    public void killBackgroundProcesses(String packageName) {
        try {
            ActivityManagerNative.getDefault().killBackgroundProcesses(packageName,
                    UserHandle.myUserId());
        } catch (RemoteException e) {
        }
    }

    最终调用到ActivityManagerService的killBackgroundProcesses()方法。

    @Override
    public void killBackgroundProcesses(final String packageName, int userId) {
        if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
                != PackageManager.PERMISSION_GRANTED &&
                checkCallingPermission(android.Manifest.permission.RESTART_PACKAGES)
                        != PackageManager.PERMISSION_GRANTED) {
            String msg = "Permission Denial: killBackgroundProcesses() from pid="
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid()
                    + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }

        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
        long callingId = Binder.clearCallingIdentity();
        try {
            IPackageManager pm = AppGlobals.getPackageManager();
            synchronized(this) {
                int appId = -1;
                try {
                    appId = UserHandle.getAppId(
                            pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId));
                } catch (RemoteException e) {
                }
                if (appId == -1) {
                    Slog.w(TAG, "Invalid packageName: " + packageName);
                    return;
                }
                // 杀死package相关的进程
                killPackageProcessesLocked(packageName, appId, userId,
                        ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }
    调用AMS的killPackageProcessesLocked()方法,杀死package相关的进程。

forceStopPackage()

调用此方法,系统会强制停止与指定包关联的所有事情,将会杀死使用同一个uid的所有进程,停止所有服务,移除所有activity。所有注册的定时器和通知也会移除。这个方法时不允许第三方应用调用的,否则,APP间可以相互停止对方(因为这个方法可以停止其他应用的服务,移除alarm等);forceStopPackage方法源码解释如下():

    /**
     * Have the system perform a force stop of everything associated with
     * the given application package.  All processes that share its uid
     * will be killed, all services it has running stopped, all activities
     * removed, etc.  In addition, a {@link Intent#ACTION_PACKAGE_RESTARTED}
     * broadcast will be sent, so that any of its registered alarms can
     * be stopped, notifications removed, etc.
     *
     * <p>You must hold the permission
     * {@link android.Manifest.permission#FORCE_STOP_PACKAGES} to be able to
     * call this method.
     *
     * @param packageName The name of the package to be stopped.
     * @param userId The user for which the running package is to be stopped.
     *
     * @hide This is not available to third party applications due to
     * it allowing them to break other applications by stopping their
     * services, removing their alarms, etc.
     */
    public void forceStopPackageAsUser(String packageName, int userId) {
        try {
            ActivityManagerNative.getDefault().forceStopPackage(packageName, userId);
        } catch (RemoteException e) {
        }
    }

    /**
     * @see #forceStopPackageAsUser(String, int)
     * @hide
     */
    public void forceStopPackage(String packageName) {
        forceStopPackageAsUser(packageName, UserHandle.myUserId());
    }

注意使用forceStopPackage方法时,需要添加权限android.permission.FORCE_STOP_PACKAGES。同时注意该方法是隐藏的方法,需要使用反射机制调用。如下:

ActivityManager mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
Method method = Class.forName("android.app.ActivityManager").getMethod("forceStopPackage", String.class);
method.invoke(mActivityManager, packageName);  //packageName是需要强制停止的应用程序包名


该方法最终在AMS中处理:

   @Override
    public void forceStopPackage(final String packageName, int userId) {
        if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                != PackageManager.PERMISSION_GRANTED) {
            String msg = "Permission Denial: forceStopPackage() from pid="
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid()
                    + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }// 检查是否有android.permission.FORCE_STOP_PACKAGES权限
        final int callingPid = Binder.getCallingPid();
        userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
                userId, true, ALLOW_FULL_ONLY, "forceStopPackage", null);
        long callingId = Binder.clearCallingIdentity();
        try {
            IPackageManager pm = AppGlobals.getPackageManager();//在系统中可以这样获取PackageManager对象
            synchronized(this) {
                int[] users = userId == UserHandle.USER_ALL
                        ? mUserController.getUsers() : new int[] { userId };
                for (int user : users) {//遍历所有user
                    int pkgUid = -1;
                    try {
                        pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
                                user);
                    } catch (RemoteException e) {
                    }
                    if (pkgUid == -1) {
                        Slog.w(TAG, "Invalid packageName: " + packageName);
                        continue;
                    }
                    try {
                        pm.setPackageStoppedState(packageName, true, user);//(1)设置应用的stop状态
                    } catch (RemoteException e) {
                    } catch (IllegalArgumentException e) {
                        Slog.w(TAG, "Failed trying to unstop package "
                                + packageName + ": " + e);
                    }
                    if (mUserController.isUserRunningLocked(user, 0)) {
                        forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);//(2)清理进程,以及package所涉及的四大组件
                        finishForceStopPackageLocked(packageName, pkgUid);//(3)清理alarm,notification。
                    }
                }
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

    (1)setPackageStoppedState()

    这里有一个过程非常重要,就是setPackageStoppedState()方法将包的状态设置为stopped,那么所有广播都无法接收,除非带有标记FLAG_INCLUDE_STOPPED_PACKAGES的广播,系统默认的广播几乎都不带有该标记,也就意味着被force-stop的应用是无法通过建立手机网络状态或者亮灭屏广播来拉起进程的。

    当使用force stop方式来结束进程时,reason一般都是“from pid ” + callingPid。当然也有另外,那就是AMS.clearApplicationUserData方法调用forceStopPackageLocked的reason为“clear data”。

    (2)forceStopPackageLocked()

    private void forceStopPackageLocked(final String packageName, int uid, String reason) {
        forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
                false, true, false, false, UserHandle.getUserId(uid), reason);
    }
    final boolean forceStopPackageLocked(String packageName, int appId,
            boolean callerWillRestart, boolean purgeCache, boolean doit,
            boolean evenPersistent, boolean uninstalling, int userId, String reason) {
        int i;

        if (userId == UserHandle.USER_ALL && packageName == null) {
            Slog.w(TAG, "Can't force stop all processes of all users, that is insane!");
        }

        if (appId < 0 && packageName != null) {
            try {
                appId = UserHandle.getAppId(AppGlobals.getPackageManager()
                        .getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, 0));//重新获取正确的appid
            } catch (RemoteException e) {
            }
        }

        if (doit) {// doit为true,进入这个分支,
            if (packageName != null) {
                Slog.i(TAG, "Force stopping " + packageName + " appid=" + appId
                        + " user=" + userId + ": " + reason);
            } else {
                Slog.i(TAG, "Force stopping u" + userId + ": " + reason);
            }

            mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);//移除应用的crash计数
        }
        

        boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
                ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
                packageName == null ? ("stop user " + userId) : ("stop " + packageName));//清理进程

        // 清理进程包含的Activity
        if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
                packageName, null, doit, evenPersistent, userId)) {
            if (!doit) {
                return true;
            }
            didSomething = true;
        }

        // 结束该包中的service
        if (mServices.bringDownDisabledPackageServicesLocked(
                packageName, null, userId, evenPersistent, true, doit)) {
            if (!doit) {
                return true;
            }
            didSomething = true;
        }

        if (packageName == null) {
            // Remove all sticky broadcasts from this user.
            mStickyBroadcasts.remove(userId);//包名为空时,则移除当前用户的所有stickyBroadcasts
        }
        

        ArrayList<ContentProviderRecord> providers = new ArrayList<>();//收集进程包含的providers
        if (mProviderMap.collectPackageProvidersLocked(packageName, null, doit, evenPersistent,
                userId, providers)) {
            if (!doit) {
                return true;
            }
            didSomething = true;
        }
        for (i = providers.size() - 1; i >= 0; i--) {
            removeDyingProviderLocked(null, providers.get(i), true);//清理providers
        }

        // Remove transient permissions granted from/to this package/user 
        removeUriPermissionsForPackageLocked(packageName, userId, false);//移除以获取的跟该package/user相关的临时权限

        if (doit) {
            // 清理广播
            for (i = mBroadcastQueues.length - 1; i >= 0; i--) {
                didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
                        packageName, null, userId, doit);
            }
        }

        if (packageName == null || uninstalling) {// 包名为null的情况
            // Remove pending intents.  For now we only do this when force移除Pending Intent
            // stopping users, because we have some problems when doing this
            // for packages -- app widgets are not currently cleaned up for
            // such packages, so they can be left with bad pending intents.
            // 现在我们仅在强制停止用户时才这样做,因为我们在为包做这件事时遇到了一些问题——应用程序小部件目前没有被清理出来,
            // 所以它们可以留下坏的挂起意图。
            if (mIntentSenderRecords.size() > 0) {
                Iterator<WeakReference<PendingIntentRecord>> it
                        = mIntentSenderRecords.values().iterator();
                while (it.hasNext()) {
                    WeakReference<PendingIntentRecord> wpir = it.next();
                    if (wpir == null) {
                        it.remove();
                        continue;
                    }
                    PendingIntentRecord pir = wpir.get();
                    if (pir == null) {
                        it.remove();
                        continue;
                    }
                    if (packageName == null) {
                        // Stopping user, remove all objects for the user.
                        if (pir.key.userId != userId) {
                            // Not the same user, skip it.
                            continue;
                        }
                    } else {
                        if (UserHandle.getAppId(pir.uid) != appId) {
                            // Different app id, skip it.
                            continue;
                        }
                        if (userId != UserHandle.USER_ALL && pir.key.userId != userId) {
                            // Different user, skip it.
                            continue;
                        }
                        if (!pir.key.packageName.equals(packageName)) {
                            // Different package, skip it.
                            continue;
                        }
                    }
                    if (!doit) {
                        return true;
                    }
                    didSomething = true;
                    it.remove();
                    pir.canceled = true;
                    if (pir.key.activity != null && pir.key.activity.pendingResults != null) {
                        pir.key.activity.pendingResults.remove(pir.ref);
                    }
                }
            }
        }

        if (doit) {
            if (purgeCache && packageName != null) {// purgeCache为false
                AttributeCache ac = AttributeCache.instance();
                if (ac != null) {
                    ac.removePackage(packageName);
                }
            }
            if (mBooted){//恢复栈顶的Activity
                mStackSupervisor.resumeFocusedStackTopActivityLocked();
                mStackSupervisor.scheduleIdleLocked();
            }
        }

        return didSomething;
    }
    对于didSomething只指当方法中所有行为都发生时,则返回true。比如killPackageProcessesLocked(),只要杀死一个进程则代表didSomething为true。

    该方法主要功能:

        1、Process:调用AMS.killPackageProcessesLocked()清理该package所涉及的进程;

        2、Activity:调用ActivityStackSupervisor.finishDisablePackageActivitiesLocked()清理该package所涉及的Activity;

        3、Service:调用ActiveService.bringDownDisabledPackageServiceLocked()清理该package所涉及的Service;

        4、Provider:调用AMS.removeDyingProviderLocked()清理该package所涉及的Provider;

        5、BroadcastReceiver:调用BroadcastQueue.cleanupDisabledPackageReceiversLocked()清理该package所涉及的广播。

        6、还要清除进程临时获取的Uri访问权限。

    各个方法的具体分析可参考:http://blog.csdn.net/wuyabing12345/article/details/53067470

      (3)finishForceStopPackageLocked()

    private void finishForceStopPackageLocked(final String packageName, int uid) {
        Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
                Uri.fromParts("package", packageName, null));
        if (!mProcessesReady) {
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                    | Intent.FLAG_RECEIVER_FOREGROUND);
        }
        intent.putExtra(Intent.EXTRA_UID, uid);
        intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));

       //发送广播用于停止alarm以及通知
       broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid)); 
    清理跟该包名相关的进程和四大组件之外,还会发送广播ACTION_PACKAGE_RESTARTED,用于清理已注册的alarm,notification信息。

    发送这个广播后,最后调用到注册该广播的接收者。

        (A)、Alarm清理:在AlarmManagerService.java中注册了该广播。

    class UninstallReceiver extends BroadcastReceiver {
        public UninstallReceiver() {
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
            filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
            filter.addDataScheme("package");
            getContext().registerReceiver(this, filter);
            ......
        }
        @Override
        public void onReceive(Context context, Intent intent) {
            synchronized (mLock) {
                String action = intent.getAction();
                String pkgList[] = null;
                if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
                    ......
                } else {
                    ......
                    Uri data = intent.getData();
                    if (data != null) {
                        String pkg = data.getSchemeSpecificPart();
                        if (pkg != null) {
                            pkgList = new String[]{pkg};
                        }
                    }
                }
                if (pkgList != null && (pkgList.length > 0)) {
                    for (String pkg : pkgList) {
                        // 移除alarm
                        removeLocked(pkg);
                        mPriorities.remove(pkg);
                        for (int i=mBroadcastStats.size()-1; i>=0; i--) {
                            ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);
                            if (uidStats.remove(pkg) != null) {
                                if (uidStats.size() <= 0) {
                                    mBroadcastStats.removeAt(i);
                                }
                            }
                        }
                    }
                }
            }
        }
    调用AlarmManagerService中的removeLocked()方法,从mAlarmBatches和mPendingWhileIdleAlarms队列中移除包所相关的alarm。

        (B)、notification清理:NotificationManagerService.java注册该广播。

    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action == null) {
                return;
            }

            boolean queryRestart = false;
            boolean queryRemove = false;
            boolean packageChanged = false;
            boolean cancelNotifications = true;
            int reason = REASON_PACKAGE_CHANGED;

            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
                    || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
                int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
                        UserHandle.USER_ALL);
                String pkgList[] = null;
                boolean removingPackage = queryRemove &&
                        !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                    ......
                } else {
                    Uri uri = intent.getData();
                    if (uri == null) {
                        return;
                    }
                    String pkgName = uri.getSchemeSpecificPart();
                    if (pkgName == null) {
                        return;
                    }
                    if (packageChanged) {
                        // We cancel notifications for packages which have just been disabled
                        ......
                }

                if (pkgList != null && (pkgList.length > 0)) {
                    for (String pkgName : pkgList) {
                        // 移除notification
                        if (cancelNotifications) {
                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
                                    changeUserId, reason, null);
                        }
                    }
                }
                mListeners.onPackagesChanged(removingPackage, pkgList);
                mRankerServices.onPackagesChanged(removingPackage, pkgList);
                mConditionProviders.onPackagesChanged(removingPackage, pkgList);
                mRankingHelper.onPackagesChanged(removingPackage, pkgList);
            }
        }
    };
    调用NotificationManagerService.java中的cancelAllNotificationsInt()方法,从mNotificationList队列中移除package所相关的Notification。

功能点归纳:

    1、force stop并不会杀死persistent进程;

    2、当app被force stop后,  无法接收到正常广播,那么也就无法通过监听手机网络状态的变化或亮灭屏来拉起进程;

    3、当app被force stop后,alarm闹钟一并被清理,无法实现定时响起的功能;

    4、当app被force stop后,四大组件以及相关进程被一一清理,即便多进程架构的app也无法拉起自己;

    5、级联诛杀:当app通过ClassLoader加载另一个app,则会在force stop的过程中会被级联诛杀;

    6、生死与共: 当app与另一个app使用了share uid,则会在force stop的过程,任意一方被杀则另一方也被杀,建立起生死与共的强关系。


                
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值