使用LifeCycle解耦Activity与普通组件
-
案例分析
假设我们有一个需求:用户打开我们的界面时,获取用户当前的地理位置。
如果我们用普通写法,就会将相应方法的调用写在Activity的生命周期回调方法中:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "初始化位置管理器!"); ... } @Override protected void onResume() { super.onResume(); Log.d(TAG, "获取地理位置!"); } @Override protected void onPause() { super.onPause(); Log.d(TAG, "停止获取地理位置!"); }
上诉代码能实现我们所提的需求功能,但是Activity和我们的业务逻辑耦合度太大。这个时候我们需要将获取地理位置这一功能独立成一个组件。但是我们要考虑Activity的生命周期,当Activity生命周期发生变化时,会对我们自定义组件进行通知,然后调用其相应的处理方法。该怎么实现这一功能呢?
-
LifeCycle的实现步骤
Jetpack为我们提供了两个类:LifecycleOwner(被观察者)和LifecycleObserver(观察者),通过观察者模式,实现对页面生命周期的监听。
Activity和Fragment默认已经实现了LifecycleOwner,我们让自定义组件实现LifecycleObserver接口即可,下面以Activity为例:
第一步:在app的build.gradle文件中添加相关依赖
dependencies { ... implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' ... }
第二步:编写自定义组件MyLocationListener类,实现LifecycleObserver接口,获取地理位置的逻辑代码在这个类中编写。
public class MyLocationListener implements LifecycleObserver { private static final String TAG = "MyLocationListener"; public MyLocationListener() { Log.d(TAG, "初始化操作!"); } /** * 当Activity执行onResume()方法时,该方法会被自动调用 */ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) private void startGetLocation() { Log.d(TAG, "startGetLocation"); } /** * 当Activity执行onPause()方法时,该方法会被自动调用 */ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void stopGetLocation() { Log.d(TAG, "stopGetLocation"); } }
在上诉代码中,对于自定义组件中的那些需要在Activity生命周期发生变化时得到通知的方法,我们需要在这些方法上使用@OnlifecycleEvent(Lifecycle.Event.On_XXX)注解进行标识。这样,当Activity生命周期发生变化时,这些被标识过的方法会被自动调用。
第三步:在MainActivity中引用自定义组件,再通过getLifecycle().addObserver()方法,将观察者与被观察者绑定起来。
public class MainActivity extends AppCompatActivity { private MyLocationListener myLocationListener; private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myLocationListener = new MyLocationListener(); //将观察者与被观察者绑定 getLifecycle().addObserver(myLocationListener); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "获取地理位置!"); } @Override protected void onPause() { super.onPause(); Log.d(TAG, "停止获取地理位置!"); } }
运行上诉代码,结果和我们普通写法的结果一样。
使用LifeCycleService解耦Service与普通组件
-
LifeCycleService
为了便于对Service生命周期的监听,Android提供了一个名为LifecycleService的类,该类继承自Service,并实现了LifecycleOwner接口。它也提供了getLifecycle()方法。
-
LifecycleService的使用方法
第一步:添加依赖,和上诉Activity添加的一样。
第二步:创建一个名为MyService的类,让其继承自LifeCycleService。由于LifeCycleService继承自Service,所以用法和普通的Service一样。
public class MyService extends LifecycleService { private static final String TAG = "MyService"; private MyServiceObserver myServiceObserver; public MyService() { Log.d(TAG, "MyService"); myServiceObserver = new MyServiceObserver(); getLifecycle().addObserver(myServiceObserver); } }
第三步:自定义MyService类,并实现LifecycleObserver接口。接着使用@OnLifecycleEvent注解对那些你希望能够在Service生命周期发生变化时得到同步调用的方法进行标识。
public class MyServiceObserver implements LifecycleObserver { private String TAG = this.getClass().getName(); /** * 当Service执行onCreate()方法时,该方法会被自动调用 */ @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) private void startGetLocation() { Log.d(TAG, "startGetLocation"); } /** * 当Service执行onDestroy()方法时,该方法会被自动调用 */ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) public void stopGetLocation() { Log.d(TAG, "stopGetLocation"); } }
第四步:在主Activity中添加两个Button,一个用于启动Service,一个用于停止Service
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(MainActivity.this, MyService.class); startService(intent); } }); findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(MainActivity.this, MyService.class); stopService(intent); } });
第五步:千万不要忘记,在配置文件中声明Service。在标签下声明
<service android:name=".MyService" />
运行代码,查看日志发现。随着Service生命周期的变化,MyServiceObserver中带有对应注解的方法被自动调用了。因此,LifecycleService类很好的实现了自定义组件和Service之间的解耦。
使用ProcessLifecycleOwner监听应用程序的生命周期
-
ProcessLifecycleOwner
LifeCycle提供了一个名为ProcessLifecycleOwner的类,方便我们知道整个应用程序的生命周期。
-
ProcessLifecycleOwner的使用方法
第一步:添加依赖,和Activity、Service添加方式一样。
第二步:在MainActivity的onCreate()方法中进行绑定
ProcessLifecycleOwner.get().getLifecycle().addObserver(new ApplicationObserver());
第三步:自定义ApplicationObserver类,让该类实现LifecycleObserver接口,负责对应用程序生命周期的监听
public class ApplicationObserver implements LifecycleObserver { private String TAG = this.getClass().getName(); /** * 在应用程序的整个生命周期中只会被调用一次 */ @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) public void onCreate() { Log.d(TAG, "Lifecycle.Event.ON_CREATE"); } /** * 当应用程序出现在前台时调用 */ @OnLifecycleEvent(Lifecycle.Event.ON_START) public void onStart() { Log.d(TAG, "Lifecycle.Event.ON_START"); } /** * 当应用程序出现在前台时调用 */ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void onResume() { Log.d(TAG, "Lifecycle.Event.ON_RESUME"); } /** * 当应用程序退出到后台时调用 */ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void onPause() { Log.d(TAG, "Lifecycle.Event.ON_PAUSE"); } /** * 当应用程序退出到后台时调用 */ @OnLifecycleEvent(Lifecycle.Event.ON_STOP) public void onStop() { Log.d(TAG, "Lifecycle.Event.ON_STOP"); } /** * 永远不会被调用,系统不会分发调用ON_DESTROY事件 */ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) public void onDestroy() { Log.d(TAG, "Lifecycle.Event.ON_DESTROY"); } }
如此,就可以随时随地监听我们的应用程序何时在前台,何时退出到后台。利用ProcessLifecycleOwner类,不会给我们的项目增加任何的耦合度。
注意:
-
ProcessLifecycleOwner是针对整个应用程序,而不是对某个Activity。所以只有两种情况,应用程序可见和不可见。
-
Lifecycle.Event.ON_CREATE事件在整个生命周期内只会被分发一次。而永远不会分发Lifecycle.Event.ON_DESTROY事件。
屏幕旋转时也会分发Lifecycle.Event.ON_CREATE。
-
当应用程序由前台退出到后台时,调用Lifecycle.Event.ON_PAUSE和Lifecycle.Event.ON_STOP标记的方法时会有一定的延时,这是因为系统需要判断“屏幕是否发生旋转”,系统需要保证在屏幕发生旋转时不会调用这两个方法。对于应用程序来说,屏幕旋转前后程序都在前台,不需要pause和stop。但是会像Activity一样分发onCreate、onStart、onResume事件。
-
LifeCycle总结
一般来说,我们自定义实现观察者接口的组件可称为生命周期感知型组件,它可以检测到系统组件(Activity、Fragment、Service、Application)的生命周期的变化。没有LifeCycle时,我们可能会在Activity的生命周期的方法中依赖自定义组件的操作。这种模式会导致代码条理性很差,耦合性很大。通过使用LifeCycle,我们可以把自定义组件的逻辑操作代码从生命周期方法中移出到自定义组件本身。
LifeCycle存在的意义就是帮助我们解耦,让自定义组件感受到系统组件的生命周期变化,再进一步执行相关操作。这样可以有效避免内存泄漏等一些问题。
ps:
- 本文仅介绍LifeCycle的基本用法,关于和ViewModel、LiveData等组件的最佳结合用法请参考官网文档,或者期待作者新文章。
- 本文笔记整理自《Android Jetpack应用指南》和官方文档,有错误欢迎评论留言指正,感谢。