android本身有PkgUsageStats等相关类来统计应用使用情况,但这些类在SDK不公开,只能通过反射或者在源码环境下才能访问到。所以,针对这一特点,如果需要获取应用使用信息,可以采取反射或者源码下开发这两种方式。
1、在源码环境下(源码环境下可以访问一些标记为hide的方法),代码如下:
2、通过反射来调用,代码如下:
这是获取信息的两种实现方式,另外,要想让程序能够正常运行并成功获取到数据,我们还需要做如下的配置:
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
1、在源码环境下(源码环境下可以访问一些标记为hide的方法),代码如下:
- 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、通过反射来调用,代码如下:
- /**
- * Use reflect to get Package Usage Statistics data.<br>
- */
- 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"
<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集合,代码如下:
- 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<String, Map<String, Long>> 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)
我们可以先获取当前系统所有安装包包名,再根据包名逐个通过此方法去获取对应包名的启动次数和运行时间。