Android判断前后台的方法分析(一)

299 阅读2分钟

( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)🥬( ´◔︎ ‸◔︎)

借助ActivityManager独特的属性,根据进程重要性来判断当前APP的前后台状态,一般在前台进程为IMPORTANCE_FOREGROUND,请注意,这只是一般的情况,如果某一个Service设置为sticky,那么会将这个进程的重要性进行提升,至于提升到IMPORTANCE_FOREGROUND_SERVICE还是 IMPORTANCE_FOREGROUND,博主还没有实质性验证,从之前阅读资料来看,即使在Android11后增加IMPORTANCE_FOREGROUND_SERVICE,但是更多使用的依旧是 IMPORTANCE_FOREGROUND

public class AppUtils {
    public static Boolean isAppInBackground(Context context){
        ArrayList<Integer> list = getProcessImportance(context);
        if (list != null && list.size() > 0) {
            for (Integer importance : list) {
                if (importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                    return false;
                }
            }
        }
        return true;
    }

    private static ArrayList<Integer> getProcessImportance(Context context){
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        if (activityManager == null) {
            Log.d("AppUtils", "getProcessImportance: activityManager is null");
            return null;
        }
        ArrayList<Integer> list = new ArrayList<>();
        List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfos =  activityManager.getRunningAppProcesses();
        if (runningAppProcessInfos != null && runningAppProcessInfos.size() > 0) {
            for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : runningAppProcessInfos) {
                if (runningAppProcessInfo.processName.equals(context.getPackageName())) {
                    list.add(runningAppProcessInfo.importance);
                    Log.d("AppUtils",
                            "getProcessImportance: importanceReasonCode=" + runningAppProcessInfo.importanceReasonCode
                    + ", importanceReasonComponent=" + runningAppProcessInfo.importanceReasonComponent);
                }
            }
        }
        Log.d("AppUtils", "getProcessImportance: " + list);
        return list;
    }
}

虽然方法可以用,但是官方还是在getRunningAppProcesses中注释道:

Note: this method is only intended for debugging or building a user-facing process management UI.

这是有一些考虑的,例如通过getRunningAppProcesses能获取的信息可能会包含敏感数据(如当前正在运行的应用列表,具体来看,在Android6以下版本可以获取到其他正在运行的app的信息,但是在Android6及以上版本中已经做了限制,如何限制的:

@GuardedBy("mService")
List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLocked(boolean allUsers,
        int userId, boolean allUids, int callingUid, int clientTargetSdk) {
    // Lazy instantiation of list
    List<ActivityManager.RunningAppProcessInfo> runList = null;
    //非自己的uid就直接跳过了
    for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
        ProcessRecord app = mLruProcesses.get(i);
        if ((!allUsers && app.userId != userId)
                || (!allUids && app.uid != callingUid)) {
            continue;
        }
        。。。省略一些代码。。。
    }
    return runList;
}

那么getRunningAppProcesses既然被质疑有风险了,如何规避呢?其实方法很多,不仅可以通过反射ActivityThread,也可以通过IO直接命令拿到,下面附一种替换方法:

private static String replaceMethod() {
    BufferedReader reader = null;
    try {
        FileInputStream inputStream = new FileInputStream(FILE_SYSTEM + android.os.Process.myPid() + BASE_FILE);
        reader = new BufferedReader(new InputStreamReader(inputStream, CHARSET));
        int c;
        StringBuilder processName = new StringBuilder();
        while ((c = reader.read()) > 0) {
            processName.append((char) c);
        }
        return processName.toString();
    } catch (Throwable e) {
        Log.e(TAG, e.getLocalizedMessage());
    } finally {
        //别忘了关闭呦
        if (reader != null) {
            try {
                reader.close();
            } catch (Throwable e) {
                Log.e(TAG, e.getLocalizedMessage());
            }
        }
    }
    return null;
}

本次方法介绍到此就结束啦,续集请看juejin.cn/post/732159…