Android 7 一鍵清理近期任務(wù)并保留白名單實現(xiàn)記錄

需求背景

根據(jù)客戶需求,需要提供一個一鍵結(jié)束所有應用并排除白名單的功能,效果類似 Android SystemUI 里「近期任務(wù)」界面的清理/結(jié)束任務(wù)按鈕:
點擊后清空近期任務(wù),但對于指定的白名單應用,需要繼續(xù)保持運行,不被殺掉。

在網(wǎng)上搜索后,并沒有找到現(xiàn)成可用的實現(xiàn)方案,只能基于源碼自行實現(xiàn)。

實現(xiàn)原理

既然 SystemUI 自身已經(jīng)有「清理近期任務(wù)」的功能,就說明系統(tǒng)內(nèi)部一定已經(jīng)提供了相關(guān)接口,只是沒有對外暴露。
通過調(diào)試 frameworks/base/packages/SystemUI 源碼,可以看到:當按下近期任務(wù)按鈕、執(zhí)行清理操作時,會調(diào)用:

  • SystemServicesProxy#getRecentTasks(...):獲取近期任務(wù)列表
  • 最終通過 ActivityManager.removeTask(int taskId) 來真正清理任務(wù)

思路就很明確了:

  1. 復用 SystemServicesProxy#getRecentTasks(...) 拿到當前所有近期任務(wù)列表;
  2. 根據(jù)白名單列表,將需要保留的應用從任務(wù)列表中移除;
  3. 對剩余任務(wù)逐個調(diào)用 ActivityManager.removeTask(...),完成清理。

為方便在系統(tǒng)中統(tǒng)一調(diào)用,這里通過自定義廣播的方式來觸發(fā)清理邏輯。


新增廣播 KillProcessesReceiver

frameworks/base/packages/SystemUI/src/com/android/systemui/receiver/ 下新增文件:

  • KillProcessesReceiver.java

示例代碼如下(去掉了多余的高亮標簽,保留核心邏輯):

package com.android.systemui.receiver;

import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;

import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 結(jié)束指定進程(清理近期任務(wù),支持白名單)
 */
public class KillProcessesReceiver extends BroadcastReceiver {

    public static final String ACTION = "con.yt.kill.processes";
    private static final String TAG = "recent";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (!ACTION.equals(intent.getAction())) {
            return;
        }

        PackageManager pm = context.getPackageManager();
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

        try {
            SystemServicesProxy ssp = Recents.getSystemServices();
            int currentUserId = UserHandle.USER_CURRENT;

            // 獲取近期任務(wù)列表
            List<ActivityManager.RecentTaskInfo> tasks =
                    ssp.getRecentTasks(
                            ActivityManager.getMaxRecentTasksStatic(),
                            currentUserId,
                            false,
                            new ArraySet<>()
                    );

            if (tasks == null || tasks.size() == 0) {
                Log.e(TAG, "tasks Empty");
                return;
            }

            // 讀取白名單(包名或類名)
            ArrayList<String> whiteApp =
                    intent.getStringArrayListExtra("whiteApp"); // 過濾白名單

            if (whiteApp != null && !whiteApp.isEmpty()) {
                Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
                while (iter.hasNext()) {
                    ActivityManager.RecentTaskInfo t = iter.next();
                    for (String s : whiteApp) {
                        if (s.equals(t.realActivity.getClassName())
                                || s.equals(t.realActivity.getPackageName())) {
                            iter.remove();
                            Log.i(TAG, "package is in white list, id=" + t.id
                                    + ", package=" + s);
                            break;
                        }
                    }
                }
            }

            if (tasks.size() == 0) {
                Log.e(TAG, "tasks Empty");
                return;
            }

            // 逐個清理剩余任務(wù)
            Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
            while (iter.hasNext()) {
                ActivityManager.RecentTaskInfo t = iter.next();
                Log.i(TAG, "RecentTaskInfo stackId: " + t.stackId
                        + ", baseActivity: " + t.baseActivity);

                // 注意:這里使用 persistentId
                am.removeTask(t.persistentId);
            }
        } catch (Exception e) {
            Log.e(TAG, "Failed to get recent tasks", e);
        }
    }
}

核心邏輯說明:

  • 通過 Recents.getSystemServices() 拿到 SystemServicesProxy
  • 調(diào)用 getRecentTasks(...) 獲取當前用戶的所有近期任務(wù);
  • 從 Intent 中取出白名單列表 whiteApp,按包名 / 類名過濾;
  • 對過濾后的任務(wù)使用 ActivityManager#removeTask(persistentId) 逐個移除。

注冊廣播(SystemUI AndroidManifest.xml

文件位置:

  • frameworks/base/packages/SystemUI/AndroidManifest.xml

application 節(jié)點中增加廣播注冊(示例):

<receiver
    android:name=".receiver.KillProcessesReceiver"
    android:exported="false">
    <intent-filter>
        <action android:name="con.yt.kill.processes" />
    </intent-filter>
</receiver>

保護廣播(系統(tǒng) AndroidManifest.xml

文件位置:

  • frameworks/base/core/res/AndroidManifest.xml

為防止普通應用隨意發(fā)送該廣播,需要將其聲明為受保護廣播:

<protected-broadcast android:name="con.yt.kill.processes" />

這樣,只有具有系統(tǒng)權(quán)限的組件才能正常發(fā)送此廣播。


調(diào)用方式示例

上層只需要構(gòu)造相應的 Intent,并攜帶白名單列表即可。例如:

public static final String ACTION_KILL_PROCESSES = "con.yt.kill.processes";

@Override
public void killBackgroundProcesses(Context context, ArrayList<String> whiteApp) {
    Intent intent = new Intent(ACTION_KILL_PROCESSES);
    intent.putStringArrayListExtra("whiteApp", whiteApp);
    context.sendBroadcast(intent);
}

其中:

  • whiteApp:白名單列表,可填入包名或具體 Activity 類名;
  • 不在白名單中的近期任務(wù),會被統(tǒng)一通過 removeTask(...) 清理掉。

至此,就實現(xiàn)了在 Android 7 平臺上,一鍵清理近期任務(wù)且保留白名單應用的系統(tǒng)級方案。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容