Android Jetpack开发:原理解析与应用实战
上QQ阅读APP看书,第一时间看更新

2.2 Lifecycle的基本使用

2.2.1 使用Lifecycle优化广告引导页的需求

什么是Lifecycle呢?Lifecycle是Jetpack架构组件中用来感知生命周期的组件,使用Lifecycle可以帮助开发者写出与生命周期相关且更简洁、更易维护的代码。这单纯从定义上可能并不好理解,接下来通过优化上面所述广告引导页的功能来展示Lifecycle的具体使用方法。

首先在项目中添加Lifecycle组件的依赖项,代码如下:

dependencies {
    ...
    def lifecycle_version = "2.2.0"
    implementation
"androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    ...
}

接着在广告管理类AdvertisingManage中实现LifecycleObserver接口,代码如下:

object AdvertisingManage:LifecycleObserver {
    ...
}

通过LifecycleObserver的源码可以看出,LifecycleObserver是一个空接口,所以开发者不需要实现额外的方法。

然后通过OnLifecycleEvent注解将方法与生命周期绑定,比如,要在Activity onCreate的生命周期中执行AdvertisingManage类的onStart方法,且在onDestroy的生命周期中执行onCancel方法,那么可以采用如下代码:

/**
* 开始计时
*/
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun start() {
        Log.d(TAG, "开始计时")
        countDownTimer?.start()
}
/**
* 停止计时
*/
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onCancel() {
        Log.d(TAG, "停止计时")
        countDownTimer?.cancel()
        countDownTimer = null
}

当然仅这样还不行,还要在Activity中通过addObserver方法注册Advertising-Manage,此时应该将之前在Activity生命周期中主动调用的方法移除。修改后Activity的代码如下:

class AdvertisingActivity : AppCompatActivity() {
    //跳过广告按钮
    lateinit var btnIngore: Button
    //广告时间
    lateinit var tvAdvertisingTime: TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_advertising)
        val advertisingManage = AdvertisingManage()
        lifecycle.addObserver(advertisingManage)
        btnIngore = findViewById(R.id.btn_ignore)
        tvAdvertisingTime = findViewById(R.id.tv_advertising_time)
        advertisingManage.advertisingManageListener =
                object : AdvertisingManage.AdvertisingManageListener {
            override fun timing(second: Int) {
                tvAdvertisingTime.text = "广告剩余$second秒"
            }
            override fun enterMainActivity() {
                MainActivity.actionStart(this@AdvertisingActivity)
                finish()
            }
        }
        //跳过广告点击事件
        btnIngore.setOnClickListener {
            MainActivity.actionStart(this@AdvertisingActivity)
            finish()
        }
}

再次运行程序,打印日志如图2-3所示。

022-1

图2-3 打印日志

从图2-3中可以看到,最终结果与之前的结果无异,但是使用Lifecycle改造后的实现方式极大简化了Activity中的代码逻辑,这种方式也使得业务功能与Activity业务逻辑分离。

现在回过头来看OnLifecycleEvent的注解方法,其中,Lifecycle.Event的枚举如下:

public enum Event {
    /**
     * Constant for onCreate event of the {@link LifecycleOwner}.
     */
    ON_CREATE,
    /**
     * Constant for onStart event of the {@link LifecycleOwner}.
     */
    ON_START,
    /**
     * Constant for onResume event of the {@link LifecycleOwner}.
     */
    ON_RESUME,
    /**
     * Constant for onPause event of the {@link LifecycleOwner}.
     */
    ON_PAUSE,
    /**
     * Constant for onStop event of the {@link LifecycleOwner}.
     */
    ON_STOP,
    /**
     * Constant for onDestroy event of the {@link LifecycleOwner}.
     */
    ON_DESTROY,
    /**
     * An {@link Event Event} constant that can be used to match all events.
     */
    ON_ANY;
}

上述代码中,前面几个状态分别对应Activity的生命周期,最后一个ON_ANY状态则表示可对应Activity的任意生命周期。

