1、UsageStatsService作用是什么?
这是一个Android私有service,主要作用是收集用户使用每一个APP的频率、使用时常;
2、如何通过UsageStatsService获取用户使用APP的数据?
(1)必须要具备系统权限;(APP内置在/system/app下)
(2)必须要在manifest中申明权限:PACKAGE_USAGE_STATS;例如:
(3)调用UsageStatsService.getAllPkgUsageStats or UsageStatsService.getPkgUsageStats 接口获取用户使用APP频率:
//相当于:IBinder oRemoteService = ServiceManager.getService("usagestats");
Class> cServiceManager = Class.forName("android.os.ServiceManager");
Method mGetService = cServiceManager.getMethod("getService", java.lang.String.class);
Object oRemoteService = mGetService.invoke(null, "usagestats");
// 相当于:IUsageStats mUsageStatsService = 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);
//相当于
//for (PkgUsageStats pkgUsageStats: oPkgUsageStatsArray)
//{
// 当前APP的包名:
// packageName = pkgUsageStats.packageName
// 当前APP的启动次数
// launchCount = pkgUsageStats.launchCount
// 当前APP的累计使用时间:
// usageTime = pkgUsageStats.usageTime
// 当前APP的每个Activity的最后启动时间
// componentResumeTimes = pkgUsageStats.componentResumeTimes
//}
Class> cPkgUsageStats = Class.forName("com.android.internal.os.PkgUsageStats");
for (Object pkgUsageStats : oPkgUsageStatsArray) {
String packageName = (String) cPkgUsageStats.getDeclaredField("packageName").get(pkgUsageStats);
int launchCount = cPkgUsageStats.getDeclaredField("launchCount").getInt(pkgUsageStats);
long usageTime = cPkgUsageStats.getDeclaredField("usageTime").getLong(pkgUsageStats);
Map componentResumeMap = (Map) cPkgUsageStats.getDeclaredField("componentResumeTimes").get(pkgUsageStats);
}
//初始化/data/system/usagestats/usage-history.xml
mHistoryFile = new AtomicFile(new File(mDir, FILE_HISTORY));
//解析xml文件,将xml解析成为数据,并存储
readStatsFromFile();
//指定文件前缀为usage,根据日志生成文件后缀,例如usage-20140817
mFileLeaf = getCurrentDateStr(FILE_PREFIX);
//解析文件,为文件流的格式,直接读取即可
readStatsFromFile();
// key为包名,value为Map,记录该包下的Activity名以及该Activity最后启动时间
final private Map> mLastResumeTimes;
// String为包名,PkgUsageStatsExtended存储相应的启动信息
final private Map mStats;
private class PkgUsageStatsExtended {
//每个Activity最后启动时间
final HashMap mLaunchTimes
= new HashMap();
//当前APP启动次数
int mLaunchCount;
//当前APP使用时间
long mUsageTime;
//当前APP最后一次调用onPause时间
long mPausedTime;
//当前APP最后一个调用onResume时间
long mResumedTime;
//更新mResumedTime为当前时间
void updateResume(String comp, boolean launched) {
if (launched) {
mLaunchCount ++;
}
mResumedTime = SystemClock.elapsedRealtime();
}
//更新mPausedTime为当前时间,并且使用时常=mPausedTime - mResumedTime
void updatePause() {
mPausedTime = SystemClock.elapsedRealtime();
mUsageTime += (mPausedTime - mResumedTime);
}
//记录activity次数
void addLaunchCount(String comp) {
TimeStats times = mLaunchTimes.get(comp);
if (times == null) {
times = new TimeStats();
mLaunchTimes.put(comp, times);
}
times.incCount();
}
//记录activity启动时间
void addLaunchTime(String comp, int millis) {
TimeStats times = mLaunchTimes.get(comp);
if (times == null) {
times = new TimeStats();
mLaunchTimes.put(comp, times);
}
times.add(millis);
}
/*more code
*/
}
public void noteResumeComponent(ComponentName componentName) {
enforceCallingPermission();
String pkgName;
synchronized (mStatsLock) {
/*some code
*/
final boolean samePackage = pkgName.equals(mLastResumedPkg);
//1、mIsResumed会在onResume中变为true,在onPause中变为false
if (mIsResumed) {
if (mLastResumedPkg != null) {
//2、这里是为了避免没有调用onPause的情况出现,理论上不存在
PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
if (pus != null) {
//3、增加保护,调用一次updatePause
pus.updatePause();
}
}
}
final boolean sameComp = samePackage
&& componentName.getClassName().equals(mLastResumedComp);
//5、内部数据更新,记录最后一次启动的Activity
mIsResumed = true;
mLastResumedPkg = pkgName;
mLastResumedComp = componentName.getClassName();
PkgUsageStatsExtended pus = mStats.get(pkgName);
if (pus == null) {
pus = new PkgUsageStatsExtended();
mStats.put(pkgName, pus);
}
//6、更新Activity启动时间,如果用户是从一个App启动进入另外一个APP,那么需要App标识启动次数+1
pus.updateResume(mLastResumedComp, !samePackage);
if (!sameComp) {
//7、同上,Activity启动次数+1
pus.addLaunchCount(mLastResumedComp);
}
Map componentResumeTimes = mLastResumeTimes.get(pkgName);
if (componentResumeTimes == null) {
componentResumeTimes = new HashMap();
mLastResumeTimes.put(pkgName, componentResumeTimes);
}
//8、更新componentResumeTimes
componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
}
}
public void notePauseComponent(ComponentName componentName) {
/*some code
*/
synchronized (mStatsLock) {
/*some code
*/
//1、mIsResumed会在onResume中变为true,在onPause中变为false
mIsResumed = false;
PkgUsageStatsExtended pus = mStats.get(pkgName);
if (pus == null) {
// Weird some error here
Slog.i(TAG, "No package stats for pkg:"+pkgName);
return;
}
//2、更新onPause的时间
pus.updatePause();
}
//3、视情况而定确认是否需要将内存数据保存成文件
writeStatsToFile(false, false);
}
/**
* 在特定条件下,将mStats或者mLastResumeTimes写入到文件中,由用户操作触发
* @params force 强制将mStats实例化到文件中
*
* @params forceWriteHistoryStats 强制将mLastResumeTimes实例化到文件中
*/
private void writeStatsToFile(final boolean force, final boolean forceWriteHistoryStats) {
}