首发于Jetpacker
利用LifeCycle解耦组件

利用LifeCycle解耦组件

在我们设计Android应用程序的组件时,通常希望组件与页面(Activity/Fragment)之间的耦合度能够尽量低。一行代码就能使用某个组件是最好的,但是组件的使用往往与页面的生命周期存在关联,我们通常需要在页面生命周期的不同阶段对组件进行适当的管理,比如有时我们需要在页面的onPause()方法停止组件,在页面的onDestroy()方法对组件进行资源回收,这样的工作非常繁琐,但又不得不做,因为可能会引发内存泄漏。

在应用程序的架构方面,也存在类似的问题。比如MVP模式中的P层,当页面生命周期发生变化的时候,我们需要在相应的回调方法中主动去通知P层,这样做不仅繁琐,使得模块间耦合度变高,更糟糕的是,开发者可能会在回调方法中忘记通知P层。

如何在生命周期发生变化时,得到通知,是开发者们经常遇到的问题,这在组件和架构的设计中显得尤为重要。为此,Google提供了LifeCycle作为解决方案。LifeCycle可以帮助开发者创建“可感知生命周期的组件”,这样组件便能够在其内部管理自己的生命周期,从而降低模块间的耦合度,并减少内存泄漏发生的可能性。


假设有这样一个常见的需求:在用户打开某个页面,获取用户当前的地理位置。

面对这样一个需求,我们通常会这样写代码:

public class MainActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        iniLocationManager();//初始化位置管理器
    }

    @Override
    protected void onResume()
    {
        startGetLocation();//开始获取用户地理位置
    }

    @Override
    protected void onPause()
    {
        stopGetLocation();//停止获取用户地理位置
    }
}

可以看到,获取地理位置这个需求的实现,与页面的生命周期是息息相关。如果我们希望将获取地理位置这一功能独立成一个组件,那么生命周期是必须考虑在内的。我们需要在页面生命周期的各个回调方法中,对组件进行通知,因为组件不能感知到生命周期的变化。

LifeCycle是怎么解决这个问题呢?Android主要提供了两个类LifecycleOwner(被观察者类)和LifecycleObserver(观察者)。通过观察者模式,实现对页面生命周期的回调与监听。通过查看源码,可以看到,新版本的sdk包中,Activity已经默认实现了LifecycleOwner接口。

也就是说Activity已经替你实现了”被观察者“应该实现的那一部分代码,你不需要额外做这些事情,如果你想要监听Activity的生命周期变化,你只要实现”观察者“那一部分的代码,即,让你希望在页面生命周期发生变化时得到通知的类,实现LifecycleObserver接口即可,该接口没有接口方法,无需任何其他实现。

LifecycleOwner接口中只有一个getLifecycle(LifecycleObserver observer)方法。 通过该方法实现”观察者模式“。

现在,让我们利用LifeCycle,改写获取用户地理位置这个需求。我们的目地是将这个功能从Activity中独立出去,减少耦合度的同时,又不影响对生命周期的监听。我们编写一个名为MyLocationListener的类,让它实现LifecycleObserver接口,并把获取地理位置相关的代码放到该类中,如下所示:

public class MyLocationListener implements LifecycleObserver//实现观察者接口
{
    public MyLocationListener(Activity context, OnLocationChangedListener onLocationChangedListener)
    {
        iniLocationManager();//初始化操作
    }

    /**
     * 使用注解  @OnLifecycleEvent 来表明该方法需要监听指定的生命周期事件
     * 当Activity执行了onResume()时,该方法会被自动调用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private void startGetLocation()
    {
        Log.d(TAG, "startGetLocation()");
    }

    /**
     * 使用注解  @OnLifecycleEvent 来表明该方法需要监听指定的生命周期事件
     * 当Activity执行了onPause()时,该方法会被自动调用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void stopGetLocation()
    {
        Log.d(TAG, "stopGetLocation()");
    }

    /**
     * 地理位置发生变化的时候,通过该接口通知调用者
     */
    public interface OnLocationChangedListener
    {
        void onChanged(double latitude, double longitude);
    }
}

在MainActivity中,只需要引用MyLocationListener即可,无需再关心Activity生命周期变化对获取地址位置所带来的影响。生命周期的管理完全交给MyLocationListener内部自行处理,在Activity中要做的只是通过getLifecycle().addObserver()方法将观察者与被观察者绑定起来即可。

public class MainActivity extends AppCompatActivity
{

    private MyLocationListener myLocationListener;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        myLocationListener = new MyLocationListener(this, new MyLocationListener.OnLocationChangedListener()
        {
            @Override
            public void onChanged(double latitude, double longitude)
            {
                //展示收到的位置信息
            }
        });

        //观察者模式。当MainActivity的生命周期发生变化时,会自动通知到MyLocationListener中使用@OnLifecycleEvent标签的方法
        getLifecycle().addObserver(myLocationListener);
    }
}

可以看到,LifeCycle完美解决了组件对页面生命周期的依赖。使得组件能够自己管理其生命周期,而不需要页面的生命周期对其进行管理,降低了代码的耦合度,提高了组件的复用程度,也减少了由于对生命周期管理的疏忽也引起的内存泄漏,这在项目工程量大的情况下非常有帮助。

除了Activity之外,在新版本的sdk中,Fragment同样默认实现了LifecycleOwner接口。所以以上案例同样适用于Fragment。

对于Service和整个应用程序的生命周期,Android也提供了相应的LifeCycle解决方案,我们会在接下来的文章里面进行讨论。

项目源码:

注意:项目源码实现了在Activity中获取地理位置这样一个功能,但是在实际使用中,还需要考虑到权限等问题,本项目更多只是为了说明LifeCycle的使用方式和能解决的问题,在实际生产环境中,这样获取地理位置可能并不是最佳的解决方案。

michaelye/LifecycleDemogithub.com图标


项目演示:

编辑于 2019-07-30

文章被以下专栏收录