Android系統(tǒng)中獲取進(jìn)程(和頂端包名)

概要:

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)限。

  1. 首先添加manifest權(quán)限
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" 
tools:ignore="ProtectedPermissions" />

順帶可以看下怎樣引導(dǎo)用戶開通對(duì)應(yīng)的權(quán)限的問(wèn)題:

  1. 判斷是否開啟了權(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;
    }
  1. 沒(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

  1. 獲取頂端的包名(獲取一段時(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)程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問(wèn)題, 分享了一些自己做題目的經(jīng)驗(yàn)。 張土汪:刷leetcod...
    土汪閱讀 12,891評(píng)論 0 33
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,516評(píng)論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 34,628評(píng)論 18 399
  • 一個(gè)人最好的生活狀態(tài),有喜歡的一件事做,有愛(ài)的人,也有愛(ài)你的人,能養(yǎng)自己的家,父母健康,朋友不多但死黨有幾個(gè),你好...
    篤學(xué)青衿閱讀 135評(píng)論 0 0
  • 茶從離開茶樹那一刻起,就期待著與水相逢;水喚醒茶,茶成就水;水包容茶,茶豐富水;茶因水而重生,水因茶而清香。 ...
    馬榮軍閱讀 440評(píng)論 0 1

友情鏈接更多精彩內(nèi)容