最近有一个这样的小功能,动态监听App在前台运行还是后台运行,然后做对应业务逻辑操作,故此全文主讲Android app 前后台监听 ~
up ~
- 提要
- 了解
- 入门
- 项目进阶 - A
- 项目进阶 - B
- 小课堂
提要
在Android中从你打开App的那一霎那就已经调用了Application的基本配置
,随后又调用了对应四大组件,当然在调用对应类时肯定是先走的 onCreate()生命周期
了 ~
故此在一切正式开始之前,首先我们要创建一个属于我们的Application继承于系统Application,这里我创建的是MyApplication,创建好之后将其注入到清单文件中 ~
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nk.com.activitylifecycle">
<!--将我们创建好的Application在application标签内进行声明 - android:name=".MyApplication"-->
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
关于前后台监听,主要涉及三种场景
- 用户将app放于多任务界面
- 用户将app返回手机主页界面
- 用户将app返回app应用内部界面
关于前后台监听的功能,主要涉及的API:registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)
,从字面意思不难看出就是注册Activity生命周期的监听回调 ~
通过Application的源码可以发现内部有注册的时候,自然少不了注销,一般这样的开闭方法都减少了内存方面可能隐藏的Bug,注销方法可以看场景使用,可以用在MainActivity销毁的时候,也可以不用 ~
嘘,还有一点要注意
因为我们监听前后台的目的主要是要做一些业务逻辑,但是有的业务逻辑针对的是整个项目,有的业务逻辑可能只是针对某一个或者某几个类,所以当业务逻辑涉及到仅仅某些类的话,可以采用接口回调或者EventBus进行事件处理 ~ 个人建议是采用EventBus,因为接口回调有些朋友容易绕晕,同时代码会冗余较多,而EventBus的话发展至今已经比较完善了,所以为自己少点烦恼 ~
了解
以下代码是一个最简单的实现,你通过切换App场景可以发现已经实现了监听前后台的功能,但是!它是存在很严重的缺陷!当我们存在多个Activity且相互进行跳转的时候,通过Log可以发现频繁的调用了生命周期的监听方法 ~ 故此方法只用于基础理解,可以在看下方入门 ~
Look here:频繁调用方法,也就意味着内部的逻辑也会重复调用,从而大大增加了bug率 ~
package nk.com.activitylifecycle;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;
/**
* @author MrLiu
* @date 2020/10/12
* desc
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//监听Activity生命周期
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
//当前Activity个数
private int activityNumber = 0;
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
Log.e("tag", "ActivityLifecycleCallbacksAdapter:后台 --> 前台");
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
Log.e("tag", "ActivityLifecycleCallbacksAdapter:前台 --> 后台");
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
入门
与上方的不同点在于监听前后台切换时,如果用户多Activity切换的话就不会被重复调用切后台监听内的逻辑方法,从而减少了部分bug,但是此入门方式仅仅解决了最基础的需求,如果在项目中使用的话还可以在优化一下,不过还是先看入门,然后在继续往进阶看吧~
package nk.com.activitylifecycle;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;
/**
* @author MrLiu
* @date 2020/10/12
* desc
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
private int activityNumber = 0;
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
if (activityNumber++ == 0) {
Log.e("tag", "app回到前台");
Log.e("tag", "activityNumber = " + activityNumber);
}
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
if (--activityNumber == 0) {
// app回到后台
Log.e("tag", "app回到后台");
Log.e("tag", "activityNumber = " + activityNumber);
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
项目进阶 - A
与上方的不同在于对Application内方法进行了抽离,内部使用了单例模式,减少了内存开销,同时将该业务进行了剥离,算是一个代码优化把 - -,这个阶段的代码基本可以投入项目使用了
LifecycleMonitor
package nk.com.activitylifecycle;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;
/**
* @author MrLiu
* @date 2020/10/12
* desc
*/
public class LifecycleMonitor {
private int resumeActivityCount = 0;
private static LifecycleMonitor lifecycleHelper;
public static LifecycleMonitor getInstance() {
if (lifecycleHelper == null) {
lifecycleHelper = new LifecycleMonitor();
}
return lifecycleHelper;
}
public void register(Application application) {
if (application != null)
//这里对ActivityLifecycleCallbacksAdapter进行了二次包装,不必实现原有的抽象方法,仅需实现我们需要的方法即可!
application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksAdapter() {
@Override
public void onActivityStarted(Activity activity) {
if (resumeActivityCount++ == 0) {
// 执行切换到前台的逻辑
Log.e("tag", "后台 --> 前台" + resumeActivityCount);
// ... 使用EventBus传递数据
}
}
@Override
public void onActivityStopped(Activity activity) {
if (--resumeActivityCount == 0) {
// 执行切换到后台的逻辑
Log.e("tag", "前台 --> 后台" + resumeActivityCount);
// ... 使用EventBus传递数据
}
}
});
}
/**
* Activity 生命周期监听,用于监控app前后台状态切换(对应Activity生命周期)
*/
public static class ActivityLifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
}
MyApplication
package nk.com.activitylifecycle;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;
/**
* @author MrLiu
* @date 2020/10/12
* desc
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
LifecycleMonitor.getInstance().register(this);
}
}
项目进阶 - B
与上方相比,此处调用主要采取了static的方式进行调用,同时加入了内部的俩个监听回调(个人感觉有点画蛇添足,但是从一定层面其实有达到了解耦的效果,所以仁者见仁智者见智咯),可能这个方法不是最好的,但是对于我这种懒人可能是最合适的 ~ 所以当前代码也可投入项目使用 ~
AppStateTracker
package nk.com.activitylifecycle;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;
/**
* @author MrLiu
* @date 2020/10/10
* desc
*/
public class AppStateMonitor {
public static final int STATE_FOREGROUND = 0;
public static final int STATE_BACKGROUND = 1;
private static int currentState;
public static int getCurrentState() {
return currentState;
}
public interface AppStateChangeListener {
void appTurnIntoForeground();
void appTurnIntoBackGround();
}
public static void track(Application application, final AppStateChangeListener appStateChangeListener) {
application.registerActivityLifecycleCallbacks(new SimpleActivityLifecycleCallbacks() {
private int resumeActivityCount = 0;
@Override
public void onActivityStarted(Activity activity) {
// if (resumeActivityCount == 0) {
// currentState = STATE_FOREGROUND;
// appStateChangeListener.appTurnIntoForeground();
// }
// resumeActivityCount++;
if (resumeActivityCount++ == 0) {
currentState = STATE_FOREGROUND;
appStateChangeListener.appTurnIntoForeground();
}
}
@Override
public void onActivityStopped(Activity activity) {
// resumeActivityCount--;
// if (resumeActivityCount == 0) {
// currentState = STATE_BACKGROUND;
// appStateChangeListener.appTurnIntoBackGround();
// }
if (--resumeActivityCount == 0) {
currentState = STATE_BACKGROUND;
appStateChangeListener.appTurnIntoBackGround();
}
}
});
}
private static class SimpleActivityLifecycleCallbacks implements Application
.ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
}
MyApplication
package nk.com.activitylifecycle;
import android.app.Application;
import android.util.Log;
/**
* @author MrLiu
* @date 2020/10/12
* desc
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
AppStateMonitor.track(this, new AppStateMonitor.AppStateChangeListener() {
@Override
public void appTurnIntoForeground() {
// 处理app到前台的逻辑
Log.e("tag", "App - 处于前台");
}
@Override
public void appTurnIntoBackGround() {
// app处理到到后台的逻辑
Log.e("tag", "App - 处于后台");
}
});
}
}
小课堂
因为监听的去重方式使用到了++、–故此科普一下自增++、自减-- 前后位置作用
自加(++)自减(–)运算
自加: ++
对原有的数据进行+1
自减:- -
对原有的数据进行-1
使用场景
- 单独使用
放在操作数的前面和后面效果一样(这种用法是我们比较常见的) - 参与运算使用
放在操作数的前面(++1||- -1),先自增或者自减,然后再参与运算
放在操作数的后面(1++||1- -),先参与运算,再自增或者自减
在查询监听前后台的功能实现时 也查到了以下的这种方法,此方法主要是在监听BaseActivity内的生命周期,理论上也是可行的,但是我并没有亲自尝试,故此当做饭后甜点吧 ~
不过有人指出在Android6.0的机器上,在某些情景下使用无法获取到当前正在运行的包名,如在屏幕暗下后,使用下方代码中的isAppOnForeground方法内的获取进程的方法后,可能会获取不到正确的进程列表 ~
public class BaseActivity extends Activity {
//全局变量
public static boolean isActive;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
}
@Override
protected void onResume() {
if (!isActive) {
//app 从后台唤醒,进入前台
isActive = true;
Log.i("ACTIVITY", "程序从后台唤醒");
}
super.onResume();
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onStop() {
if (!isAppOnForeground()) {
//app 进入后台
isActive = false;//记录当前已经进入后台
Log.i("ACTIVITY", "程序进入后台");
}
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
/**
* APP是否处于前台唤醒状态
*
* @return
*/
public boolean isAppOnForeground() {
ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
String packageName = getApplicationContext().getPackageName();
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null)
return false;
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
// The name of the process that this object is associated with.
if (appProcess.processName.equals(packageName)
&& appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
return false;
}
}
查询当前进程名
根据功能查询到早期处理方式,也可以使用getRunningTasks
,不过后来随着更新此方式已经废弃
!然后又有了通过getRunningTasks和RunningAppProcessInfo结合
的处理方式(兼容API22+和API22-),不过有人说这种方法在Android6.0的机器黑屏时获取不到正确的进程列表
~ 所以还是 建议使用上方的项目进阶 A 和 B 把
/**
* 查询当前进程名
*
* @param context
* @return
*/
public static String getCurrentPkgName(Context context) {
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
String pkgName = null;
if (Build.VERSION.SDK_INT >= 22) {
ActivityManager.RunningAppProcessInfo currentInfo = null;
Field field = null;
int START_TASK_TO_FRONT = 2;
try {
field = ActivityManager.RunningAppProcessInfo.class
.getDeclaredField("processState");
} catch (Exception e) {
e.printStackTrace();
}
List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo app : appList) {
if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
Integer state = null;
try {
state = field.getInt(app);
} catch (Exception e) {
e.printStackTrace();
}
if (state != null && state == START_TASK_TO_FRONT) {
currentInfo = app;
break;
}
}
}
if (currentInfo != null) {
pkgName = currentInfo.processName;
}
} else {
List<RunningTaskInfo> runTaskInfos = am.getRunningTasks(1);
// 拿到当前运行的任务栈
ActivityManager.RunningTaskInfo runningTaskInfo = runTaskInfos
.get(0);
// 拿到要运行的Activity的包名
pkgName = runningTaskInfo.baseActivity.getPackageName();
}
return pkgName;
}