概要:
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.
通過(guò)PackageManager.getApplicationInfo(pakgName, 0)來(lái)獲取對(duì)應(yīng)包名的應(yīng)用信息,而在lolipop之后,google在android 5.0 系統(tǒng)中收緊了對(duì)API的使用權(quán)限,導(dǎo)致我們?cè)谑褂蒙线厓蓚€(gè)方法的時(shí)候,有可能只會(huì)獲取到我們自身應(yīng)用的包名。針對(duì)這一情況,網(wǎng)上也出現(xiàn)了很多的解決方案,今天我們就來(lái)了解一二。
獲取進(jìn)程方法:
首先還是來(lái)介紹下上邊提到的兩種方法:
1. getRunningTasks(int maxNum)
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// 1是返回應(yīng)用列表的size,0是獲取當(dāng)前列表第一個(gè)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(); // 當(dāng)前顯示在頂端的包名
當(dāng)然熟悉的朋友應(yīng)該了解這里需要添加一個(gè)權(quán)限
<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.
從官方文檔可以看到這個(gè)方法在5.0之后已經(jīng)deprecate了。
2. getRunningAppProcesses()
ActivityManager activityManager = (ActivityManager)context.getApplicationContext().getSystemService( Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
// 如果有需要這里可以遍歷當(dāng)前系統(tǒng)所有的進(jìn)程
// String pkgName = processInfo.processName;
// ApplicationInfo applicationInfo = PackageManager.getApplicationInfo(pkgName, 0);
// 這里開始獲取頂端的進(jìn)程信息
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
Log.i("top_info", "當(dāng)前頂端進(jìn)程的信息");
Log.i("process_pid", "進(jìn)程id: " + processInfo.pid);
Log.i("proccess_name", "進(jìn)程名 : " + processInfo.processName);
Log.i("process_pkgs", "該進(jìn)程下可能存在的包名");
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.
不過(guò)在L上獲取process還是有一些問(wèn)題
3. UsageStatsManager
UsageStatsManager是在5.0之后google提供給我們的一個(gè)新的API。不過(guò)同樣的需要用戶提供權(quán)限。需要我們?cè)讷@取進(jìn)程之前,詢問(wèn)是否用戶允許了該權(quán)限。
- 首先添加manifest權(quán)限
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
順帶可以看下怎樣引導(dǎo)用戶開通對(duì)應(yīng)的權(quán)限的問(wèn)題:
- 判斷是否開啟了權(quán)限
@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;
}
- 沒(méi)有開啟權(quán)限的跳轉(zhuǎn)
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);
順帶提一下,有部分國(guó)產(chǎn)廠家的手機(jī)系統(tǒng),在系統(tǒng)安全設(shè)置里邊是沒(méi)有有權(quán)查看使用情況的應(yīng)用程序這個(gè)選項(xiàng)的。需要注意下。原生系統(tǒng)5.0之后都是有的。
device-2016-11-04-101034.png
- 獲取頂端的包名(獲取一段時(shí)間內(nèi)系統(tǒng)進(jìn)程)
if (Build.VERSION.SDK_INT >= 21) {
try {
// 根據(jù)最近time_ms毫秒內(nèi)的應(yīng)用統(tǒng)計(jì)信息進(jìn)行排序獲取當(dāng)前頂端的包名
long time = System.currentTimeMillis();
UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
// 這里返回了在time_ms時(shí)間內(nèi)系統(tǒng)所有的進(jìn)程列表
// 如果有獲取系統(tǒng)的一段時(shí)間之內(nèi)進(jìn)程的需要可以打印出每個(gè)包名
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來(lái)獲取頂端包名
// 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", "##當(dāng)前頂端應(yīng)用包名:" + 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", "##當(dāng)前頂端應(yīng)用包名:" + topPackageName);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
1.使用這個(gè)方法之前需要用戶允許應(yīng)用有權(quán)查看組件使用情況,然后才有數(shù)據(jù)返回
2.新的API能完美進(jìn)行,就是需要添加新的權(quán)限和做一下用戶引導(dǎo)
3.如果獲取的時(shí)間設(shè)置得太短,并且這個(gè)時(shí)間內(nèi)又沒(méi)有發(fā)生改變的話,那么是獲取不到最新的頂端的包名情況的
4.最重要的是,最近又發(fā)現(xiàn)在一些機(jī)器上(國(guó)產(chǎn)為主,魅族領(lǐng)頭,LG G3在列),是沒(méi)有允許應(yīng)用有權(quán)查看使用組件的情況的設(shè)置頁(yè)面的
引用自《獲取當(dāng)前頂端包名》作者還列舉了很多的方法,這里就不一一詳細(xì)介紹了。需要的同學(xué)可以自行跳轉(zhuǎn)。
3.通過(guò)/proc目錄進(jìn)行判斷
/proc目錄是存放當(dāng)前系統(tǒng)運(yùn)行中的一些信息,包括各個(gè)進(jìn)程,cpu,內(nèi)存,啟動(dòng)時(shí)長(zhǎng)等,通過(guò)讀取該目錄下的信息,可以獲取系統(tǒng)的進(jìn)程和頂端包名
說(shuō)到這里就不得不提下jaredrummler/AndroidProcesses這個(gè)GitHub上的類庫(kù)來(lái)獲取系統(tǒng)的進(jìn)程。導(dǎo)入也很方便,不需要添加權(quán)限,直接將項(xiàng)目的源碼復(fù)制到自己項(xiàng)目中也可以使用。也是使用了讀取/proc目錄來(lái)判斷系統(tǒng)的進(jìn)程。
有需要的同學(xué)可以看下該類庫(kù)作者在StackOverflow上的回復(fù)。這里只是簡(jiǎn)要的貼了下代碼
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 >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獲取系統(tǒng)進(jìn)程
// 當(dāng)前的系統(tǒng)版本超過(guò)5.0
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// import com.xxxx.processmanager.ProcessManager;
// import com.xxxx.processmanager.model.AndroidAppProcess;
// 這里我是直接把項(xiàng)目的包復(fù)制到自己項(xiàng)目中使用
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);
}
}
/** * 通過(guò)包名來(lái)獲取應(yīng)用的詳細(xì)信息
* * @param pkgName
* @param processInfoList
*/
private void getAppInfoByPkgName(String pkgName, List<ProcessInfo> processInfoList) {
PackageManager pm = getPackageManager();
ProcessInfo processInfo = new ProcessInfo();
// 通過(guò)使用包名來(lái)獲取對(duì)應(yīng)應(yīng)用的詳細(xì)信息
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(pkgName, 0);
// 獲取應(yīng)用名字
String appName = applicationInfo.loadLabel(pm).toString();
processInfo.setName(appName);
// 獲取應(yīng)用的圖標(biāo)
Drawable icon = applicationInfo.loadIcon(pm);
processInfo.setIcon(icon);
if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
// 用戶進(jìn)程
processInfo.setUser(true);
} else {
// 系統(tǒng)進(jìn)程
processInfo.setUser(false);
}
processInfo.setPkgName(pkgName);
processInfoList.add(processInfo);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
// 系統(tǒng)內(nèi)核進(jìn)程 沒(méi)有名稱
processInfo.setPkgName(pkgName);
processInfo.setName("系統(tǒng)內(nèi)核進(jìn)程");
processInfo.setUser(false);
processInfoList.add(processInfo);
}
// 排序
Collections.sort(processInfoList);
// for (ProcessInfo info : processInfoList) {
// Log.e("appName", info.getName());
// Log.e("isSys", info.isUser() + "");
// }
}
接下來(lái)是ProcessInfo 類(實(shí)現(xiàn)了排序功能)
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;
}
}
}
到此處基本可以完成獲取系統(tǒng)進(jìn)程。
