1. 案例分析
假设有这样一个常见的需求:在用户打开某个页面时,获取用户当前的地理位置。面对该需求,我们通常会这样写代码。
package com.kf.ui;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
/**
* TODO Description 文件描述信息
*
* @author nomi
* @package com.kf.ui
* @date 2021/7/22-10:56 上午
*/
public class LocationActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//初始化位置管理器
initLocationManager();
}
/**
* 初始化定位操作
*/
private void initLocationManager() {
Log.d("Location", "initLocationManager");
}
@Override
protected void onResume() {
super.onResume();
//开始获取用户的地理位置
startGetLocation();
}
@Override
protected void onPause() {
super.onPause();
//停止获取用户的地理位置
stopGetLocation();
}
/**
* 当Activity 执行 onResume() 方法时,该方法会被自动调用
*/
private void startGetLocation() {
Log.d("Location", "startGetLocation");
}
/**
* 当Activity 执行 onPause() 方法时,该方法会被自动调用
*/
private void stopGetLocation() {
Log.d("Location", "stopGetLocation");
}
}
从以上代码可以看出,获取地理位置这个需求的实现,与页面的生命周期息息相关。如果我们希望将获取地理位置这一功能独立成一个组件,那么生命周期是必须要考虑在内的。我们不得不在页面生命周期的各个回调方法中,对组件进行通知,因为组件不能主动感知生命周期的变化。
2. LifeCycIe的原理
LifeCycle是如何解决这个问题的呢?Jetpack为我们提供了两个类:LifecycleOwner(被观察者)和LifecycleObserver(观察者)。即通过观察者模式,实现对页面生命周期的监听。通过查看SupportActivity的源码,可以看到,在新版本的SDK包中,Activity已经默认实现了LifecycleOwner接口。LifecycleOwner接口中只有一个getLifecycle(LifecycleObserverobserver)方法,LifecycleOwner正是通过该方法实现观察者模式的。源码示例如下。
从以上源码可知,SupportActivity已经替我们实现了被观察者应该实现的那一部分代码。因此,我们不需要再去实现这部分代码。当我们希望监听Activity的生命周期时,只需要实现观察者那一部分的代码,即让自定义组件实现LifecycleObserver接口即可。该接口没有接口方法,无须任何具体实现。
SupportActivity和ComponentActivity的代码区别不大:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
private final SavedStateRegistryController mSavedStateRegistryController =
SavedStateRegistryController.create(this);
// Lazily recreated from NonConfigurationInstances by getViewModelStore()
private ViewModelStore mViewModelStore;
private ViewModelProvider.Factory mDefaultFactory;
private final OnBackPressedDispatcher mOnBackPressedDispatcher =
new OnBackPressedDispatcher(new Runnable() {
@Override
public void run() {
ComponentActivity.super.onBackPressed();
}
});
@LayoutRes
private int mContentLayoutId;
/**
* Default constructor for ComponentActivity. All Activities must have a default constructor
* for API 27 and lower devices or when using the default
* {@link android.app.AppComponentFactory}.
*/
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
if (lifecycle == null) {
throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
+ "constructor. Please make sure you are lazily constructing your Lifecycle "
+ "in the first call to getLifecycle() rather than relying on field "
+ "initialization.");
}
if (Build.VERSION.SDK_INT >= 19) {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_STOP) {
Window window = getWindow();
final View decor = window != null ? window.peekDecorView() : null;
if (decor != null) {
decor.cancelPendingInputEvents();
}
}
}
});
}
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
}
3. 解决方案
现在,让我们利用LifeCycle改写该需求。我们的目的是将该功能从Activity中独立出去,在减少耦合度的同时,又不影响对生命周期的监听。1.编写一个名为LocationLifeCycleListener
的类。该类就是我们的自定义组件,我们需要让该组件实现LifecycleObserver接口。与获取地理位置相关的代码在该类中完成。对于组件中那些需要在页面生命周期发生变化时得到通知的方法,我们需要在这些方法上使用@OnLifecycleEvent(Lifecycle.Event.ON_XXX)标签进行标识。这样,当页面生命周期发生变化时,这些被标识过的方法便会被自动调用。如下所示。
package com.kf.ui.util;
import android.app.Activity;
import android.util.Log;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
/**
* TODO Description 文件描述信息
*
* @author nomi
* @package com.kf.ui.util
* @date 2021/7/22-10:46 上午
*/
public class LocationLifeCycleListener implements LifecycleObserver {
public LocationLifeCycleListener(Activity context, OnLocationChangedListener onLocationChangedListener) {
//初始化位置管理器
initLocationManager();
}
/**
* 初始化定位操作
*/
private void initLocationManager() {
Log.d("Location", "initLocationManager");
}
/**
* 当Activity 执行 onResume() 方法时,该方法会被自动调用
*/
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private void startGetLocation() {
Log.d("Location", "startGetLocation");
}
/**
* 当Activity 执行 onPause() 方法时,该方法会被自动调用
*/
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
private void stopGetLocation() {
Log.d("Location", "stopGetLocation");
}
public interface OnLocationChangedListener {
/**
* 获取定位信息
*
* @param latitude
* @param longitude
*/
void onChanged(double latitude, double longitude);
}
}
在MainActivity中,只需要引用MyLocationListener即可,不用再关心Activity生命周期变化对该组件所带来的影响。生命周期的管理完全交给MyLocationListener内部自行处理。在Activity中要做的只是通过getLifecycle().addObserver()方法,将观察者与被观察者绑定起来,代码如下所示。
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
/**
* TODO Description 文件描述信息
*
* @author nomi
* @package com.kf.ui
* @date 2021/7/22-10:56 上午
*/
public class LocationLifecycleActivity extends AppCompatActivity {
private LocationLifeCycleListener locationLifeCycleListener;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
locationLifeCycleListener = new LocationLifeCycleListener(this, new LocationLifeCycleListener.OnLocationChangedListener() {
@Override
public void onChanged(double latitude, double longitude) {
//展示收到的位置信息
}
});
//将观察者与被观察者绑定
getLifecycle().addObserver(locationLifeCycleListener);
}
}
LifeCycle完美解决了组件对页面生命周期的依赖问题,使组件能够自己管理其生命周期,而无须在页面中对其进行管理。这无疑大大降低了代码的耦合度,提高了组件的复用程度,也杜绝了由于对页面生命周期管理的疏忽而引发的内存泄漏问题,这在项目工程量大的情况下是非常有帮助的。
除Activity之外,在新版本的SDK中,Fragment同样也默认实现了LifecycleOwner接口。因此,以上案例同样适用于Fragment。Fragment的源码如下所示。
摘自 叶坤 著《Android Jetpack应用指南》item.jd.com/12684819.ht…