3.2 使用ViewModel解决广告引导页屏幕旋转问题

在上一节中提到了ViewModel类可在发生屏幕旋转等配置更改后让数据继续留存,这时,细心的读者就会思考了,该如何使用ViewModel来解决广告引导页屏幕旋转问题呢?

在使用ViewModel之前,先引入ViewModel的依赖项,代码如下:

def lifecycle_version = "2.3.1"
implementation
"androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

然后新建一个继承自ViewModel的AdvertisingViewModel类,在该类中声明计时起始变量millisInFuture,代码如下:

class AdvertisingViewModel : ViewModel() {
    /**
     * 计时开始时间,默认5秒
     */
    var millisInFuture: Long = 5000
}

若开发者想在ViewModel类中使用资源文件,则要使用到Context上下文了。这里要注意的是,一定不能将Activity的上下文传给ViewModel,否则会存在内存泄漏的风险。这一点本书后面会有详细的讲解。那这里该如何处理呢?只需要将父类ViewModel修改为AndroidViewModel即可。示例如下:

class AdvertisingViewModel(application: Application) :
AndroidViewModel(application) {
    /**
     * 计时开始时间,默认5秒
     */
    var millisInFuture: Long = 5000
}

拥有Application的实例之后就可以访问资源文件了。在Activity中初始化ViewModel,示例如下:

private lateinit var advertisingViewModel: AdvertisingViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        advertisingViewModel = ViewModelProvider(this).get(AdvertisingViewModel::class.java)
        ...
}

接下来只需要将原Activity中的millisInFuture变量统一替换为AdvertisingView-Model中的millisInFuture即可。示例代码如下:

class AdvertisingActivity : AppCompatActivity() {
    ...
    private lateinit var advertisingViewModel: AdvertisingViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_advertising)
        advertisingViewModel = ViewModelProvider(this).get(AdvertisingViewModel::class.java)
        val advertisingManage = AdvertisingManage(advertisingViewModel.millisInFuture)
        lifecycle.addObserver(advertisingManage)
        tvAdvertisingTime = findViewById(R.id.tv_advertising_time)
        advertisingManage.advertisingManageListener =
            object : AdvertisingManage.AdvertisingManageListener {
                override fun timing(second: Int) {
                    tvAdvertisingTime.text = "广告剩余$second秒"
                    advertisingViewModel.millisInFuture = second.toLong() * 1000
                }
            }
        ...
    }
}

运行程序,当广告剩余2秒的时候,旋转屏幕,打印的日志如图3-4所示。

038-1

图3-4 广告剩余2秒时旋转屏幕的日志

从日志中可以看出,广告剩余2秒的时候,由于旋转屏幕停止计时了,因此旋转后会继续从1秒钟开始计时,这与使用onSaveInstanceState方法实现的效果相同。但是从代码中可以很明显地看出,使用ViewModel远比使用onSaveInstanceState简洁,这一切都得益于ViewModel的生命周期。