(Android 9.0)应用使用数据统计服务——UsageStatsManager

前言

Android5.0以前,使用ActivityManager的getRunningTasks()方法,可以得到应用包名和Activity;Android5.0以后,可以通过UsageStatsManager.queryUsageStats方法替代,但是也只能得到应用包名。当然你也可以通过AccessibilityService,也可以得到应用包名和Activity。正好最近用到UsageStatsManager来实现获取顶部应用的功能,写篇文章系统的学习一下。

相关知识

在介绍UsageStatsManager之前,先学习一下相关联的一些知识。

UsageStats

UsageStats是在指定时间区间内某个应用使用统计数据的封装类。包含的公开方法及对应的作用如下:

方法 用途
getFirstTimeStamp() 获取指定时间区间内应用第一次使用时间戳
getLastTimeStamp() 获取指定时间区间内应用最后一次使用时间戳
getLastTimeUsed() 获取应用最后一次使用时间戳
getPackageName() 获取应用包名
getTotalTimeInForeground() 获取应用在前台的时间

EventStats

EventStats是在指定时间区间内某个类型事件统计数据的封装类。包含的公开方法及对应的作用如下:

方法 用途
getCount() 获取在指定时间区间内事件发生的次数
getEventType() 获取事件类型
getFirstTimeStamp() 获取指定时间区间内这个事件第一次发生的时间戳
getLastEventTime() 获取这个事件最后一次发生的时间戳
getLastTimeStamp() 获取指定时间区间内这个事件最后一次发生的时间戳
getTotalTime() 获取这个事件总共发生的次数

UsageEvents

UsageEvents是用来返回指定时间区间内组件状态变化事件数据的封装类,其返回的组件状态变化事件类型如下:

UsageEvents.Event:
        public static final int NONE = 0;
        public static final int MOVE_TO_FOREGROUND = 1;
        public static final int MOVE_TO_BACKGROUND = 2;
        public static final int END_OF_DAY = 3;
        public static final int CONTINUE_PREVIOUS_DAY = 4;
        public static final int CONFIGURATION_CHANGE = 5;
        public static final int SYSTEM_INTERACTION = 6;
        public static final int USER_INTERACTION = 7;
        public static final int SHORTCUT_INVOCATION = 8;
        public static final int CHOOSER_ACTION = 9;
        public static final int NOTIFICATION_SEEN = 10;
        public static final int STANDBY_BUCKET_CHANGED = 11;
        public static final int NOTIFICATION_INTERRUPTION = 12;
        public static final int SLICE_PINNED_PRIV = 13;
        public static final int SLICE_PINNED = 14;
        public static final int SCREEN_INTERACTIVE = 15;
        public static final int SCREEN_NON_INTERACTIVE = 16;
        public static final int KEYGUARD_SHOWN = 17;
        public static final int KEYGUARD_HIDDEN = 18;

UsageStatsManager

UsageStatsManager 是Android提供统计应用使用情况的服务。通过这个服务可以获取指定时间区间内应用使用统计数据、组件状态变化事件统计数据以及硬件配置信息统计数据。提供的主要查询方法如下表:

方法 用途
queryAndAggregateUsageStats(long beginTime, long endTime) 获取指定时间区间内使用统计数据,以应用包名为键值进行数据合并。
queryConfigurations(int intervalType, long beginTime, long endTime) 获取指定时间区间内硬件配置信息统计数据。
queryEventStats(int intervalType, long beginTime, long endTime) 获取指定时间区间内发生组件状态变化事件统计数据。
queryEvents(long beginTime, long endTime) 获取指定时间区间内组件状态变化事件
queryEventsForSelf(long beginTime, long endTime) 与queryEvents相似,获取指定时间区间内本应用的组件状态变化事件
queryUsageStats(int intervalType, long beginTime, long endTime) 获取指定时间区间内应用使用统计数据。

查询时间间隔如下:

    public static final int INTERVAL_DAILY = 0;
    public static final int INTERVAL_WEEKLY = 1;
    public static final int INTERVAL_MONTHLY = 2;
    public static final int INTERVAL_YEARLY = 3;
    public static final int INTERVAL_BEST = 4;

除了上述查询方法,UsageStatsManager还提供了一些其他的重要方法。

方法 用途
isAppInactive(String packageName) 判断应用是否处于活跃状态
setAppInactive(String packageName, boolean inactive) 设置应用活跃状态,系统API
registerAppUsageObserver(int observerId, @NonNull String[] packages, long timeLimit,@NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) 注册应用使用观察者,系统API
unregisterAppUsageObserver(int observerId) 注销应用使用观察者,系统API

获取顶部应用示例

使用步骤如下:
1、在AndroidManifest.xml中声明权限。

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

2、启动授权设置界面

    public static void checkUsageStateAccessPermission(Context context) {
        if(!AppUsageUtil.checkAppUsagePermission(context)) {
            AppUsageUtil.requestAppUsagePermission(context);
        }
    }

    public static boolean checkAppUsagePermission(Context context) {
        UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
        if(usageStatsManager == null) {
            return false;
        }
        long currentTime = System.currentTimeMillis();
        // try to get app usage state in last 1 min
        List<UsageStats> stats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, currentTime - 60 * 1000, currentTime);
        if (stats.size() == 0) {
            return false;
        }

        return true;
    }

    public static void requestAppUsagePermission(Context context) {
        Intent intent = new Intent(android.provider.Settings.ACTION_USAGE_ACCESS_SETTINGS);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            context.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            Log.i(TAG,"Start usage access settings activity fail!");
        }
    }

3、查询10秒钟前应用使用统计数据并根据最后使用时间进行排序,得到的最后使用的应用。

    public static String getTopActivityPackageName(@NonNull Context context) {
        final UsageStatsManager usageStatsManager = (UsageStatsManager)context.getSystemService(Context.USAGE_STATS_SERVICE);
        if(usageStatsManager == null) {
            return PACKAGE_NAME_UNKNOWN;
        }

        String topActivityPackageName = PACKAGE_NAME_UNKNOWN;
        long time = System.currentTimeMillis();
        // 查询最后十秒钟使用应用统计数据
        List<UsageStats> usageStatsList = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000*10, time);
        // 以最后使用时间为标准进行排序
        if(usageStatsList != null) {
            SortedMap<Long,UsageStats> sortedMap = new TreeMap<Long,UsageStats>();
            for (UsageStats usageStats : usageStatsList) {
                sortedMap.put(usageStats.getLastTimeUsed(),usageStats);
            }
            if(sortedMap.size() != 0) {
                topActivityPackageName =  sortedMap.get(sortedMap.lastKey()).getPackageName();
                Log.d(TAG,"Top activity package name = " + topActivityPackageName);
            }
        }

        return topActivityPackageName;
    }

dump命令

adb shell dumpsys usagestats

Demo

小结

本文基于Android9.0 SDK列举了应用使用数据统计服务UsageStatsManager所能提供的服务,并给出获取顶部应用的代码示例。其他获取前台应用的方法可以参看《4种获取前台应用的方法(肯定有你不知道的)》,里面关于UsageStatsManager使用的相关优化可以学习一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342