Activity生命周期监控方案
实际开发中,我们经常需要在Activity的onResume或者onStop中进行全局资源的获取或释放,那么怎么去监控Activity生命周期变化呢?
通知式监控
一般情况下,我们可以在资源管理类中提供onActivityResume,onActivityStop之类的公共接口来实现该需求,这种情况下,需要在Activity内部的各个生命周期函数中手动调用资源管理类的对应函数,实现如下所示:
// 资源管理类
public class ResourceManager {
private static final String TAG = "ResourceManager";
public void onActivityResume() {
Log.d(TAG,"doing something in onActivityResume");
}
public void onActivityStop() {
Log.d(TAG,"doing something in onActivityStop");
}
}
public class NotifyAcLifecycleActivity extends AppCompatActivity {
private ResourceManager mResourceManager = new ResourceManager();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notify_ac_lifecycle);
}
@Override
protected void onResume() {
super.onResume();
mResourceManager.onActivityResume();
}
@Override
protected void onStop() {
super.onStop();
mResourceManager.onActivityStop();
}
}
可以看出,通知式实现的生命周期监控具有以下显著缺陷:
- 代码侵入性强:需要在Activity中手动调用资源管理类的对应公共方法
- 耦合严重:资源管理类的公共方法和Activity生命周期函数强耦合,当资源管理类的数量发生变化时,新增或者删除,都需改动Activity代码
监听式监控
即然通知式监控具有那么多的缺陷,那么我们怎么来解决该问题呢?从操作意图可以看出,我们期望在Activity生命周期变化的时候资源管理类能收到通知,换句话说就是资源管理类可以监听到Activity的生命周期变更,说到监听,我们自然而言的想到了设计模式中的观察者模式。
观察者模式包含了被观察者和观察者两个角色,描述的是当被观察者状态发生变化时,所有依赖于该被观察者的观察者都可以接收到通知并根据需要完成操作
由观察者模式定义来看,Activity应该是被观察者,资源管理器应该是观察者,为进一步解耦,我们引入接口,定义观察者接口如下所示:
public interface LifecycleObserver {
void onActivityResume();
void onActivityStop();
}
在被观察者(Activity)中通知观察者,修改的代码如下:
public class ObserverLifecycleActivity extends AppCompatActivity {
private LifecycleObserver mObserver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_observer_lifecycle);
}
public void setObserver(LifecycleObserver observer) {
mObserver = observer;
}
@Override
protected void onResume() {
super.onResume();
if (mObserver != null) {
mObserver.onActivityResume();
}
}
@Override
protected void onStop() {
super.onStop();
if (mObserver != null) {
mObserver.onActivityStop();
}
}
}
使需要观察的对象实现观察者接口,并在onCreate中完成观察,代码如下:
public class ResourceManager implements LifecycleObserver{
private static final String TAG = "ResourceManager";
@Override
public void onActivityResume() {
Log.d(TAG,"doing something in onActivityResume");
}
@Override
public void onActivityStop() {
Log.d(TAG,"doing something in onActivityStop");
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_observer_lifecycle);
setObserver(new ResourceManager());
}
这样就通过LifecycleObserver完成了ResourceManager观察Activity生命周期变化的操作,如果不需要接收通知,不调用setObserver方法即可。
简单业务中,上述实现没问题,单随着业务的逐步扩大,资源管理器可能不止一个,而且并不一定需要一直监听变化,在一定情况下,可能需要移除,接下来我们进一步修改被观察者中关于观察者的管理,使其支撑多个观察者以及动态移除观察者,代码如下:
public class ObserverLifecycleActivity extends AppCompatActivity {
private List<LifecycleObserver> mObservers = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_observer_lifecycle);
addObserver(new ResourceManager());
}
public void addObserver(LifecycleObserver observer) {
mObservers.add(observer);
}
public void removeObserver(LifecycleObserver observer) {
mObservers.remove(observer);
}
@Override
protected void onResume() {
super.onResume();
if (mObservers != null && !mObservers.isEmpty()) {
for (LifecycleObserver observer : mObservers) {
observer.onActivityResume();
}
}
}
@Override
protected void onStop() {
super.onStop();
if (mObservers != null && !mObservers.isEmpty()) {
for (LifecycleObserver observer : mObservers) {
observer.onActivityStop();
}
}
}
}
从上述实现可以看出,该方案具有以下缺点:
- 不适用于多Activity场景
- 仍然需要耦合Activity的addObserver和removeObserver方法
ActivityLifecycleCallbacks
上面都是开发者实现的,那么系统内部有没有已经实现的方案呢?查看源码,可以找到ActivityLifecycleCallbacks,其定义如下:
public interface ActivityLifecycleCallbacks {
/**
* Called as the first step of the Activity being created. This is always called before
* {@link Activity#onCreate}.
*/
default void onActivityPreCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
}
/**
* Called when the Activity calls {@link Activity#onCreate super.onCreate()}.
*/
void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState);
/**
* Called as the last step of the Activity being created. This is always called after
* {@link Activity#onCreate}.
*/
default void onActivityPostCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
}
/**
* Called as the first step of the Activity being started. This is always called before
* {@link Activity#onStart}.
*/
default void onActivityPreStarted(@NonNull Activity activity) {
}
/**
* Called when the Activity calls {@link Activity#onStart super.onStart()}.
*/
void onActivityStarted(@NonNull Activity activity);
/**
* Called as the last step of the Activity being started. This is always called after
* {@link Activity#onStart}.
*/
default void onActivityPostStarted(@NonNull Activity activity) {
}
/**
* Called as the first step of the Activity being resumed. This is always called before
* {@link Activity#onResume}.
*/
default void onActivityPreResumed(@NonNull Activity activity) {
}
/**
* Called when the Activity calls {@link Activity#onResume super.onResume()}.
*/
void onActivityResumed(@NonNull Activity activity);
/**
* Called as the last step of the Activity being resumed. This is always called after
* {@link Activity#onResume} and {@link Activity#onPostResume}.
*/
default void onActivityPostResumed(@NonNull Activity activity) {
}
/**
* Called as the first step of the Activity being paused. This is always called before
* {@link Activity#onPause}.
*/
default void onActivityPrePaused(@NonNull Activity activity) {
}
/**
* Called when the Activity calls {@link Activity#onPause super.onPause()}.
*/
void onActivityPaused(@NonNull Activity activity);
/**
* Called as the last step of the Activity being paused. This is always called after
* {@link Activity#onPause}.
*/
default void onActivityPostPaused(@NonNull Activity activity) {
}
/**
* Called as the first step of the Activity being stopped. This is always called before
* {@link Activity#onStop}.
*/
default void onActivityPreStopped(@NonNull Activity activity) {
}
/**
* Called when the Activity calls {@link Activity#onStop super.onStop()}.
*/
void onActivityStopped(@NonNull Activity activity);
/**
* Called as the last step of the Activity being stopped. This is always called after
* {@link Activity#onStop}.
*/
default void onActivityPostStopped(@NonNull Activity activity) {
}
/**
* Called as the first step of the Activity saving its instance state. This is always
* called before {@link Activity#onSaveInstanceState}.
*/
default void onActivityPreSaveInstanceState(@NonNull Activity activity,
@NonNull Bundle outState) {
}
/**
* Called when the Activity calls
* {@link Activity#onSaveInstanceState super.onSaveInstanceState()}.
*/
void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState);
/**
* Called as the last step of the Activity saving its instance state. This is always
* called after{@link Activity#onSaveInstanceState}.
*/
default void onActivityPostSaveInstanceState(@NonNull Activity activity,
@NonNull Bundle outState) {
}
/**
* Called as the first step of the Activity being destroyed. This is always called before
* {@link Activity#onDestroy}.
*/
default void onActivityPreDestroyed(@NonNull Activity activity) {
}
/**
* Called when the Activity calls {@link Activity#onDestroy super.onDestroy()}.
*/
void onActivityDestroyed(@NonNull Activity activity);
/**
* Called as the last step of the Activity being destroyed. This is always called after
* {@link Activity#onDestroy}.
*/
default void onActivityPostDestroyed(@NonNull Activity activity) {
}
/**
* Called when the Activity configuration was changed.
* @hide
*/
default void onActivityConfigurationChanged(@NonNull Activity activity) {
}
}
从接口函数可以看出这是用于监听Activity生命周期事件的回调,我们可以在Application中使用registerActivityLifecycleCallbacks注册Activity生命周期的全局监听,当有Activity的生命周期发生变化时,就会回调该接口中的方法,代码如下:
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
}
});
随后我们就可以根据回调的Activity对象判定应该由哪个资源管理器响应对应的生命周期变化。
通常情况下,我们可以依赖该方法实现以下需求:
- 自定义的全局的Activity栈管理
- 用户行为统计收集
- Activity切入前后台后的资源申请或释放
- 应用前后台判定
- 页面数据保存与恢复
- ... etc
Lifecycle in Jetpack
Lifecycle相关内容可以参考前面发布的系列文章:
Instrumentation
从Activity启动流程可知每个Activity生命周期变化时,ActivityThread都会通过其内部持有的Instrumentation类的对象进行分发,如果我们能自定义Instrumentation类,用我们自定义的Instrumentation类对象替换这个成员变量,那么自然可以通过这个自定义Instrumentation类对象来监听Activity生命周期变化。
那么怎么修改ActivityThread类的mInstrumentation成员呢?自然要用反射实现了。
自定义Instrumentation类如下所示:
public class CustomInstrumentation extends Instrumentation {
private static final String TAG = "CustomInstrumentation";
private Instrumentation mBaseInstrumentation;
public CustomInstrumentation(Instrumentation instrumentation) {
super();
mBaseInstrumentation = instrumentation;
}
@Override
public void callActivityOnResume(Activity activity) {
super.callActivityOnResume(activity);
Log.d(TAG, "callActivityOnResume " + activity.toString());
}
@Override
public void callActivityOnStop(Activity activity) {
super.callActivityOnStop(activity);
Log.d(TAG, "callActivityOnStop " + activity.toString());
}
}
在Application的attachBaseContext函数中反射修改ActivityThread的mInstrumentation成员为CustomInstrumentation类的对象,相关代码如下:
@Override
protected void attachBaseContext(Context base) {
hookInstrumentation();
super.attachBaseContext(base);
}
public void hookInstrumentation() {
Class<?> activityThread;
try{
activityThread = Class.forName("android.app.ActivityThread");
Method sCurrentActivityThread = activityThread.getDeclaredMethod("currentActivityThread");
sCurrentActivityThread.setAccessible(true);
//获取ActivityThread 对象
Object activityThreadObject = sCurrentActivityThread.invoke(null);
//获取 Instrumentation 对象
Field mInstrumentation = activityThread.getDeclaredField("mInstrumentation");
mInstrumentation.setAccessible(true);
Instrumentation instrumentation = (Instrumentation) mInstrumentation.get(activityThreadObject);
CustomInstrumentation customInstrumentation = new CustomInstrumentation(instrumentation);
//将我们的 customInstrumentation 设置进去
mInstrumentation.set(activityThreadObject, customInstrumentation);
}catch (Exception e){
e.printStackTrace();
}
}
编写两个Activity分别为MainActivity和NotifyAcLifecycleActivity,在MainActivity中点击按钮跳转到NotifyAcLifecycleActivity,日志输出如下:
可以拿出,虽然正常代理到了Activity的生命周期变更,但是每次Activity启动都会爆出Uninitialized ActivityThread, likely app-created Instrumentation, disabling AppComponentFactory
的异常,查看源码,查找该问题的原因:
// Instrumentation.java
private ActivityThread mThread = null;
private AppComponentFactory getFactory(String pkg) {
if (pkg == null) {
Log.e(TAG, "No pkg specified, disabling AppComponentFactory");
return AppComponentFactory.DEFAULT;
}
if (mThread == null) {
Log.e(TAG, "Uninitialized ActivityThread, likely app-created Instrumentation,"
+ " disabling AppComponentFactory", new Throwable());
return AppComponentFactory.DEFAULT;
}
LoadedApk apk = mThread.peekPackageInfo(pkg, true);
// This is in the case of starting up "android".
if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
return apk.getAppFactory();
}
final void basicInit(ActivityThread thread) {
mThread = thread;
}
可以看到当mThread成员为空时,会抛出该问题,mThread是在basicInit中赋值的,由于我们创建的CustomInstrumentation对象没有调用该函数,故mThread必然为空,那么如何规避该问题呢?方案主要有两个方向
初始化CustomInstrumentation对象的mThread对象
反射获取原始Instrumentation对象的mThread取值,然后设置到自定义的CustomInstrumentation对象中
针对getFactory方法使用的函数,将函数重写,调用原始Instrumentation对应的函数
这里我们使用第二个方案,在CustomInstrumentation中重写newActivity方法,使用原始的Instrumentation对象代理,代码如下:
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return mBaseInstrumentation.newActivity(cl, className, intent);
}
再次运行,可以看到日志中不再打印该异常,同时我们也能正常监听到Activity生命周期变化了,详细日志如下:
综上,我们就可以在自定义Instrumentation类的callActivityOnStop方法中过滤某些Activity,在其切入后台时进行资源的释放。
不难看出,自定义Instrumentation走通后,我们可以在该类中接管系统的Activity启动,进而将某个目标Activity替换成我们自己的Activity,这也是插件化实现中的一个核心步骤
链接:https://juejin.cn/post/7199609821980229691
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。