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