Android 取得应用程序的启动次数和运行时间等信息

使用情景:最近有个需求是统计后台应用运行时间,如果一个应用在后台运行超过一定时间就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<PkgUsageStats> {

Map<String, CharSequence> mAppLabelList;

AppNameComparator(Map<String, CharSequence> 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<PkgUsageStats> {

public final int compare(PkgUsageStats a, PkgUsageStats b) {

// return by descending order

return b.launchCount - a.launchCount;

}

}

public static class UsageTimeComparator implements Comparator<PkgUsageStats> {

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<PkgUsageStats> mUsageStats;

private LaunchCountComparator mLaunchCountComparator;

private UsageTimeComparator mUsageTimeComparator;

private AppNameComparator mAppLabelComparator;

private HashMap<String, CharSequence> mAppLabelMap;

UsageStatsAdapter() {

mUsageStats = new ArrayList<PkgUsageStats>();

mAppLabelMap = new HashMap<String, CharSequence>();

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
plain
copy

  1. private void getPkgUsageStats()
  2. {
  3. IUsageStats statsService = (IUsageStats) IUsageStats.Stub.
  4. asInterface(ServiceManager.getService("usagestats"));
  5. PkgUsageStats[] pkgStats = null;
  6. try {
  7. pkgStats = statsService.getAllPkgUsageStats();
  8. } catch (RemoteException e) {
  9. // TODO Auto-generated catch block
  10. e.printStackTrace();
  11. }
  12. if(pkgStats != null)
  13. {
  14. StringBuffer sb = new StringBuffer();
  15. sb.append("nerver used : ");
  16. for(PkgUsageStats usageStats : pkgStats)
  17. {
  18. String packageName = usageStats.packageName;
  19. int launchCount = usageStats.launchCount;
  20. long usageTime = usageStats.usageTime;
  21. if(launchCount > 0)
  22. Log.v("getPkgUsageStats",packageName + "  count: " + launchCount + "  time:  "
  23. + usageTime);
  24. else{
  25. sb.append(packageName+" ");
  26. }
  27. }
  28. Log.v("getPkgUsageStats",sb.toString());
  29. }
  30. }

2、通过反射来调用,代码如下:

[java] view
plain
copy

  1. /**
  2. * Use reflect to get Package Usage Statistics data.<br>
  3. */
  4. public static void getPkgUsageStats() {
  5. LogUtils.d(TAG, "[getPkgUsageStats]");
  6. try {
  7. Class<?> cServiceManager = Class
  8. .forName("android.os.ServiceManager");
  9. Method mGetService = cServiceManager.getMethod("getService",
  10. java.lang.String.class);
  11. Object oRemoteService = mGetService.invoke(null, "usagestats");
  12. // IUsageStats oIUsageStats =
  13. // IUsageStats.Stub.asInterface(oRemoteService)
  14. Class<?> cStub = Class
  15. .forName("com.android.internal.app.IUsageStats$Stub");
  16. Method mUsageStatsService = cStub.getMethod("asInterface",
  17. android.os.IBinder.class);
  18. Object oIUsageStats = mUsageStatsService.invoke(null,
  19. oRemoteService);
  20. // PkgUsageStats[] oPkgUsageStatsArray =
  21. // mUsageStatsService.getAllPkgUsageStats();
  22. Class<?> cIUsageStatus = Class
  23. .forName("com.android.internal.app.IUsageStats");
  24. Method mGetAllPkgUsageStats = cIUsageStatus.getMethod(
  25. "getAllPkgUsageStats", (Class[]) null);
  26. Object[] oPkgUsageStatsArray = (Object[]) mGetAllPkgUsageStats
  27. .invoke(oIUsageStats, (Object[]) null);
  28. LogUtils.d(TAG, "[getPkgUsageStats] oPkgUsageStatsArray = "+oPkgUsageStatsArray);
  29. Class<?> cPkgUsageStats = Class
  30. .forName("com.android.internal.os.PkgUsageStats");
  31. StringBuffer sb = new StringBuffer();
  32. sb.append("nerver used : ");
  33. for (Object pkgUsageStats : oPkgUsageStatsArray) {
  34. // get pkgUsageStats.packageName, pkgUsageStats.launchCount,
  35. // pkgUsageStats.usageTime
  36. String packageName = (String) cPkgUsageStats.getDeclaredField(
  37. "packageName").get(pkgUsageStats);
  38. int launchCount = cPkgUsageStats
  39. .getDeclaredField("launchCount").getInt(pkgUsageStats);
  40. long usageTime = cPkgUsageStats.getDeclaredField("usageTime")
  41. .getLong(pkgUsageStats);
  42. if (launchCount > 0)
  43. LogUtils.d(TAG, "[getPkgUsageStats] "+ packageName + "  count: "
  44. + launchCount + "  time:  " + usageTime);
  45. else {
  46. sb.append(packageName + "; ");
  47. }
  48. }
  49. LogUtils.d(TAG, "[getPkgUsageStats] " + sb.toString());
  50. } catch (IllegalArgumentException e) {
  51. e.printStackTrace();
  52. } catch (IllegalAccessException e) {
  53. e.printStackTrace();
  54. } catch (InvocationTargetException e) {
  55. e.printStackTrace();
  56. } catch (NoSuchFieldException e) {
  57. e.printStackTrace();
  58. } catch (ClassNotFoundException e) {
  59. e.printStackTrace();
  60. } catch (NoSuchMethodException e) {
  61. e.printStackTrace();
  62. }
  63. }

这是获取信息的两种实现方式,另外,要想让程序能够正常运行并成功获取到数据,我们还需要做如下的配置:

1、在AndroidManifest.xml中增加android:sharedUserId="android.uid.system"

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.xxx"

android:versionCode="1"

android:versionName="1.0"

android:sharedUserId="android.uid.system" >

还有权限的声明

<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />

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
plain
copy

  1. public PkgUsageStats[] getAllPkgUsageStats() {
  2. mContext.enforceCallingOrSelfPermission(
  3. android.Manifest.permission.PACKAGE_USAGE_STATS, null);
  4. synchronized (mStatsLock) {
  5. int size = mLastResumeTimes.size();
  6. if (size <= 0) {
  7. return null;
  8. }
  9. PkgUsageStats retArr[] = new PkgUsageStats[size];
  10. int i = 0;
  11. for (Map.Entry<String, Map<String, Long>> entry : mLastResumeTimes.entrySet()) {
  12. String pkg = entry.getKey();
  13. long usageTime = 0;
  14. int launchCount = 0;
  15. PkgUsageStatsExtended pus = mStats.get(pkg);
  16. if (pus != null) {
  17. usageTime = pus.mUsageTime;
  18. launchCount = pus.mLaunchCount;
  19. }
  20. retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime, entry.getValue());
  21. i++;
  22. }
  23. return retArr;
  24. }
  25. }

所以根据以上方法仅能获取开机后被启动过的apk信息集合,那如何获取所有apk的信息集合呢?该Service提供有另一个方法:

public PkgUsageStats getPkgUsageStats(ComponentName componentName)

我们可以先获取当前系统所有安装包包名,再根据包名逐个通过此方法去获取对应包名的启动次数和运行时间。

时间: 2024-10-29 10:45:55

Android 取得应用程序的启动次数和运行时间等信息的相关文章

【Android】应用程序Activity启动过程分析

在Android系统中,有两种操作会引发Activity的启动,一种用户点击应用程序图标时,Launcher会为我们启动应用程序的主Activity:应用程序的默认Activity启动起来后,它又可以在内部通过调用startActvity接口启动新的Activity,依此类推,每一个Activity都可以在内部启动新的Activity.通过这种连锁反应,按需启动Activity,从而完成应用程序的功能. Activity的启动方式有两种,一种是显式的,一种是隐式的,隐式启动可以使得Activit

android在一个应用程序员启动另一个程序

一般我们知道了另一个应用的包名和MainActivity的名字之后便可以直接通过如下代码来启动: Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER);             ComponentName cn = new ComponentName(packageName, className);             intent.setComponent(c

Android - 在一个应用程序中启动另外一个已经安装的应用程序或系统程序(前提是要知道该应用程序的主activity和包名)

//组件名称,第一个参数是应用程序的包名,后一个是这个应用程序的主Activity ComponentName com = new ComponentName("com.antroid.Test", "com.antroid.Test.TestActivity"); Intent  intent = new Intent(); //设置部件 intent.setComponent(com); startActivity(intent); 我们也可以使用下面的代码启动

Android UsageStats:应用根据启动次数、启动时间、应用名称排序

Android 7.1.1 developers/samples/android/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsFragment.java packages/apps/Settings/src/com/android/settings/UsageStatsActivity.java /** * Copyrigh

C# 避免程序重复启动

using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using System.Threading; namespace WindowsFormsApplication1 { static class Program { /// <summary> /// The main entry point for the application. /// </s

Android 并发编程:(一)基础知识 —— 1.2 程序的启动和终结

本章节所有内容皆为原创,如需转载,请注明出处. http://blog.csdn.net/manoel/article/details/38471825 Android是一个多用户,多任务的系统. 允许多个app在同一时刻执行,在多个程序之间切换并不会有明显的延迟. 多任务是由Linux内核负责处理的,而程序的运行基于Linux进程. Linux进程 Linux为每一个用户分配一个唯一的用户ID(User ID),用于区分不同的User. 因为权限的原因,每一个用户只能访问私有资源,没有用户(除

【Android UI设计与开发】第05期:引导界面(五)实现应用程序只启动一次引导界面

[Android UI设计与开发]第05期:引导界面(五)实现应用程序只启动一次引导界面 jingqing 发表于 2013-7-11 14:42:02 浏览(229501) 这篇文章算是对整个引导界面开发专题的一个终结了吧,个人觉得大部分的引导界面基本上都是千篇一律的,只要熟练掌握了一个,基本上也就没什么好说的了,要是在今后的开发中遇到了更好玩,更有趣的引导界面,博主也会在这里及时的跟大家分享,今天的内容主要是教大家的应用程序只有在第一次启动的时候显示引导界面,以后在启动程序的时候就不再显示了

【Android】应用程序启动过程源码分析

在Android系统中,应用程序是由Activity组成的,因此,应用程序的启动过程实际上就是应用程序中的默认Activity的启动过程,本文将详细分析应用程序框架层的源代码,了解Android应用程序的启动过程. 启动Android应用程序中的Activity的两种情景:其中,在手机屏幕中点击应用程序图标的情景就会引发Android应用程序中的默认Activity的启动,从而把应用程序启动起来.这种启动方式的特点是会启动一个新的进程来加载相应的Activity. 这里,我们以这个例子为例来说明

APM之Android应用的启动次数(2)

怎么防止sdk反复启动呢?---->一次启动对于应用来说算一次启动次数 private static boolean started = false; public void start(Context context) //sdk初始化执行的逻辑 { if (started) { //用一个变量防止重复初始化. log.debug("NewRelic is already running."); return; } try { AgentLogManager.setAgentL