使用情景:最近有個需求是統計後台應用運行時間,如果一個應用在後台運行超過一定時間就Kill掉進程,達到省電的目的。此時就可以使用PkgUsageStats這個類來實現啦!
通過com.android.internal.os.PkgUsageStats這個類可以得到一個應用程序的啟動次數,運行時間等信息,功能強大,但是google並沒有將這個類作為API接口提供給開發者,如果在android源碼下開發,可以通過以下代碼來使用這個類:
import com.android.internal.app.IUsageStats;
import com.android.internal.os.PkgUsageStats;
//比較兩個應用程序的啟動次數和運行時間
public final int compare(ApplicationInfo a, ApplicationInfo b) {
ComponentName aName = a.intent.getComponent();
ComponentName bName = b.intent.getComponent();
int result = 0;
// get usagestats service
IUsageStats mUsageStatsService = IUsageStats.Stub
.asInterface(ServiceManager.getService("usagestats"));
try {
// get PkgUsageStats
PkgUsageStats aStats = mUsageStatsService.getPkgUsageStats(aName);
PkgUsageStats bStats = mUsageStatsService.getPkgUsageStats(bName);
if (aStats != null && bStats != null) {
if ((aStats.launchCount > bStats.launchCount)
|| ((aStats.launchCount == bStats.launchCount) && (aStats.usageTime > bStats.usageTime)))
result = -1;
else if ((aStats.launchCount < bStats.launchCount)
|| ((aStats.launchCount == bStats.launchCount) && (aStats.usageTime < bStats.usageTime)))
result = 1;
else {
result = 0;
}
} else if (aStats != null && bStats == null) {
result = -1;
} else if (aStats == null && bStats != null) {
result = 1;
}
} catch (RemoteException e) {
Log.i("TAG", "get package usage stats fail");
}
return result;
}
那麼如果想在sdk中使用這個 類要如果作呢--可以使用反射 的方法,代碼如下:
public final int compare(ApplicationInfo a, ApplicationInfo b) {
ComponentName aName = a.intent.getComponent();
ComponentName bName = b.intent.getComponent();
int aLaunchCount, bLaunchCount;
long aUseTime, bUseTime;
int result = 0;
try {
// 獲得ServiceManager類
Class> ServiceManager = Class
.forName("android.os.ServiceManager");
// 獲得ServiceManager的getService方法
Method getService = ServiceManager.getMethod("getService",
java.lang.String.class);
// 調用getService獲取RemoteService
Object oRemoteService = getService.invoke(null, "usagestats");
// 獲得IUsageStats.Stub類
Class> cStub = Class
.forName("com.android.internal.app.IUsageStats$Stub");
// 獲得asInterface方法
Method asInterface = cStub.getMethod("asInterface",
android.os.IBinder.class);
// 調用asInterface方法獲取IUsageStats對象
Object oIUsageStats = asInterface.invoke(null, oRemoteService);
// 獲得getPkgUsageStats(ComponentName)方法
Method getPkgUsageStats = oIUsageStats.getClass().getMethod(
"getPkgUsageStats", ComponentName.class);
// 調用getPkgUsageStats 獲取PkgUsageStats對象
Object aStats = getPkgUsageStats.invoke(oIUsageStats, aName);
Object bStats = getPkgUsageStats.invoke(oIUsageStats, bName);
// 獲得PkgUsageStats類
Class> PkgUsageStats = Class
.forName("com.android.internal.os.PkgUsageStats");
aLaunchCount = PkgUsageStats.getDeclaredField("launchCount")
.getInt(aStats);
bLaunchCount = PkgUsageStats.getDeclaredField("launchCount")
.getInt(bStats);
aUseTime = PkgUsageStats.getDeclaredField("usageTime").getLong(
aStats);
bUseTime = PkgUsageStats.getDeclaredField("usageTime").getLong(
bStats);
if ((aLaunchCount > bLaunchCount)
|| ((aLaunchCount == bLaunchCount) && (aUseTime > bUseTime)))
result = 1;
else if ((aLaunchCount < bLaunchCount)
|| ((aLaunchCount == bLaunchCount) && (aUseTime < bUseTime)))
result = -1;
else {
result = 0;
}
} catch (Exception e) {
Log.e("###", e.toString(), e);
}
return result;
}
如何看自己的android的詳細使用信息:
我們使用Andoroid手機時想看看自己的手機的使用情況,那麼我們又如何去操作呢?也是必需得像程序這樣要自己寫一個程序才能查看吧,如果用戶不是編程的,那得怎麼辦,呵,其實我們查看自己的手機使用詳情是沒有那麼復雜的,今天androidkaifa.com就會大家說一下如何查詢自己的手機的使用情況,下面是具體的查看方法,
其實查看方法非常簡單,直接進入Android的工程模式即可,操作步驟如下:(筆者的android手機系統是4.2)
1、首先進入Android手機操作系統的撥號界面,直接輸入“*#*#4636#*#*”(不加引號)即可以快速進入Android操作系統的工程測試模式。
2、在“測試”模式菜單中有手機信息“Phone information”、電池信息“Battery information”、WI-FI信息“WI-FI information”、使用狀態“Usage statistics”四個選項。
3、我們點擊選擇第二項“Battery information”進入電池信息,然後就可以看到手機電池的詳細信息了,其中包括電量等級、電池狀態、溫度、電池材質、電壓等等信息。
4: 我們相應點擊其實的選擇就可以看到其實的相應的使用詳細信息
==================================================================================================================
import com.android.internal.app.IUsageStats;
import com.android.settings.R;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import com.android.internal.os.PkgUsageStats;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.AdapterView.OnItemSelectedListener;
/**
* Activity to display package usage statistics.
*/
public class UsageStats extends Activity implements OnItemSelectedListener {
private static final String TAG="UsageStatsActivity";
private static final boolean localLOGV = true;
private Spinner mTypeSpinner;
private ListView mListView;
private IUsageStats mUsageStatsService;
private LayoutInflater mInflater;
private UsageStatsAdapter mAdapter;
private PackageManager mPm;
public static class AppNameComparator implements Comparator
{
Map mAppLabelList;
AppNameComparator(Map appList) {
mAppLabelList = appList;
}
public final int compare(PkgUsageStats a, PkgUsageStats b) {
String alabel = mAppLabelList.get(a.packageName).toString();
String blabel = mAppLabelList.get(b.packageName).toString();
return alabel.compareTo(blabel);
}
}
public static class LaunchCountComparator implements Comparator {
public final int compare(PkgUsageStats a, PkgUsageStats b) {
// return by descending order
return b.launchCount - a.launchCount;
}
}
public static class UsageTimeComparator implements Comparator {
public final int compare(PkgUsageStats a, PkgUsageStats b) {
long ret = a.usageTime-b.usageTime;
if (ret == 0) {
return 0;
}
if (ret < 0) {
return 1;
}
return -1;
}
}
// View Holder used when displaying views
static class AppViewHolder {
TextView pkgName;
TextView launchCount;
TextView usageTime;
}
class UsageStatsAdapter extends BaseAdapter {
// Constants defining order for display order
private static final int _DISPLAY_ORDER_USAGE_TIME = 0;
private static final int _DISPLAY_ORDER_LAUNCH_COUNT = 1;
private static final int _DISPLAY_ORDER_APP_NAME = 2;
private int mDisplayOrder = _DISPLAY_ORDER_USAGE_TIME;
private List mUsageStats;
private LaunchCountComparator mLaunchCountComparator;
private UsageTimeComparator mUsageTimeComparator;
private AppNameComparator mAppLabelComparator;
private HashMap mAppLabelMap;
UsageStatsAdapter() {
mUsageStats = new ArrayList();
mAppLabelMap = new HashMap();
PkgUsageStats[] stats;
try {
stats = mUsageStatsService.getAllPkgUsageStats();
} catch (RemoteException e) {
Log.e(TAG, "Failed initializing usage stats service");
return;
}
if (stats == null) {
return;
}
for (PkgUsageStats ps : stats) {
mUsageStats.add(ps);
// load application labels for each application
CharSequence label;
try {
ApplicationInfo appInfo = mPm.getApplicationInfo(ps.packageName, 0);
label = appInfo.loadLabel(mPm);
} catch (NameNotFoundException e) {
label = ps.packageName;
}
mAppLabelMap.put(ps.packageName, label);
}
// Sort list
mLaunchCountComparator = new LaunchCountComparator();
mUsageTimeComparator = new UsageTimeComparator();
mAppLabelComparator = new AppNameComparator(mAppLabelMap);
sortList();
}
public int getCount() {
return mUsageStats.size();
}
public Object getItem(int position) {
return mUsageStats.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
AppViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.usage_stats_item, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new AppViewHolder();
holder.pkgName = (TextView) convertView.findViewById(R.id.package_name);
holder.launchCount = (TextView) convertView.findViewById(R.id.launch_count);
holder.usageTime = (TextView) convertView.findViewById(R.id.usage_time);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (AppViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder
PkgUsageStats pkgStats = mUsageStats.get(position);
if (pkgStats != null) {
CharSequence label = mAppLabelMap.get(pkgStats.packageName);
holder.pkgName.setText(label);
holder.launchCount.setText(String.valueOf(pkgStats.launchCount));
holder.usageTime.setText(String.valueOf(pkgStats.usageTime)+" ms");
} else {
Log.w(TAG, "No usage stats info for package:" + position);
}
return convertView;
}
void sortList(int sortOrder) {
if (mDisplayOrder == sortOrder) {
// do nothing
return;
}
mDisplayOrder= sortOrder;
sortList();
}
private void sortList() {
if (mDisplayOrder == _DISPLAY_ORDER_USAGE_TIME) {
if (localLOGV) Log.i(TAG, "Sorting by usage time");
Collections.sort(mUsageStats, mUsageTimeComparator);
} else if (mDisplayOrder == _DISPLAY_ORDER_LAUNCH_COUNT) {
if (localLOGV) Log.i(TAG, "Sorting launch count");
Collections.sort(mUsageStats, mLaunchCountComparator);
} else if (mDisplayOrder == _DISPLAY_ORDER_APP_NAME) {
if (localLOGV) Log.i(TAG, "Sorting by application name");
Collections.sort(mUsageStats, mAppLabelComparator);
}
notifyDataSetChanged();
}
}
/** Called when the activity is first created. */
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mUsageStatsService = IUsageStats.Stub.asInterface(ServiceManager.getService("usagestats"));
if (mUsageStatsService == null) {
Log.e(TAG, "Failed to retrieve usagestats service");
return;
}
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mPm = getPackageManager();
setContentView(R.layout.usage_stats);
mTypeSpinner = (Spinner) findViewById(R.id.typeSpinner);
mTypeSpinner.setOnItemSelectedListener(this);
mListView = (ListView) findViewById(R.id.pkg_list);
// Initialize the inflater
mAdapter = new UsageStatsAdapter();
mListView.setAdapter(mAdapter);
}
public void onItemSelected(AdapterView> parent, View view, int position,
long id) {
mAdapter.sortList(position);
}
public void onNothingSelected(AdapterView> parent) {
// do nothing
}
}
================================================================================
來源網絡:
android本身有PkgUsageStats等相關類來統計應用使用情況,但這些類在SDK不公開,只能通過反射或者在源碼環境下才能訪問到。所以,針對這一特點,如果需要獲取應用使用信息,可以采取反射或者源碼下開發這兩種方式。
1、在源碼環境下(源碼環境下可以訪問一些標記為hide的方法),代碼如下:
[java] view
plaincopy
-
private void getPkgUsageStats()
-
{
-
IUsageStats statsService = (IUsageStats) IUsageStats.Stub.
-
asInterface(ServiceManager.getService("usagestats"));
-
-
PkgUsageStats[] pkgStats = null;
-
try {
-
pkgStats = statsService.getAllPkgUsageStats();
-
} catch (RemoteException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
-
if(pkgStats != null)
-
{
-
StringBuffer sb = new StringBuffer();
-
sb.append("nerver used : ");
-
for(PkgUsageStats usageStats : pkgStats)
-
{
-
String packageName = usageStats.packageName;
-
int launchCount = usageStats.launchCount;
-
long usageTime = usageStats.usageTime;
-
if(launchCount > 0)
-
Log.v("getPkgUsageStats",packageName + " count: " + launchCount + " time: "
-
+ usageTime);
-
else{
-
sb.append(packageName+" ");
-
}
-
}
-
-
Log.v("getPkgUsageStats",sb.toString());
-
}
-
}
2、通過反射來調用,代碼如下:
[java] view
plaincopy
-
/**
-
* Use reflect to get Package Usage Statistics data.
-
*/
-
public static void getPkgUsageStats() {
-
LogUtils.d(TAG, "[getPkgUsageStats]");
-
try {
-
Class> cServiceManager = Class
-
.forName("android.os.ServiceManager");
-
Method mGetService = cServiceManager.getMethod("getService",
-
java.lang.String.class);
-
Object oRemoteService = mGetService.invoke(null, "usagestats");
-
-
// IUsageStats oIUsageStats =
-
// IUsageStats.Stub.asInterface(oRemoteService)
-
Class> cStub = Class
-
.forName("com.android.internal.app.IUsageStats$Stub");
-
Method mUsageStatsService = cStub.getMethod("asInterface",
-
android.os.IBinder.class);
-
Object oIUsageStats = mUsageStatsService.invoke(null,
-
oRemoteService);
-
-
// PkgUsageStats[] oPkgUsageStatsArray =
-
// mUsageStatsService.getAllPkgUsageStats();
-
Class> cIUsageStatus = Class
-
.forName("com.android.internal.app.IUsageStats");
-
Method mGetAllPkgUsageStats = cIUsageStatus.getMethod(
-
"getAllPkgUsageStats", (Class[]) null);
-
Object[] oPkgUsageStatsArray = (Object[]) mGetAllPkgUsageStats
-
.invoke(oIUsageStats, (Object[]) null);
-
LogUtils.d(TAG, "[getPkgUsageStats] oPkgUsageStatsArray = "+oPkgUsageStatsArray);
-
-
Class> cPkgUsageStats = Class
-
.forName("com.android.internal.os.PkgUsageStats");
-
-
StringBuffer sb = new StringBuffer();
-
sb.append("nerver used : ");
-
for (Object pkgUsageStats : oPkgUsageStatsArray) {
-
// get pkgUsageStats.packageName, pkgUsageStats.launchCount,
-
// pkgUsageStats.usageTime
-
String packageName = (String) cPkgUsageStats.getDeclaredField(
-
"packageName").get(pkgUsageStats);
-
int launchCount = cPkgUsageStats
-
.getDeclaredField("launchCount").getInt(pkgUsageStats);
-
long usageTime = cPkgUsageStats.getDeclaredField("usageTime")
-
.getLong(pkgUsageStats);
-
if (launchCount > 0)
-
LogUtils.d(TAG, "[getPkgUsageStats] "+ packageName + " count: "
-
+ launchCount + " time: " + usageTime);
-
else {
-
sb.append(packageName + "; ");
-
}
-
}
-
LogUtils.d(TAG, "[getPkgUsageStats] " + sb.toString());
-
} catch (IllegalArgumentException e) {
-
e.printStackTrace();
-
} catch (IllegalAccessException e) {
-
e.printStackTrace();
-
} catch (InvocationTargetException e) {
-
e.printStackTrace();
-
} catch (NoSuchFieldException e) {
-
e.printStackTrace();
-
} catch (ClassNotFoundException e) {
-
e.printStackTrace();
-
} catch (NoSuchMethodException e) {
-
e.printStackTrace();
-
}
-
}
這是獲取信息的兩種實現方式,另外,要想讓程序能夠正常運行並成功獲取到數據,我們還需要做如下的配置:
1、在AndroidManifest.xml中增加android:sharedUserId="android.uid.system"
package="com.xxx"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="android.uid.system" >
還有權限的聲明
2、對apk進行系統簽名,在源碼中取platform.pk8、platform.x509.pem、signapk.jar文件並通過如下命令實現apk的簽名
java -jar signapk.jar platform.x509.pem platform.pk8 unsigned.apk signed.apk
unsigned.apk為簽名之前的apk,signed.apk為通過命令簽名成功的apk
補充:
UsageStats信息通過UsageStatsService保存在路徑data/system/usagestats目錄下,在系統啟動後,UsageStatsService服務開啟,在該Service的構造函數中調用readStatsFromFile()方法從本地獲取UsageStats信息,並保存到mStats成員變量中。(見源碼UsageStatsService.java)
我們通過getAllPkgUsageStats()方法來獲取信息,但是該方法所返回的信息並非從文件中讀取的全部數據,而是開機後啟動過的apk集合,代碼如下:
[java] view
plaincopy
-
public PkgUsageStats[] getAllPkgUsageStats() {
-
mContext.enforceCallingOrSelfPermission(
-
android.Manifest.permission.PACKAGE_USAGE_STATS, null);
-
synchronized (mStatsLock) {
-
int size = mLastResumeTimes.size();
-
if (size <= 0) {
-
return null;
-
}
-
PkgUsageStats retArr[] = new PkgUsageStats[size];
-
int i = 0;
-
for (Map.Entry> entry : mLastResumeTimes.entrySet()) {
-
String pkg = entry.getKey();
-
long usageTime = 0;
-
int launchCount = 0;
-
-
PkgUsageStatsExtended pus = mStats.get(pkg);
-
if (pus != null) {
-
usageTime = pus.mUsageTime;
-
launchCount = pus.mLaunchCount;
-
}
-
retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime, entry.getValue());
-
i++;
-
}
-
return retArr;
-
}
-
}
所以根據以上方法僅能獲取開機後被啟動過的apk信息集合,那如何獲取所有apk的信息集合呢?該Service提供有另一個方法:
public PkgUsageStats getPkgUsageStats(ComponentName componentName)
我們可以先獲取當前系統所有安裝包包名,再根據包名逐個通過此方法去獲取對應包名的啟動次數和運行時間。