Android UsageStatsManager的简单使用

来源:互联网 发布:淘宝小智外设店名字 编辑:程序博客网 时间:2024/04/27 17:50
最近公司要弄一个积分墙的app,因为涉及一些业务逻辑,所以查资料,做了一些总结。
(不懂积分墙的小伙伴请点 http://baike.baidu.com/link?url=N4uRhzK4qHtAsuSqotNJgT9DCIJXZb5014ia5tDsiWO81-Nh7M-7Qmdg0S1DH7_LB7USJMcL7wT9sEaCJEuk_9d1vPkkfWwneYfOigBBPMEqf_eC825RTMUJJrQve8vI)

近两年有很多试玩平台(不懂的大家可以百度下,出名的例如钱咖,试客等),通过做平台任务获取相应奖励,这里不多废话,以下将我在项目中主要使用的技术做分享:
    1.获取Android 唯一标识:Android系统中并没有提供一个完整且可长期使用的唯一码,通过多方查证以及自己demo实现试用得出几种常用的标识及一些获取办法:

        (1) DEVICE_ID

假设我们确实需要用到真实设备的标识,可能就需要用到DEVICE_ID。在以前,我们的Android设备是手机,这个DEVICE_ID可以同通过TelephonyManager.getDeviceId()获取,它根据不同的手机设备返回IMEI,MEID或者ESN码,但它在使用的过程中会遇到很多问题:

       a. 非手机设备: 如果只带有Wifi的设备或者音乐播放器没有通话的硬件功能的话就没有这个DEVICE_ID

       b. 权限: 获取DEVICE_ID需要READ_PHONE_STATE权限,但如果我们只为了获取它,没有用到其他的通话功能,那这个权限有点大才小用

       c. bug:在少数的一些手机设备上,该实现有漏洞,会返回垃圾,如:zeros或者asterisks的产品

来源: http://blog.csdn.net/qq_24531461/article/details/53096534

  1. TelephonyManager TelephonyMgr = (TelephonyManager)context.getSystemService(TELEPHONY_SERVICE);
  2. String szImei = TelephonyMgr.getDeviceId();

         (2)MAC ADDRESS

      可以使用手机Wifi或蓝牙的MAC地址作为设备标识,但是并不推荐这么做,原因有以下两点:

                   a. 硬件限制:并不是所有的设备都有Wifi和蓝牙硬件,硬件不存在自然也就得不到这一信息。

                   b. 获取的限制:如果Wifi没有打开过,是无法获取其Mac地址的;而蓝牙是只有在打开的时候才能获取到其Mac地址。

      获取Wifi Mac地址:

  1. 权限:<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
  2. WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
  3. WifiInfo info = wifi.getConnectionInfo();
  4. String Wifi_Mac = info.getMacAddress();
             来源: http://www.itnose.net/detail/6373703.htm

            (3)Sim Serial Number

        装有SIM卡的设备,可以通过下面的方法获取到Sim Serial Number:

  1. TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); 
  2. String SimSerialNumber = tm.getSimSerialNumber();
           注意:对于CDMA(包括平板电脑这些没有sim卡的设备)设备,返回的是一个空值!

           (4)ANDROID_ID

  在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被wipe后该值会被重置。可以通过下面的方法获取:

  1. import android.provider.Settings;
  2. String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);

ANDROID_ID可以作为设备标识,但需要注意:

      a. 厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID:9774d56d682e549c。

      b. 厂商定制系统的Bug:有些设备返回的值为null。

      c. 设备差异:对于CDMA设备,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。

    2.获取桌面应用列表信息

    1. /**
    2. * 获取到桌面的应用程序
    3. */
    4. public static List<ResolveInfo> getLauncherApp(Context context) {
    5. // 桌面应用的启动在INTENT中需要包含ACTION_MAIN 和CATEGORY_HOME.
    6. Intent intent = new Intent();
    7. intent.addCategory(Intent.CATEGORY_LAUNCHER);
    8. intent.setAction(Intent.ACTION_MAIN);
    9. PackageManager manager = context.getPackageManager();
    10. List<ResolveInfo> resolveInfoList = manager.queryIntentActivities(intent, 0);
    11. return resolveInfoList;
    12. }
    3.获取系统栈顶的进程
    在5.0以前google给我们提供的API是ActivityManager类中通过getRunningTasks()获取当前打开的所有应用程序 ,所以,如果想要获取当前的栈顶Activity和进程,可以使用以下方法:
  1. ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  2. List<ActivityManager.RunningTaskInfo> appTasks = activityManager.getRunningTasks(1);
  3. if (null != appTasks && !appTasks.isEmpty()) {
  4. return appTasks.get(0).topActivity.getPackageName();
  5. }
    但是,随着系统版本的升级,Google对权限控制更加严谨了,这为我们这项项目开发需求造成了一定的困扰。5.0版本之后google废弃了getRunningTasks()方法,意味着我们在5.0以后不能通过该方法获取正在运行的应用程序,但5.0又提供类新的Api,那就是getRunningAppProcesses(),通过ActivityManager的getRunningAppProcesses()方法也可以获取正在运行的应用程序。

但是,在5.1的版本发布后getRunningAppProcesses()已经获取不到正在运行的服务...

