Android获取当前运行的App进程

三种方式概述及对比

  • getRunningTasks方法
    在Android5.0以前,系统允许我们通过ActivityManager的getRunningTasks()函数,直接获取当前运行的App。
    这种方法唯一的问题就是过时了,在5.0以上不能使用。
  • USAGE_STATS_SERVICE方法
    从Android5.0以后,系统为了安全起见,开始重重设限,要求我们通过用户手动授权的方式,获得USAGE_STATS_SERVICE的访问权限,才能读到数据。
    但是,这种方式有两个问题:
    1.要求用户手动授权,不能自动运行。
    2.在碎片化系统中表现不一,有的系统无法给出权限,例如Android电视就无法真正获取权限。
  • 系统proc文件夹方法
    原理:Android系统在管理进程时,通过low memory killer机制来定时找出oom_score评分高出阈值的进程,进行回收,那么反过来考虑,oom_score值最低的,且oom_adj值为0的进程(0为前台进程的adj值),就是很可能是当前的前台进程了。
    这种方式可以静默运行,但是也有两个问题:
    1.获取的其实是进程名,默认进程名为App的包名,但是开发者可以自定义进程名,所以你可能拿不到App的包名。
    2.获取的结果并不精准,因为评分最低的不一定是当前显示的App,还有可能是某个进程优先级很高的后台服务,我们可能需要维护一个黑名单,在代码中屏蔽掉这个名单上的所有后台服务。

getRunningTasks

在Android4.X的时代,获取当前运行的App非常简单:

ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = activityManager.getRunningTasks(1).get(0).topActivity;
String pkg = cn.getPackageName();

USAGE_STATS_SERVICE

从Android 5.0LOLLIPOP起,为了提升安全性,系统禁用了getRunningTasks,改为通过USAGE_STATS_SERVICE来获取当前运行的App:

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            UsageStatsManager m = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
            if (m != null) {
                long now = System.currentTimeMillis();
                //获取60秒之内的应用数据
                List stats = m.queryUsageStats(UsageStatsManager.INTERVAL_BEST, now - 60 * 1000, now);
                Log.i(TAG, "Running app number in last 60 seconds : " + stats.size());
                String topActivity = "";
                //取得最近运行的一个app,即当前运行的app
                if ((stats != null) && (!stats.isEmpty())) {
                    int j = 0;
                    for (int i = 0; i < stats.size(); i++) {
                        if (stats.get(i).getLastTimeUsed() > stats.get(j).getLastTimeUsed()) {
                            j = i;
                        }
                        topActivity = stats.get(j).getPackageName();
                        Log.i(TAG, "top running app is : "+topActivity);
                    }

                }
                return stats.size();
            }
        }

这需要用户手动授权,调起系统授权页面“有权查看使用情况的应用”


Android获取当前运行的App进程_第1张图片
系统页面授权

代码如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (!hasPermission()) {
                startActivityForResult(
                        new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS),
                        MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS);
            }
        }
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS) {
            if (!hasPermission()) {
                Toast.makeText(this,"未能开启部分App权限!",Toast.LENGTH_SHORT).show();
            }
        }
    }
    //检测用户是否对本app开启了“Apps with usage access”权限
    private boolean hasPermission() {
        AppOpsManager appOps = (AppOpsManager)
                getSystemService(Context.APP_OPS_SERVICE);
        int mode = 0;
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
            mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
                    android.os.Process.myUid(), getPackageName());
        }
        return mode == AppOpsManager.MODE_ALLOWED;
    }

    private static final int MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS = 1101;

通过系统proc文件夹读取

通过读取系统proc文件夹,找到所有进程列表,然后利用oom_adj文件,判断出当前正在使用的进程。
代码如下:

    //5.0以上,没有usage state权限
    public static final int AID_APP = 10000;
    public static final int AID_USER = 100000;
    /**
     * 5.0以上,没有usage state权限
     * @return
     */
    public static String getForegroundApp() {
        File[] files = new File("/proc").listFiles();
        int lowestOomScore = Integer.MAX_VALUE;
        String foregroundProcess = null;
        for (File file : files) {
            if (!file.isDirectory()) {
                continue;
            }
            int pid;

            try {
                pid = Integer.parseInt(file.getName());
            } catch (NumberFormatException e) {
                continue;
            }

            try {
                String cgroup = read(String.format("/proc/%d/cgroup", pid));
                String[] lines = cgroup.split("\n");
                String cpuSubsystem;
                String cpuaccctSubsystem;

                if (lines.length == 2) {// 有的手机里cgroup包含2行或者3行,我们取cpu和cpuacct两行数据
                    cpuSubsystem = lines[0];
                    cpuaccctSubsystem = lines[1];
                } else if (lines.length == 3) {
                    cpuSubsystem = lines[0];
                    cpuaccctSubsystem = lines[2];
                } else {
                    continue;
                }

                if (!cpuaccctSubsystem.endsWith(Integer.toString(pid))) {
                    // not an application process
                    continue;
                }
                if (cpuSubsystem.endsWith("bg_non_interactive")) {
                    // background policy
                    continue;
                }

                String cmdline = read(String.format("/proc/%d/cmdline", pid));
                //屏蔽掉你自己的其他后台进程
                if(cmdline.contains("com.XXX.xxx")){
                    continue;
                }
                if (cmdline.contains("com.android.systemui")) {
                    continue;
                }
                int uid = Integer.parseInt(cpuaccctSubsystem.split(":")[2]
                        .split("/")[1].replace("uid_", ""));
                if (uid >= 1000 && uid <= 1038) {
                    // system process
                    continue;
                }
                int appId = uid - AID_APP;
                int userId = 0;
                // loop until we get the correct user id.
                // 100000 is the offset for each user.

                while (appId > AID_USER) {
                    appId -= AID_USER;
                    userId++;
                }

                if (appId < 0) {
                    continue;
                }
                // u{user_id}_a{app_id} is used on API 17+ for multiple user
                // account support.
                // String uidName = String.format("u%d_a%d", userId, appId);
                File oomScoreAdj = new File(String.format(
                        "/proc/%d/oom_score_adj", pid));
                if (oomScoreAdj.canRead()) {
                    int oomAdj = Integer.parseInt(read(oomScoreAdj
                            .getAbsolutePath()));
                    if (oomAdj != 0) {
                        continue;
                    }
                }
                int oomscore = Integer.parseInt(read(String.format(
                        "/proc/%d/oom_score", pid)));
                if (oomscore < lowestOomScore) {
                    lowestOomScore = oomscore;
                    foregroundProcess = cmdline;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return foregroundProcess;

    }

    private static String read(String path) throws IOException {
        StringBuilder output = new StringBuilder();
        BufferedReader reader = new BufferedReader(new FileReader(path));
        output.append(reader.readLine());

        for (String line = reader.readLine(); line != null; line = reader
                .readLine()) {
            output.append('\n').append(line);
        }
        reader.close();
        return output.toString().trim();// 不调用trim(),包名后会带有乱码
    }

引用

android5.1+获取当前运行的app(Android5.1-也支持)

你可能感兴趣的:(Android获取当前运行的App进程)