Android UsageStatsManager的简单使用

3/1/2017来源:Android开发人气:2324

最近公司要弄一个积分墙的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

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

         (2)MAC ADDRESS

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

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

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

      获取Wifi Mac地址:

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

            (3)Sim Serial Number

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

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

           (4)ANDROID_ID

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

import android.PRovider.Settings;
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.获取桌面应用列表信息

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

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

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

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

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

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

Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);  
context.startActivity(intent);   一定要申请用户授权,如果用户不给你授权,那么你还是拿不到的哦~~~

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

UsageStatsManager usm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);  
Calendar calendar = Calendar.getInstance();  long endTime = calendar.getTimeInMillis();  calendar.add(Calendar.YEAR, -1);  long startTime = calendar.getTimeInMillis();  List<UsageStats> usageStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, startTime, endTime);    来源: http://blog.csdn.net/ballonge/article/details/51085953        综上所述, 根据版本获取应用列表: /** * 获取栈顶运行的进程 * @param context * @return */ public static String getLauncherTopApp(Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> appTasks = activityManager.getRunningTasks(1); if (null != appTasks && !appTasks.isEmpty()) { return appTasks.get(0).topActivity.getPackageName(); } } else { long endTime = System.currentTimeMillis(); long beginTime = endTime - 1000; if (sUsageStatsManager == null) { sUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); } String result = ""; UsageEvents.Event event = new UsageEvents.Event(); UsageEvents usageEvents = sUsageStatsManager.queryEvents(beginTime, endTime); while (usageEvents.hasNextEvent()) { usageEvents.getNextEvent(event); //监测app由后台转前台 if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) { result = event.getPackageName(); } //监测app由前台转后台// if (event.getEventType() == UsageEvents.Event.MOVE_TO_BACKGROUND) {// result = event.getPackageName();// } } if (!android.text.TextUtils.isEmpty(result)) { return result; } } return ""; }         另附,判断应用是否由前台转后台的代码:      /** * 判断指定进程是否由前台转后台 * @param context * @return */ public static boolean IsLauncherToBack(Context context,String pkgName) { boolean isRunning = false; long endTime = System.currentTimeMillis(); long beginTime = endTime - 1000; if (sUsageStatsManager == null) { sUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); } String result = ""; UsageEvents.Event event = new UsageEvents.Event(); UsageEvents usageEvents = sUsageStatsManager.queryEvents(beginTime, endTime); while (usageEvents.hasNextEvent()) { usageEvents.getNextEvent(event); //监测app由前台转后台 if (event.getEventType() == UsageEvents.Event.MOVE_TO_BACKGROUND) { result = event.getPackageName(); } } if (!android.text.TextUtils.isEmpty(result)&&pkgName.equals(result)) { isRunning = true; } return isRunning; }      4. 获取指定应用运行的时间         查找资料发现Android本身提供了获取应用使用时间的api(来源:http://blog.csdn.net/pierce0young/article/details/22292603),但是自己试了发现两个主要的类都找不到,不知道为什么,如果大家发现什么,请一定给我留下解释,谢谢!那么接下来我就是间接的获取时间,即监听指定app的安装成功及启动,开始计时,再配合判断应用在前台以及后台,计算时间的长短来获取运行时间。     5.允许获取应用使用情况         以上步骤大多基于用户已打开,允许该应用获取其他应用使用情况,这个前提,接下来是判断手机上是否存在这个打开界面、是否已打开这个开关以及打开这个界面的代码: /** * 监测手机上是否存在允许查看应用使用情况 * @return */ private boolean isNoOption() { PackageManager packageManager = getapplicationContext() .getPackageManager();        //打开允许获取应用使用情况的界面 Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); return list.size() > 0; }          /** * 监测允许查看应用使用情况是否打开 * @return */ private boolean isNoSwitch() { long ts = System.currentTimeMillis(); UsageStatsManager usageStatsManager = (UsageStatsManager) getApplicationContext() .getSystemService(USAGE_STATS_SERVICE); List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats( UsageStatsManager.INTERVAL_BEST, 0, ts); if (queryUsageStats == null || queryUsageStats.isEmpty()) { return false; } return true; }