• 概述

  项目中很多场景交互非常依赖于客户端的前后景状态以及其他一些辅助信息上传,譬如当前新闻在前台(看到的是新闻界面)播放时,语音开启音乐应用,此时我们希望能看到音乐界面,并且音乐在播;而在导航应用在前台时,我们不希望跳转至音乐应用;此时,如果AIUI云平台不知道我们哪个应用在前台,交互就会混乱,由此可见,客户端获取前台进程还是非常有必要讨论一下的.

1.通过RunningTask

  当一个App处于前台的时候,会处于RunningTask的这个栈的栈顶,所以我们可以取出RunningTask的栈顶的任务进程,看他与我们的想要判断的App的包名是否相同,来达到效果.

  List tasks = am.getRunningTasks(1);

if (tasks != null && !tasks.isEmpty()) {

   ComponentName componentName = tasks.get(0).topActivity;

  if (componentName != null) {

    return componentName.getClassName();

    }

  }

  然而getRunningTask方法在Android5.0以上已经被废弃,只会返回自己和系统的一些不敏感的task,不再返回其他应用的task,用此方法来判断自身App是否处于后台,仍然是有效的,但是无法判断其他应用是否位于前台,因为不再能获取信息

2.通过RunningProcess

  通过runningProcess获取到一个当前正在运行的进程的List,我们遍历这个List中的每一个进程,判断这个进程的一个importance 属性是否是前台进程,并且包名是否与我们判断的APP的包名一样,如果这两个条件都符合,那么这个App就处于前台。

public static boolean getRunningAppProcesses(Context context, String packageName) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcess == null){
return false;
}
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses){
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FORGROUND && appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}

遗憾的是从Andriod5.1版本后getRunningAppProcesses()只能拿到自己的进程信息,所以已经不再适用了.

3.通过ActivityLifecycleCallbacks

AndroidSDK14在Application类里增加了ActivityLifecycleCallbacks,我们可以通过这个Callback拿到App所有Activity的生命周期回调。

public interface ActivityLifecycleCallbacks {

voidonActivityCreated(Activity activity, Bundle savedInstanceState);

voidonActivityStarted(Activity activity);

voidonActivityResumed(Activity activity);

voidonActivityPaused(Activity activity);

voidonActivityStopped(Activity activity);

voidonActivitySaveInstanceState(Activity activity, Bundle outState);

voidonActivityDestroyed(Activity activity);

}

知道这些信息,我们就可以用更官方的办法来解决问题,当然还是利用方案二里的Activity生命周期的特性,我们只需要在Application的onCreate()里去注册上述接口,然后由Activity回调回来运行状态即可。

private ActivityLifecycleCallbacks mActivityLifecycleCallbacks = new ActivityLifecycleCallbacks() {@Overridepublic void onActivityCreated(Activity activity, Bundle savedInstanceState) {}@Overridepublic void onActivityStarted(Activity activity) {Log.d(TAG, "onActivityStarted " + activity.toString());if (CustomActivityManager.getInstance().getTopActivity() == null) {Status.INSTANCE.setSatus(Constant.FG_ACTIVITY, null, null);}}@Overridepublic void onActivityResumed(Activity activity) {Log.d(TAG, "onActivityResumed " + activity.toString());CustomActivityManager.getInstance().addTopActivity(activity);}@Overridepublic void onActivityPaused(final Activity activity) {Log.d(TAG, "onActivityPaused " + activity.toString());CustomActivityManager.getInstance().removeTopActivity(activity);}@Overridepublic void onActivityStopped(final Activity activity) {if (CustomActivityManager.getInstance().getTopActivity() == null) {Status.INSTANCE.setSatus(Constant.BG_ACTIVITY, null, null);}}@Overridepublic void onActivitySaveInstanceState(Activity activity, Bundle outState) {
//            Log.d(TAG, "onActivitySaveInstanceState " + activity.toString());
        }@Overridepublic void onActivityDestroyed(Activity activity) {}};

该方法需要在Application中进行注册相关Activity生命周期的回调registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);

4.通过UsageStatsManager

UsageStatsManager就是使用情况统计管理者,通过它可以获取应用的使用情况。它是Android 5.0 才有的API。使用它之前需要在清单文件中配置 “android.permission.PACKAGE_USAGE_STATS”的权限

