parent
10d43f8a2d
commit
1173843e39
@ -0,0 +1,221 @@ |
||||
--- |
||||
埋点之应用前后台判断 |
||||
--- |
||||
|
||||
#### 目录 |
||||
|
||||
1. 思维导图 |
||||
2. 六种实现方式 |
||||
- RunningTask |
||||
- RunningProcess |
||||
- ActivityLifecucleCallbacks |
||||
- UsageStatsManager |
||||
- 无障碍服务 |
||||
- 读取 /proc 目录下的信息 |
||||
|
||||
#### 思维导图 |
||||
|
||||
#### ---- RnnningTask 方式 |
||||
|
||||
```java |
||||
public static boolean getRunningTask(Context context, String packageName) { |
||||
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); |
||||
ComponentName cn = am.getRecentTasks(1, ActivityManager.RECENT_WITH_EXCLUDED).get(0).topActivity; |
||||
return !TextUtils.isEmpty(packageName) && packageName.equals(cn.getPackageName()); |
||||
} |
||||
``` |
||||
|
||||
主要功能在于 ActivityManager#getRecentTasks 方法。 |
||||
|
||||
原理: |
||||
|
||||
是当一个 APP 处于前台的时候,会处于 RunningTask 的栈顶,所以我们可以取出栈顶的任务进程,和我们传入的包名对比来确定 APP 是否位于前台。 |
||||
|
||||
缺点: |
||||
|
||||
该方法在 API 21 已经被废弃,只会返回自己和系统的一些不敏感的 Task,不再返回其他应用的从该方法的注释信息可以了解到,它返回用户最近启动的任务列表,此方法仅用于调试和显示任务管理用户界面,**绝对不能**用在应用程序中,因为未来可能会崩溃。 |
||||
|
||||
#### ---- RunningProcess 方式 |
||||
|
||||
```java |
||||
public static boolean getRunningAppProcess(Context context, String packageName) { |
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); |
||||
List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses(); |
||||
if (appProcessInfos == null) { |
||||
return false; |
||||
} |
||||
for (ActivityManager.RunningAppProcessInfo info : appProcessInfos) { |
||||
if (info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND |
||||
&& info.processName.equals(packageName)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
``` |
||||
|
||||
原理: |
||||
|
||||
通过 runningProcess 获取到一个当前正在运行的进程的 List,遍历这个 List 的每一个进程,判断这个进程的 importance 属性是否是前台进程,并且包名一致,从而判断是否是前台应用。 |
||||
|
||||
缺点: |
||||
|
||||
在社交类型的 APP 中,常常需要常驻后台来不间断的获取服务器消息,这就需要我们把 Service 设置为 START_STICKY,保证 Service 常驻后台。如果设置了这个属性,这个 APP 的进程就会判断为前台,appProcess.importance 的值永远是 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,这样就永远无法判断到底哪个是前台应用了。 |
||||
|
||||
#### ---- ActivityLifecycleCallbacks 方式 |
||||
|
||||
```java |
||||
public class MyApplication extends Application { |
||||
private int appCount = 0; |
||||
|
||||
@Override |
||||
public void onCreate() { |
||||
super.onCreate(); |
||||
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { |
||||
@Override |
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { |
||||
} |
||||
|
||||
@Override |
||||
public void onActivityStarted(Activity activity) { |
||||
appCount++; |
||||
} |
||||
|
||||
@Override |
||||
public void onActivityResumed(Activity activity) { |
||||
} |
||||
|
||||
@Override |
||||
public void onActivityPaused(Activity activity) { |
||||
} |
||||
|
||||
@Override |
||||
public void onActivityStopped(Activity activity) { |
||||
appCount--; |
||||
} |
||||
|
||||
@Override |
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void onActivityDestroyed(Activity activity) { |
||||
|
||||
} |
||||
}); |
||||
} |
||||
|
||||
public int getAppCount() { |
||||
return appCount; |
||||
} |
||||
|
||||
public void setAppCount(int appCount) { |
||||
this.appCount = appCount; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
即使当 Application 因为内存不足被 kill 掉时,这个方法仍然可以使用,虽然全局变量的值会丢失,但是再次进入 App 时会重新统计。 |
||||
|
||||
需要注意这些方法的执行是在 Activity 的生命周期方法之前执行。 |
||||
|
||||
#### ---- UsageStatsManager 方式 |
||||
|
||||
原理: |
||||
|
||||
通过使用 UsageStatsManager 获取,此方法是 Android 5.0 之后提供的 API,可以获取一个时间段内的应用统计信息,但是必须需用户授权: |
||||
|
||||
```java |
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> |
||||
``` |
||||
|
||||
手机设置 -> 查看应用使用情况 |
||||
|
||||
#### ---- 无障碍服务 |
||||
|
||||
```java |
||||
public class DetectService extends AccessibilityService { |
||||
|
||||
|
||||
private static String mForegroundPackageName; |
||||
private static volatile DetectService mInstance = null; |
||||
|
||||
public static DetectService getInstance() { |
||||
if (mInstance == null) { |
||||
synchronized (DetectService.class) { |
||||
if (mInstance == null) { |
||||
mInstance = new DetectService(); |
||||
} |
||||
} |
||||
} |
||||
return mInstance; |
||||
} |
||||
|
||||
public static String getmForegroundPackageName() { |
||||
return mForegroundPackageName; |
||||
} |
||||
|
||||
/** |
||||
* 判断当前应用的辅助功能是否开启 |
||||
*/ |
||||
public static boolean isAccessibilitySettingOn(Context context) { |
||||
int accessibilityEnable = 0; |
||||
try { |
||||
accessibilityEnable = Settings.Secure.getInt(context.getContentResolver(), |
||||
Settings.Secure.ACCESSIBILITY_ENABLED); |
||||
} catch (Settings.SettingNotFoundException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
if (accessibilityEnable == 1) { |
||||
String services = Settings.Secure.getString(context.getContentResolver(), |
||||
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); |
||||
if (services != null) { |
||||
return services.toLowerCase().contains(context.getPackageName()); |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* 监听窗口焦点,并且获取焦点窗口的包名 |
||||
* |
||||
* @param event |
||||
*/ |
||||
@Override |
||||
public void onAccessibilityEvent(AccessibilityEvent event) { |
||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { |
||||
mForegroundPackageName = event.getPackageName().toString(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onInterrupt() { |
||||
|
||||
} |
||||
``` |
||||
|
||||
使用: |
||||
|
||||
```java |
||||
public static boolean getFromAccessibilityService(Context context,String packageName){ |
||||
if (DetectService.isAccessibilitySettingOn(context)){ |
||||
DetectService detectService=DetectService.getInstance(); |
||||
String foreground=detectService.getmForegroundPackageName(); |
||||
return foreground.equals(packageName); |
||||
}else { |
||||
Intent intent=new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); |
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
||||
context.startActivity(intent); |
||||
return false; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
原理: |
||||
|
||||
Android 辅助功能( AccessibilityService )为我们提供了一系例的事件回调,帮助我们指示一些用户界面的状态变化。我们可以派生辅助功能类,进而对不同的 AccessibilityEvent 进行处理,同样的,这个服务就可以判断当前的前台应用。可以用来判断任意应用甚至 Activity、PopopWindow、Dialog 对象是否处于前台。 |
||||
|
||||
#### —— 读取 /proc 文件 |
||||
|
||||
已失效。 |
Loading…
Reference in new issue