• 概述

  项目中很多场景交互非常依赖于客户端的前后景状态以及其他一些辅助信息上传,譬如当前新闻在前台(看到的是新闻界面)播放时,语音开启音乐应用,此时我们希望能看到音乐界面,并且音乐在播;而在导航应用在前台时,我们不希望跳转至音乐应用;此时,如果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() {

        @Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
} @Override
public void onActivityStarted(Activity activity) {
Log.d(TAG, "onActivityStarted " + activity.toString());
if (CustomActivityManager.getInstance().getTopActivity() == null) {
Status.INSTANCE.setSatus(Constant.FG_ACTIVITY, null, null);
}
} @Override
public void onActivityResumed(Activity activity) {
Log.d(TAG, "onActivityResumed " + activity.toString());
CustomActivityManager.getInstance().addTopActivity(activity); } @Override
public void onActivityPaused(final Activity activity) {
Log.d(TAG, "onActivityPaused " + activity.toString());
CustomActivityManager.getInstance().removeTopActivity(activity); } @Override
public void onActivityStopped(final Activity activity) {
if (CustomActivityManager.getInstance().getTopActivity() == null) {
Status.INSTANCE.setSatus(Constant.BG_ACTIVITY, null, null);
}
} @Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
// Log.d(TAG, "onActivitySaveInstanceState " + activity.toString());
} @Override
public 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> {
@Override
public 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;
} @Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
mForegroundPackageName = event.getPackageName().toString();
}
} @Override
public 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的版本适配问题就很是头疼,这是站在用户角度考虑,但对开发者的技术要求也越高.唯有通过不断的积累,持续地沉淀,才能跟上时代的脚步.古人云,学如逆水行舟,不进则退,也是告诫世人持续学习的道理...

Android获取前台进程的方法的更多相关文章

  1. Android获取系统时间方法的总结

    Android获取系统时间方法的方法有很多种,常用的有Calendar.Date.currentTimeMills等方法. (1)Calendar Calendar获取系统时间首先要用Calendar ...

  2. android获取时间差的方法

    本文实例讲述了android获取时间差的方法.分享给大家供大家参考.具体分析如下: 有些时候我们需要获取当前时间和某个时间之间的时间差,这时如何获取呢? 1. 引用如下命名空间: import jav ...

  3. android 获取路径目录方法以及判断目录是否存在,创建目录

    Environment 常用方法: * 方法:getDataDirectory()解释:返回 File ,获取 Android 数据目录.* 方法:getDownloadCacheDirectory( ...

  4. Android 获取keystore SHA1方法

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

  5. android获取路径目录方法

    Environment常用方法: getExternalStrongeDirectory() 返回File,获取外部存储目录即SDCard getDownloadCacheDirectory() 返回 ...

  6. android获取mac地址方法

    http://www.cnblogs.com/xioapingguo/p/4037513.html 网上找的,记录一下 public static String getMacAdress(){ Wif ...

  7. android 获取颜色 getColor 方法 deprecated 过期

    可以使用下面代码代替: ContextCompat.getColor(getContext(), R.color.post_list_content_color) 需要引入: compile 'com ...

  8. android 获取字符串的方法

    字符串数组可以在value文件夹中声明: 书写的内容是: 两者的读取方式略有不同: 如果是读取数字的话,  使用: context.getResources().getStringArray( R.a ...

  9. android 获取前台进程

    String getTopActivity() { ActivityManager manager = (ActivityManager)getSystemService(ACTIVITY_SERVI ...

随机推荐

  1. CodeForces 733C Epidemic in Monstropolis

    模拟. 连续的一段$a$合成一个$b$.每段中如果数字只有$1$个,那么可以合成.如果数字个数大于等于$2$个,如果都是一样的,那么无法合成,否则要找到一个可以移动的最大值位置开始移动.一开始写了一个 ...

  2. android:sharedUserId

    <manifest> syntax: <manifest xmlns:android="http://schemas.android.com/apk/res/android ...

  3. 总结分析Java常见的四种引用

    从JDK1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期.这四种级别由高到低依次为:强引用.软引用.弱引用和虚引用. 1.强引用 本章前文介绍的引用实际上都是强引用, ...

  4. 读书笔记(高性能javascript)(二)

    5. 字符串和正则表达式: (1) 在大多数浏览器中,数组项合并(Array.prototype.join)比其他字符串连接方法更慢,但它却在IE7及更早版本浏览器中合并大量字符串唯一高效的途径: ( ...

  5. Linux命令之useradd

    useradd [选项] LOGIN(登录名) useradd –D useradd –D [选项] 创建一个新用户或更新默认新用户信息.useradd和adduser命令相同,adduser是use ...

  6. python搜索引擎(转)

    用python如何实现一个站内搜索引擎? 先想想搜索引擎的工作流程: 1.网页搜集.用深度或者广度优先的方法搜索某个网站,保存下所有的网页,对于网页的维护采用定期搜集和增量搜集的方式. 2.建立索引库 ...

  7. UVA 147(子集和问题)

     Dollars New Zealand currency consists of $100, $50, $20, $10, and $5 notes and $2, $1, 50c, 20c, 10 ...

  8. [Codeforces-div.1 24D] Broken robots

    [Codeforces-div.1 24D] Broken robots 试题分析 显然设\(f_{i,j}\)为到\((i,j)\)的期望步数,将转移表达式列出来. 首先自己跟自己的项消掉. 然后规 ...

  9. 【线段树】Gym - 101201J - Shopping

    题意:n个数,m次询问,每次给你一个询问v,l,r,问你v%a[l]%a[l+1]%...%a[r]是多少. a%b,结果要么不变,要么至少缩小到a的一半,于是用线段树,每次询问当前区间最靠左侧的小于 ...

  10. 20162303实验二 Java面向对象程序设计实验报告

    北京电子科技学院(BESTI) 实 验 报 告 课程:程序设计与数据结构 班级: 1623 姓名: 石亚鑫 学号:20162303 成绩: 2分 指导教师:娄家鹏 王志强 实验日期:4月14日 实验密 ...