用户必须在 设置–安全–有权查看使用情况的应用 中勾选相应的应用

对应设备 Android 5.0 及其以上。

魅族和小米手机不能通过UsageStatsManager获取应用使用情况

@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
public static boolean queryUsageStats(Context context, String packageName) {class RecentUseComparator implements Comparator<UsageStats> {@Overridepublic int compare(UsageStats lhs, UsageStats rhs) {return (lhs.getLastTimeUsed() > rhs.getLastTimeUsed()) ? -1 : (lhs.getLastTimeUsed() == rhs.getLastTimeUsed()) ? 0 : 1;}}RecentUseComparator mRecentComp = new RecentUseComparator();long ts = System.currentTimeMillis();UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);List<UsageStats> usageStats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, ts - 1000 * 10, ts);if (usageStats == null || usageStats.size() == 0) {if (!havePermissionForTest(context)) {Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);Toast.makeText(context, "权限不够\n请打开手机设置,点击安全-高级-有权查看使用情况的应用,开启这个应用的权限",Toast.LENGTH_LONG).show();}return false;}Collections.sort(usageStats, mRecentComp);String currentTopPackage = usageStats.get(0).getPackageName();return currentTopPackage.equals(packageName);
}@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static boolean havePermissionForTest(Context context) {try {PackageManager packageManager = context.getPackageManager();ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STAGS, applicationInfo.uid, applicationInfo.packageName);return mode == AppOpsManager.MODE_ALLOWED;} catch (PackageManager.NameNotFoundException e) {return true;}
}

5.通过AccessbilityService

通过 Android 自带无障碍功能,监控窗口焦点的变化,拿到焦点窗口对应包名

public static boolean getFromAccessibilityService(Context context, String packageName) {if (DetectService.isAccessibilitySettingsOn(context)) {DetectService detectService = DetectService.getInstance();String foreground = detectService.getForegroundPackage();Log.d(DEBUG, "当前窗口焦点对应包名为:" + foreground);return packageName.equals(foreground);} else {Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY+SETTINGS);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);Toask.makeTexxt(context, "请为App打开辅助功能开关", Toast.LENGTH_SHORT).show();return false;}
}public class DetectService extends AccessibilityService {private static String mForegroundPackageName;private static DetectService mInstatnce = null;private DetectService {}public static DetectService getInstance() {if (mInstance == null) {synchronized (DetectService.class) {if (mInstance == null) {mInstance = new DetectService();}}}return mInstance;}@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {mForegroundPackageName = event.getPackageName().toString();}}@Overridepublic void onInterrupt() {}public String getForegroundPackageName() {return mForegroundPackageName;}public static boolean isAccessibilitySettingsOn(Context context) {int accessibilityEnabled = 0;try {accessibilityEnabled = Settings.Secure.getInt(context.getContentResolver(), android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);} catch (Settings.SettingNotFoundException e) {Log.d(DEBUG, e.getMessage());}if (accessibilityEnabled == 1) {String services = Settings.Secure.getString(context.getContentResolver(),Setting.Secure.ENABLED_ACCESSIBILITY_SERICES);if (services != null) {return services.toLowerCase().contains(context.getPackageName().toLowerCase());}}return false;}
}

需要创建 ACCESSIBILITY SERVICE INFO 属性文件,注册 DETECTION SERVICE 到 AndroidManifest.xml

AccessibilityService有非常广泛的 ROM 覆盖, AccessibilityService不再需要轮询的判断当前的应用是不是在前台,系统会在窗口状态发生变化的时候主动回调,耗时和资源消耗都极小。可以用来判断任意应用甚至 Activity,PopupWindow, Dialog 对象是否处于前台.

  • 总结

 Android设备碎片化如此严重,不说各大厂商定制系统会修改API源码,单论Android的版本适配问题就很是头疼,这是站在用户角度考虑,但对开发者的技术要求也越高.唯有通过不断的积累,持续地沉淀,才能跟上时代的脚步.古人云,学如逆水行舟,不进则退,也是告诫世人持续学习的道理...

转载于:https://www.cnblogs.com/fuyaozhishang/p/7442820.html