再来看注册的方法lifecycle.addObserver(AdvertisingManage),为什么它可以直接调用getLifecycle方法呢?那是因为getLifecycle是接口LifecycleOwner的实现方法。LifecycleOwner的源码如下:

public interface LifecycleOwner {
    /**
     * Returns the Lifecycle of the provider.
     *
     * @return The lifecycle of the provider.
     */
    @NonNull
    Lifecycle getLifecycle();
}

通过追溯源码可以发现,当前Activity继承的是androidx.core.app.Component-Activity,而ComponentActivity实现了LifecycleOwner接口,所以开发者可以直接调用getLifecycle方法。实现ComponentActivity类的源码如下:

public class ComponentActivity extends Activity implements
        LifecycleOwner,
        KeyEventDispatcher.Component {
    ...
}

除了ComponentActivity之外,在ComponentActivity的子类androidx.fragment.app.FragmentActivity、androidx.appcompat.app.AppCompatActivity以及androidx.fragment.app.Fragment中都是可以直接使用Lifecycle的,这是AndroidX帮助开发者完成的。

如果当前Activity继承的是没有实现LifecycleOwner接口的android.app.Activity,会发生什么呢?为了便于测试,这里直接将当前Activity的继承修改为android.app.Activity,之后你会看到lifecycle.addObserver(AdvertisingManage)这行代码报错了,这就是因为android.app.Activity没有实现getLifecycle方法,这时,自定义LifecycleOwner就派上用场了。

2.2.2 自定义LifecycleOwner

使用getLifecycle方法的前提是当前父类实现了LifecycleOwner接口,因此若需要在没有实现LifecycleOwner接口的类中使用该方法,则需要自定义LifecycleOwner。

首先,让继承自Activity类的页面实现LifecycleOwner接口并重写getLifecycle方法,修改后的代码如下:

class AdvertisingActivity : Activity(), LifecycleOwner {
    //跳过广告按钮
    lateinit var btnIngore: Button
    //广告时间
    lateinit var tvAdvertisingTime: TextView
    lateinit var lifecycleRegistry: LifecycleRegistry
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_advertising)
        val advertisingManage = AdvertisingManage()
        lifecycle.addObserver(advertisingManage)
        btnIngore = findViewById(R.id.btn_ignore)
        tvAdvertisingTime = findViewById(R.id.tv_advertising_time)
        advertisingManage.advertisingManageListener =
            object : AdvertisingManage.AdvertisingManageListener {
                override fun timing(second: Int) {
                    tvAdvertisingTime.text = "广告剩余$second秒"
                }
                override fun enterMainActivity() {
                    MainActivity.actionStart(this@AdvertisingActivity)
                    finish()
                }
            }
        //跳过广告点击事件
        btnIngore.setOnClickListener {
            MainActivity.actionStart(this@AdvertisingActivity)
            finish()
        }
    }
    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }
}

如此,就实现了与继承AppCompatActivity时同样的效果。这样一来就可以通过注解方法主动执行对应生命周期的方法了。如果开发者想主动获取当前Activity的生命周期状态,又该如何做呢?

开发者可以使用Lifecycle的getCurrentState方法,从源码可以看出,getCurrentState会返回如下种类的State:

public enum State {
    DESTROYED,
    INITIALIZED,
    CREATED,
    STARTED,
    RESUMED;
}

State种类状态返回值与Activity生命周期的对应关系如图2-4所示。

026-1

图2-4 State返回值与Activity生命周期的对应关系

如果getCurrentState返回的值是STARTED,则说明当前Activity已经执行了onStart方法,但是还未执行onResume方法,其他返回值可以此类推。Lifecycle的使用场景有很多,比如,在下载文件的需求中,想要节省流量,那么可以在App处于后台时停止下载,当App置于前台时又能自动恢复下载。这个功能的具体实现就交给读者去尝试了。接下来看看在实际项目中Lifecycle还可以解决哪些常见的问题。