概要:
android L前我们可以使用
getRunningTasks(int maxNum)
return RunningTaskInfo
getRunningAppProcesses()
return RunningAppProcessInfo
maxNum int: The maxNumnumber of entries to return in the list. The actual number returned may be smaller, depending on how many tasks the user has started.
通过PackageManager.getApplicationInfo(pakgName, 0)
来获取对应包名的应用信息,而在lolipop之后,google在android 5.0 系统中收紧了对API的使用权限,导致我们在使用上边两个方法的时候,有可能只会获取到我们自身应用的包名。针对这一情况,网上也出现了很多的解决方案,今天我们就来了解一二。
获取进程方法:
首先还是来介绍下上边提到的两种方法:
1. getRunningTasks(int maxNum)
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// 1是返回应用列表的size,0是获取当前列表第一个RunningTaskInfo
// RunningTaskInfo.topActivity:The activity component at the top of the history stack of the task.
ComponentName cn = activityManager.getRunningTasks(1).get(0).topActivity;
String currentPkgName = cn.getPackageName(); // 当前显示在顶端的包名
当然熟悉的朋友应该了解这里需要添加一个权限
<uses-permission android:name="android.permission.GET_TASKS" />
This method was deprecated in API level 21. As of LOLLIPOP
this method is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks , and possibly some other tasks such as home that are known to not be sensitive.
从官方文档可以看到这个方法在5.0之后已经deprecate了。
2. getRunningAppProcesses()
ActivityManager activityManager = (ActivityManager)context.getApplicationContext().getSystemService( Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
// 如果有需要这里可以遍历当前系统所有的进程
// String pkgName = processInfo.processName;
// ApplicationInfo applicationInfo = PackageManager.getApplicationInfo(pkgName, 0);
// 这里开始获取顶端的进程信息
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
Log.i("top_info", "当前顶端进程的信息");
Log.i("process_pid", "进程id: " + processInfo.pid);
Log.i("proccess_name", "进程名 : " + processInfo.processName);
Log.i("process_pkgs", "该进程下可能存在的包名");
for (String pkgName : processInfo.pkgList) {
Log.i("pkg_names", " " + pkgName);
}
return processInfo.pkgList;
}
}
Returns a list of application processes that are running on the device.
不过在L上获取process还是有一些问题
3. UsageStatsManager
UsageStatsManager是在5.0之后google提供给我们的一个新的API。不过同样的需要用户提供权限。需要我们在获取进程之前,询问是否用户允许了该权限。
- 首先添加manifest权限
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
顺带可以看下怎样引导用户开通对应的权限的问题:
- 判断是否开启了权限
@SuppressLint("NewApi")
public static boolean hasEnable(Context context){
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ // 如果大于等于5.0 再做判断
long ts = System.currentTimeMillis();
UsageStatsManager usageStatsManager=(UsageStatsManager)context.getSystemService(Service.USAGE_STATS_SERVICE);
List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 0, ts);
if (queryUsageStats == null || queryUsageStats.isEmpty()) {
return false;
}
}
return true;
}
- 没有开启权限的跳转
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
intent.setComponent( new ComponentName("com.android.settings", "com.android.settings.Settings$SecuritySettingsActivity"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
顺带提一下,有部分国产厂家的手机系统,在系统安全设置里边是没有有权查看使用情况的应用程序这个选项的。需要注意下。原生系统5.0之后都是有的。
device-2016-11-04-101034.png
- 获取顶端的包名(获取一段时间内系统进程)
if (Build.VERSION.SDK_INT >= 21) {
try {
// 根据最近time_ms毫秒内的应用统计信息进行排序获取当前顶端的包名
long time = System.currentTimeMillis();
UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
// 这里返回了在time_ms时间内系统所有的进程列表
// 如果有获取系统的一段时间之内进程的需要可以打印出每个包名
List<UsageStats> usageStatsList = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, time - time_ms, time);
// 使用queryEvents
// List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 0, time);
// UsageEvents usageEvents = usageStatsManager.queryEvents(isInit ? 0 : time-time_ms, time);
// 这里使用的是usageEvent来获取顶端包名
// String result = "";
// UsageEvents.Event event = new UsageEvents.Event();
// UsageEvents usageEvents = usageStatsManager.queryEvents(time - 500, time);
// while (usageEvents.hasNextEvent()) {
// usageEvents.getNextEvent(event);
// if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
// result = event.getPackageName();
// Log.e("test", "##当前顶端应用包名:" + result);
// }
// }
if (usageStatsList != null && usageStatsList.size() > 0) {
SortedMap<Long, UsageStats> runningTask = new TreeMap<Long, UsageStats>();
for (UsageStats usageStats : usageStatsList) {
// Log.e("pkgName", usageStats.getPackageName)
runningTask.put(usageStats.getLastTimeUsed(), usageStats);
}
if (runningTask.isEmpty()) {
return null;
}
topPackageName = runningTask.get(runningTask.lastKey()).getPackageName();
Log.i("test", "##当前顶端应用包名:" + topPackageName);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
1.使用这个方法之前需要用户允许应用有权查看组件使用情况,然后才有数据返回
2.新的API能完美进行,就是需要添加新的权限和做一下用户引导
3.如果获取的时间设置得太短,并且这个时间内又没有发生改变的话,那么是获取不到最新的顶端的包名情况的
4.最重要的是,最近又发现在一些机器上(国产为主,魅族领头,LG G3在列),是没有允许应用有权查看使用组件的情况的设置页面的
引用自《获取当前顶端包名》作者还列举了很多的方法,这里就不一一详细介绍了。需要的同学可以自行跳转。
3.通过/proc目录进行判断
/proc目录是存放当前系统运行中的一些信息,包括各个进程,cpu,内存,启动时长等,通过读取该目录下的信息,可以获取系统的进程和顶端包名
说到这里就不得不提下jaredrummler/AndroidProcesses这个GitHub上的类库来获取系统的进程。导入也很方便,不需要添加权限,直接将项目的源码复制到自己项目中也可以使用。也是使用了读取/proc目录来判断系统的进程。
有需要的同学可以看下该类库作者在StackOverflow上的回复。这里只是简要的贴了下代码
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Parcel;import android.os.Parcelable;
import java.util.ArrayList;import java.util.List;
import eu.chainfire.libsuperuser.Shell;
// @author Jared Rummler
public class ProcessManager {
private static final String TAG = "ProcessManager";
private static final String APP_ID_PATTERN;
static {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// Android 4.2 (JB-MR1) changed the UID name of apps for multiple user account support.
APP_ID_PATTERN = "u\\d+_a\\d+";
} else {
APP_ID_PATTERN = "app_\\d+";
}
}
public static List<Process> getRunningProcesses() {
List<Process> processes = new ArrayList<>();
List<String> stdout = Shell.SH.run("toolbox ps -p -P -x -c");
for (String line : stdout) {
try {
processes.add(new Process(line));
} catch (Exception e) {
android.util.Log.d(TAG, "Failed parsing line " + line);
}
}
return processes;
}
public static List<Process> getRunningApps() {
List<Process> processes = new ArrayList<>();
List<String> stdout = Shell.SH.run("toolbox ps -p -P -x -c");
int myPid = android.os.Process.myPid();
for (String line : stdout) {
try {
Process process = new Process(line);
if (process.user.matches(APP_ID_PATTERN)) {
if (process.ppid == myPid || process.name.equals("toolbox")) {
// skip the processes we created to get the running apps.
continue;
}
processes.add(process);
}
} catch (Exception e) {
android.util.Log.d(TAG, "Failed parsing line " + line);
}
}
return processes;
}
public static class Process implements Parcelable {
/** User name */
public final String user;
/** User ID */
public final int uid;
/** Processes ID */
public final int pid;
/** Parent processes ID */
public final int ppid;
/** virtual memory size of the process in KiB (1024-byte units). */
public final long vsize;
/** resident set size, the non-swapped physical memory that a task has used (in kiloBytes). */
public final long rss; public final int cpu;
/** The priority */
public final int priority;
/** The priority, <a href="https://en.wikipedia.org/wiki/Nice_(Unix)">niceness</a> level */
public final int niceness;
/** Real time priority */
public final int realTimePriority;
/** 0 (sched_other), 1 (sched_fifo), and 2 (sched_rr). */
public final int schedulingPolicy;
/** The scheduling policy. Either "bg", "fg", "un", "er", or "" */
public final String policy;
/** address of the kernel function where the process is sleeping */
public final String wchan; public final String pc;
/** * Possible states: *
<p/> * "D" uninterruptible sleep (usually IO) *
<p/> * "R" running or runnable (on run queue) *
<p/> * "S" interruptible sleep (waiting for an event to complete) *
<p/> * "T" stopped, either by a job control signal or because it is being traced *
<p/> * "W" paging (not valid since the 2.6.xx kernel) *
<p/> * "X" dead (should never be seen) *
</p> * "Z" defunct ("zombie") process, terminated but not reaped by its parent */
public final String state;
/** The process name */
public final String name;
/** user time in milliseconds */
public final long userTime;
/** system time in milliseconds */
public final long systemTime;
// Much dirty. Much ugly.
private Process(String line) throws Exception {
String[] fields = line.split("\\s+");
user = fields[0];
uid = android.os.Process.getUidForName(user);
pid = Integer.parseInt(fields[1]);
ppid = Integer.parseInt(fields[2]);
vsize = Integer.parseInt(fields[3]) * 1024;
rss = Integer.parseInt(fields[4]) * 1024;
cpu = Integer.parseInt(fields[5]);
priority = Integer.parseInt(fields[6]);
niceness = Integer.parseInt(fields[7]);
realTimePriority = Integer.parseInt(fields[8]);
schedulingPolicy = Integer.parseInt(fields[9]);
if (fields.length == 16) {
policy = "";
wchan = fields[10];
pc = fields[11];
state = fields[12];
name = fields[13];
userTime = Integer.parseInt(fields[14].split(":")[1].replace(",", "")) * 1000;
systemTime = Integer.parseInt(fields[15].split(":")[1].replace(")", "")) * 1000;
} else {
policy = fields[10];
wchan = fields[11];
pc = fields[12];
state = fields[13];
name = fields[14];
userTime = Integer.parseInt(fields[15].split(":")[1].replace(",", "")) * 1000;
systemTime = Integer.parseInt(fields[16].split(":")[1].replace(")", "")) * 1000;
}
}
private Process(Parcel in) {
user = in.readString();
uid = in.readInt();
pid = in.readInt();
ppid = in.readInt();
vsize = in.readLong();
rss = in.readLong();
cpu = in.readInt();
priority = in.readInt();
niceness = in.readInt();
realTimePriority = in.readInt();
schedulingPolicy = in.readInt();
policy = in.readString();
wchan = in.readString();
pc = in.readString();
state = in.readString();
name = in.readString();
userTime = in.readLong();
systemTime = in.readLong();
}
public String getPackageName() {
if (!user.matches(APP_ID_PATTERN)) {
// this process is not an application
return null;
} else if (name.contains(":")) {
// background service running in another process than the main app process
return name.split(":")[0];
}
return name;
}
public PackageInfo getPackageInfo(Context context, int flags) throws PackageManager.NameNotFoundException {
String packageName = getPackageName();
if (packageName == null) {
throw new PackageManager.NameNotFoundException(name + " is not an application process");
}
return context.getPackageManager().getPackageInfo(packageName, flags);
}
public ApplicationInfo getApplicationInfo(Context context, int flags) throws PackageManager.NameNotFoundException {
String packageName = getPackageName();
if (packageName == null) {
throw new PackageManager.NameNotFoundException(name + " is not an application process");
}
return context.getPackageManager().getApplicationInfo(packageName, flags);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(user);
dest.writeInt(uid);
dest.writeInt(pid);
dest.writeInt(ppid);
dest.writeLong(vsize);
dest.writeLong(rss);
dest.writeInt(cpu);
dest.writeInt(priority);
dest.writeInt(niceness);
dest.writeInt(realTimePriority);
dest.writeInt(schedulingPolicy);
dest.writeString(policy);
dest.writeString(wchan);
dest.writeString(pc);
dest.writeString(state);
dest.writeString(name);
dest.writeLong(userTime);
dest.writeLong(systemTime);
}
public static final Creator<Process> CREATOR = new Creator<Process>() {
public Process createFromParcel(Parcel source) {
return new Process(source);
}
public Process[] newArray(int size) {
return new Process[size];
}
};
}
}
Just copy the class into your project if you wish to use it. You will also need to add libsuperuser as a dependency to your build.gradle file:
compile 'eu.chainfire:libsuperuser:1.0.0.+'
作者附带了使用的demo
new AsyncTask<Void, Void, List<ProcessManager.Process>>() {
long startTime;
@Override
protected List<ProcessManager.Process> doInBackground(Void... params) {
startTime = System.currentTimeMillis();
return ProcessManager.getRunningApps();
}
@Override
protected void onPostExecute(List<ProcessManager.Process> processes) {
StringBuilder sb = new StringBuilder();
sb.append("Execution time: ").append(System.currentTimeMillis() - startTime).append("ms\n");
sb.append("Running apps:\n");
for (ProcessManager.Process process : processes) {
sb.append('\n').append(process.name);
}
new AlertDialog.Builder(MainActivity.this).setMessage(sb.toString()).show();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
使用jaredrummler/AndroidProcess获取系统进程
// 当前的系统版本超过5.0
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// import com.xxxx.processmanager.ProcessManager;
// import com.xxxx.processmanager.model.AndroidAppProcess;
// 这里我是直接把项目的包复制到自己项目中使用
List<AndroidAppProcess> RunningAppInfos = ProcessManager.getRunningAppProcesses();
for (AndroidAppProcess AppInfo : RunningAppInfos) {
String pkgName = AppInfo.name;
getAppInfoByPkgName(pkgName, processInfoList);
}
} else {
List<ActivityManager.RunningAppProcessInfo> RunningAppInfos = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo appInfo : RunningAppInfos) {
String pkgName = appInfo.processName;
getAppInfoByPkgName(pkgName, processInfoList);
}
}
/** * 通过包名来获取应用的详细信息
* * @param pkgName
* @param processInfoList
*/
private void getAppInfoByPkgName(String pkgName, List<ProcessInfo> processInfoList) {
PackageManager pm = getPackageManager();
ProcessInfo processInfo = new ProcessInfo();
// 通过使用包名来获取对应应用的详细信息
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(pkgName, 0);
// 获取应用名字
String appName = applicationInfo.loadLabel(pm).toString();
processInfo.setName(appName);
// 获取应用的图标
Drawable icon = applicationInfo.loadIcon(pm);
processInfo.setIcon(icon);
if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
// 用户进程
processInfo.setUser(true);
} else {
// 系统进程
processInfo.setUser(false);
}
processInfo.setPkgName(pkgName);
processInfoList.add(processInfo);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
// 系统内核进程 没有名称
processInfo.setPkgName(pkgName);
processInfo.setName("系统内核进程");
processInfo.setUser(false);
processInfoList.add(processInfo);
}
// 排序
Collections.sort(processInfoList);
// for (ProcessInfo info : processInfoList) {
// Log.e("appName", info.getName());
// Log.e("isSys", info.isUser() + "");
// }
}
接下来是ProcessInfo 类(实现了排序功能)
import android.graphics.drawable.Drawable;
/** * Created by Admin on 2016/11/2.
* description list process info of search result
*/
public class ProcessInfo implements Comparable<ProcessInfo> {
private String name;
private String pkgName;
private int pid;
private Drawable icon;
public boolean isUser() {
return isUser;
}
public void setUser(boolean user) {
isUser = user;
}
private boolean isUser;// sys = false;
public Drawable getIcon() {
return icon;
}
public void setIcon(Drawable icon) {
this.icon = icon;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPkgName() {
return pkgName;
}
public void setPkgName(String pkgName) {
this.pkgName = pkgName;
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
@Override
public int compareTo(ProcessInfo processInfo) {
if (isUser == true && processInfo.isUser == false) {
return -1;
} else if (isUser == true && processInfo.isUser == true) {
return 0;
} else {
return 1;
}
}
}
到此处基本可以完成获取系统进程。
网友评论