应用开发中,经常遇到需要获取系统正在运行的前台应用是什么应用,例如桌面悬浮窗挂件,需要获取当前是不是处于桌面;然而随着Android版本的迭代,获取前台应用的方法都陆续被制裁,因此对这些方法做一个总结。
Android 5.0以前版本 getRunningTasks()
在Android 5.0以前的版本中,google提供了getRunningTasks()方法来获取所有正在运行的应用程序,获取前台应用可以通过下面的代码实现
public static String getTopAppPackageName(Context context) {
String packageName = "";
try {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
packageName = rti.get(0).topActivity.getPackageName();
}
}catch (Exception ignored){
}
return packageName;
}
其中Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
判断 是否是5.0之前的版本,因为在Android 5.0版本开始,getRunningTasks()方法只能返回本应用和launcher。
Android 5.0 getRunningAppProcesses()
在Android 5.0中,google新增了getRunningAppProcesses()方法,用来获取所有当前运行的应用,具体代码实现如下
public static String getTopAppPackageName(Context context) {
String packageName = "";
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
try {
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
List<ActivityManager.RunningAppProcessInfo> processes = activityManager.getRunningAppProcesses();
if (processes.size() == 0) {
return packageName;
}
for (ActivityManager.RunningAppProcessInfo process : processes) {
if (process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return process.processName;
}
}
}
}catch (Exception ignored){
}
return packageName;
}
其中Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
表示为Android 5.0时,通过getRunningAppProcesses()方法来获取当前所有运行的应用,然后通过importance来判断是否处于前台
IMPORTANCE_FOREGROUND:值为100,表示应用处于前台,且未锁屏
IMPORTANCE_TOP_SLEEPING:值为150,表示应用处于前台,且手机锁屏
IMPORTANCE_SERVICE:值为300,表示应用处于后台更多可以查看ActivityManager.RunningAppProcessInfo中的定义
Android 5.1及之后版本
Android 5.1及之后版本,getRunningAppProcesses()方法也被制裁了,也只能获取本应用的信息,只能通过其他方式来解决。下面列举一些解决方法
AccessibilityService
通过AccessibilityService辅助权限来解决吗,但是辅助权限能干很多“流氓”的事,不推荐使用。
优点
AccessibilityService 有非常广泛的 ROM 覆盖,特别是非国产手机,从 Android API Level 18(Android 2.2) 到 Android Api Level 23(Android 6.0)
AccessibilityService 不再需要轮询的判断当前的应用是不是在前台,系统会在窗口状态发生变化的时候主动回调,耗时和资源消耗都极小
不需要再额外申请权限
方法比较稳定,并非利用 Android 一些设计上的漏洞,可以长期使用的可能很大
- 可以用来判断任意应用甚至 Activity, PopupWindow, Dialog 对象是否处于前台
缺点
需要要用户开启辅助功能,辅助权限能干很多“流氓”的事,并不推荐
辅助功能会伴随应用被“强行停止”而剥夺
UsageStatsManager
Android 5.0,google 新增了UsageStatsManager类,但需要添加PACKAGE_USAGE_STATS权限,属于系统权限,需要引导用户开启。实现方式如下
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.当用户授权完成之后,通过代码获取UsageEvents Event,获取最近的一个Event,就是当前前台的应用
public static String getTopAppPackageName(Context context) {
String packageName = "";
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
final long end = System.currentTimeMillis();
final UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService( Context.USAGE_STATS_SERVICE);
if (null == usageStatsManager) {
return packageName;
}
final UsageEvents events = usageStatsManager.queryEvents((end - 60 * 1000), end);
if (null == events) {
return packageName;
}
UsageEvents.Event usageEvent = new UsageEvents.Event();
UsageEvents.Event lastMoveToFGEvent = null;
while (events.hasNextEvent()) {
events.getNextEvent(usageEvent);
if (usageEvent.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
lastMoveToFGEvent = usageEvent;
}
}
if (lastMoveToFGEvent != null) {
packageName = lastMoveToFGEvent.getPackageName();
}
}
}catch (Exception ignored){
}
return packageName;
}
使用ps命令,获取到运行进程的pid
Github上有一个国外的开源项目,通过ps命令实现的,但是貌似Android 6.0也不能使用了,我在7.0上试了,确实不行,有兴趣的可以去试试
地址:https://github.com/Chainfire/libsuperuser
总结
在Android 5.1及之后版本,推荐使用UsageStatsManager来获取前台应用,其次是AccessibilityService,下面贴出通过UsageStatsManager实现的方法代码
public static String getTopAppPackageName(Context context) {
String packageName = "";
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
try {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
List<ActivityManager.RunningTaskInfo> rti = activityManager.getRunningTasks(1);
packageName = rti.get(0).topActivity.getPackageName();
} else if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
List<ActivityManager.RunningAppProcessInfo> processes = activityManager.getRunningAppProcesses();
if (processes.size() == 0) {
return packageName;
}
for (ActivityManager.RunningAppProcessInfo process : processes) {
if (process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return process.processName;
}
}
} else if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
final long end = System.currentTimeMillis();
final UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService( Context.USAGE_STATS_SERVICE);
if (null == usageStatsManager) {
return packageName;
}
final UsageEvents events = usageStatsManager.queryEvents((end - 60 * 1000), end);
if (null == events) {
return packageName;
}
UsageEvents.Event usageEvent = new UsageEvents.Event();
UsageEvents.Event lastMoveToFGEvent = null;
while (events.hasNextEvent()) {
events.getNextEvent(usageEvent);
if (usageEvent.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
lastMoveToFGEvent = usageEvent;
}
}
if (lastMoveToFGEvent != null) {
packageName = lastMoveToFGEvent.getPackageName();
}
}
}catch (Exception ignored){
}
return packageName;
}