那还有什么办法呢?原来,Android在5.0版本Google提供了一个UsageStatsManager类,通过这个类可以获取到应用程序的运行情况,该类的使用方法如下:

    (1)在AndroidManifest文件中添加权限:

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

     (2)启动授权页面,需要用户授权

    1. Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);  
    2. context.startActivity(intent);  

一定要申请用户授权,如果用户不给你授权,那么你还是拿不到的哦~~~

     (3)如果用户给你授权了,调用相关代码获取正在运行的服务:

    1. UsageStatsManager usm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);  
    2. Calendar calendar = Calendar.getInstance();  
    3. long endTime = calendar.getTimeInMillis();  
    4. calendar.add(Calendar.YEAR, -1);  
    5. long startTime = calendar.getTimeInMillis();  
    6. List<UsageStats> usageStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, startTime, endTime);   

来源: http://blog.csdn.net/ballonge/article/details/51085953
       综上所述, 根据版本获取应用列表:
  1. /**
  2. * 获取栈顶运行的进程
  3. * @param context
  4. * @return
  5. */
  6. public static String getLauncherTopApp(Context context) {
  7. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
  8. ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  9. List<ActivityManager.RunningTaskInfo> appTasks = activityManager.getRunningTasks(1);
  10. if (null != appTasks && !appTasks.isEmpty()) {
  11. return appTasks.get(0).topActivity.getPackageName();
  12. }
  13. } else {
  14. long endTime = System.currentTimeMillis();
  15. long beginTime = endTime - 1000;
  16. if (sUsageStatsManager == null) {
  17. sUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
  18. }
  19. String result = "";
  20. UsageEvents.Event event = new UsageEvents.Event();
  21. UsageEvents usageEvents = sUsageStatsManager.queryEvents(beginTime, endTime);
  22. while (usageEvents.hasNextEvent()) {
  23. usageEvents.getNextEvent(event);
  24. //监测app由后台转前台
  25. if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
  26. result = event.getPackageName();
  27. }
  28. //监测app由前台转后台
  29. // if (event.getEventType() == UsageEvents.Event.MOVE_TO_BACKGROUND) {
  30. // result = event.getPackageName();
  31. // }
  32. }
  33. if (!android.text.TextUtils.isEmpty(result)) {
  34. return result;
  35. }
  36. }
  37. return "";
  38. }
        另附,判断应用是否由前台转后台的代码:
    
  1. /**
  2. * 判断指定进程是否由前台转后台
  3. * @param context
  4. * @return
  5. */
  6. public static boolean IsLauncherToBack(Context context,String pkgName) {
  7. boolean isRunning = false;
  8. long endTime = System.currentTimeMillis();
  9. long beginTime = endTime - 1000;
  10. if (sUsageStatsManager == null) {
  11. sUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
  12. }
  13. String result = "";
  14. UsageEvents.Event event = new UsageEvents.Event();
  15. UsageEvents usageEvents = sUsageStatsManager.queryEvents(beginTime, endTime);
  16. while (usageEvents.hasNextEvent()) {
  17. usageEvents.getNextEvent(event);
  18. //监测app由前台转后台
  19. if (event.getEventType() == UsageEvents.Event.MOVE_TO_BACKGROUND) {
  20. result = event.getPackageName();
  21. }
  22. }
  23. if (!android.text.TextUtils.isEmpty(result)&&pkgName.equals(result)) {
  24. isRunning = true;
  25. }
  26. return isRunning;
  27. }
 
   4. 获取指定应用运行的时间
        查找资料发现Android本身提供了获取应用使用时间的api(来源:http://blog.csdn.net/pierce0young/article/details/22292603),但是自己试了发现两个主要的类都找不到,不知道为什么,如果大家发现什么,请一定给我留下解释,谢谢!那么接下来我就是间接的获取时间,即监听指定app的安装成功及启动,开始计时,再配合判断应用在前台以及后台,计算时间的长短来获取运行时间。
    5.允许获取应用使用情况
        以上步骤大多基于用户已打开,允许该应用获取其他应用使用情况,这个前提,接下来是判断手机上是否存在这个打开界面、是否已打开这个开关以及打开这个界面的代码:

  1. /**
  2. * 监测手机上是否存在允许查看应用使用情况
  3. * @return
  4. */
  5. private boolean isNoOption() {
  6. PackageManager packageManager = getApplicationContext()
  7. .getPackageManager();
  8.        //打开允许获取应用使用情况的界面
  9. Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
  10. List<ResolveInfo> list = packageManager.queryIntentActivities(intent,
  11. PackageManager.MATCH_DEFAULT_ONLY);
  12. return list.size() > 0;
  13. }
        
  1. /**
  2. * 监测允许查看应用使用情况是否打开
  3. * @return
  4. */
  5. private boolean isNoSwitch() {
  6. long ts = System.currentTimeMillis();
  7. UsageStatsManager usageStatsManager = (UsageStatsManager) getApplicationContext()
  8. .getSystemService(USAGE_STATS_SERVICE);
  9. List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats(
  10. UsageStatsManager.INTERVAL_BEST, 0, ts);
  11. if (queryUsageStats == null || queryUsageStats.isEmpty()) {
  12. return false;
  13. }
  14. return true;
  15. }


        

0 0
原创粉丝点击