Android获取前台进程的方法相关推荐

  1. android屏幕密度高度,Android获取常用辅助方法(获取屏幕高度、宽度、密度、通知栏高度、截图)...

    我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现.下面就分享一下Android中常用的一些辅助方法: 获取屏幕高度: /** * 获得屏幕高度 * @para ...

  2. android获取context的方法,Android编程获取全局Context的方法

    Android编程获取全局Context的方法 本文实例讲述了Android编程获取全局Context的方法.分享给大家供大家参考,具体如下: 有时,在处理业务逻辑的时候,需要Context对象,但在 ...

  3. Android 获取keystore SHA1方法

    (第一种方式)通过Android Studio编译器获取SHA1 第一步.打开Android Studio的Terminal工具 第二步.输入命令:keytool -v -list -keystore ...

  4. Android获取Wifi信号强度方法总结

    直接封装了一个工具类,获取WIFI信号强度直接调用该工具类的getCurrentNetworkRssi方法即可. public class WifiUtils {// 获取当前热点最新的信号强度pub ...

  5. android获取电量的方法,在Android中获取电池电量和状态

    拉风的咖菲猫 这是一个代码示例,解释了如何获取电池信息.总结一下,ACTION_BATTERY_CHANGED意图的广播接收器是动态建立的,因为它不能通过清单中声明的组件接收,只能通过显式注册它Con ...

  6. android获取电量的方法,android获取当前电量

    新建一个Receiver用于接受ACTION_BATTERY_CHANGED广播. private int mBatteryLevel; private int mBatteryScale; clas ...

  7. android获取农历的方法

    先展示农历日期的算法 import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Cale ...

  8. android获取手机摄像头

    经过两天的努力终于找到了使用android获取摄像头的方法: 下面就详细介绍一下,获取摄像头的流程: 点击获取源代码 1.第一步,先获取摄像头的权限: 代码如下:(注:如果使用的是android st ...

  9. android 获取网卡mac_Android获取本机Mac地址及IP地址方法

    1.Android  获取本机Mac 地址方法: 需要在AndroidManifest.xml文件中添加权限: public String getLocalMacAddress() { WifiMan ...

  10. Android获取网速的方法

    Android获取网速的方法 Android获取网速的方法 posted on 2018-08-23 11:54 时空观察者9号 阅读(...) 评论(...) 编辑 收藏

最新文章

  1. SpringMVC框架的详细操作步骤和注解的用法
  2. 成功解决成功解决return _iterencode(o, 0) ValueError: Circular reference detected
  3. php网站分享qq代码下载,HTML5QQ登录的一篇代码分享
  4. 【转】Dynamics CRM 365零基础入门学习(二)Dynamics 插件注册的基本流程
  5. (72)FPGA面试题-使用不同的代码实现2-4译码器?使用if语句
  6. js实现treeview 级联修改状态
  7. 管理docker容器
  8. tornado + supervisor + nginx + linux 亲身体验
  9. java留言功能_java web实现简单留言板功能
  10. 玩转诺基亚5800XM,新手上路指南
  11. 网站小图标 (favicon) 的正确设置
  12. Mac系统解决matplotlib无法显示中文字体
  13. ubuntu系统下载路径(可以收藏免得以后再找)
  14. 大脑信息编码_编码人大脑的5大小吃
  15. 王强是如何学计算机的,浙江大学计算机科学与技术学院导师介绍:王强
  16. 4个团队领导必备的技能和素质
  17. 中国经济能否率先复苏
  18. 远程桌面连接笔记本,wifi自动断开导致远程断开解决方法
  19. 那些引用次数在15000次以上的都是什么神仙论文?
  20. ESP8266ESP32 NodeMcu擦除flash

热门文章

  1. MySQL外键约束详解
  2. iStack详解(三)——iStack多主检测方式
  3. IS-IS详解(四)——IS-IS Hello报文详解
  4. Ext.gridPanel中内容对齐
  5. 一分钟了解阿里云产品:弹性伸缩五大热点技术问题分析
  6. Windows2008R2自动更新造成的意外重启
  7. 2015过年之前计划
  8. dede后台文章不能上传图片及缩略图的解决办法
  9. 程序员应该做的事(转自CSDN)
  10. php伪随机数 ctf,[GWCTF 2019]枯燥的抽奖