需求背景
根據(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ù)
思路就很明確了:
- 復用
SystemServicesProxy#getRecentTasks(...)拿到當前所有近期任務(wù)列表; - 根據(jù)白名單列表,將需要保留的應用從任務(wù)列表中移除;
- 對剩余任務(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)級方案。