文章目录
四篇文章带你快速入门Jetpck(上)之Lifecycle,LiveData -
- Jetpack
-
- 官方推荐架构
- Lifecycle
-
- UI的生命周期
- Lifecycles的基本用法
- Lifecycle状态图
- 示例
- LiveData
-
- 简介
- 使用LiveData
-
- MutableLiveData
- Transformations.map
- MediatorLiveData
- SwitchMap
- 示例
Jetpack
Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者精力集中编写重要的代码。
官方推荐架构
请注意,每个组件仅依赖于其下一级的组件。例如,Activity 和 Fragment 仅依赖于视图模型。存储区是唯一依赖于其他多个类的类;在本例中,存储区依赖于持久性数据模型和远程后端数据源。
Lifecycle
感知Activity的生命周期并不复杂,但问题在于,在一个Activity中去感知它的生命周期非常简单,而如果要在一个非Activity的类中去感知Activity的生命周期,应该怎么办呢?
Lifecycles组件就是为了解决这个问题而出现的,它可以让任何一个类都能轻松感知到Activity的生命周期,同时又不需要在Activity中编写大量的逻辑处理。
UI的生命周期
Lifecycles的基本用法
新建一个MyObserver类,并让它实现LifecycleObserver接口,然后使用方法注解就能感知到Activity的生命周期了:
class MyObserver : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun activityStart() { Log.d("MyObserver", "activityStart") } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun activityStop() { Log.d("MyObserver", "activityStop") } }
另外一种方式:实现LifecycleEventObserver接口。
lifecycle.addObserver(object : LifecycleEventObserver { override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { when (event) { Lifecycle.Event.ON_CREATE -> { } Lifecycle.Event.ON_START -> { } Lifecycle.Event.ON_RESUME -> { } Lifecycle.Event.ON_PAUSE -> { } Lifecycle.Event.ON_STOP -> { } Lifecycle.Event.ON_DESTROY -> { } } } })
最后,在Activity中调用addObserver()方法来观察LifecycleOwner的生命周期:
class MainActivity : AppCompatActivity() { … override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) … lifecycle.addObserver(MyObserver()) } … }
Lifecycle状态图
示例
LifecycleActivity
class LifecycleActivity : AppCompatActivity() { private val lifecycleObject = LifecycleObject() val TAG = this.javaClass.simpleName override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_lifecycle) Log.d(TAG, "onCreate") //lifecycleObject.onCreate() //LifecycleObject1(this) LifecycleObject2(this) } // override fun onResume() { // super.onResume() // lifecycleObject.onResume() // Log.d(TAG, "onResume") // } // // override fun onPause() { // super.onPause() // lifecycleObject.onPause() // Log.d(TAG, "onPause") // } // // override fun onDestroy() { // super.onDestroy() // lifecycleObject.onDestroy() // Log.d(TAG, "onDestroy") // } }
LifecycleObject.kt
class LifecycleObject { val TAG = this.javaClass.simpleName private lateinit var player: MediaPlayer fun onCreate() { player = MediaPlayer() Log.d(TAG, "onCreate") } fun onResume() { thread { SystemClock.sleep(3000) player.start() Log.d(TAG, "onResume") } } fun onPause() { player.stop() Log.d(TAG, "onPause") } fun onDestroy() { player.release() Log.d(TAG, "onDestroy") } } class LifecycleObject1(private val owner: LifecycleOwner) : LifecycleObserver { private lateinit var player: MediaPlayer val TAG = this.javaClass.simpleName init { owner.lifecycle.addObserver(this) } @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) fun onCreate() { player = MediaPlayer() Log.d(TAG, "onCreate") } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { thread { SystemClock.sleep(3000) if (owner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { player.start() Log.d(TAG, "onResume") } } } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { player.stop() Log.d(TAG, "onPause") } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { player.release() Log.d(TAG, "onDestroy") } } class LifecycleObject2(private val owner: LifecycleOwner) : LifecycleEventObserver { private lateinit var player: MediaPlayer val TAG = this.javaClass.simpleName init { owner.lifecycle.addObserver(this) } override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { when (event) { Lifecycle.Event.ON_CREATE -> { player = MediaPlayer() Log.d(TAG, "onCreate") } Lifecycle.Event.ON_START, Lifecycle.Event.ON_RESUME -> { thread { SystemClock.sleep(3000) if (owner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { player.start() Log.d(TAG, "onResume") } } } Lifecycle.Event.ON_PAUSE, Lifecycle.Event.ON_STOP -> { player.stop() Log.d(TAG, "onPause") } Lifecycle.Event.ON_DESTROY -> { player.release() Log.d(TAG, "onDestroy") } Lifecycle.Event.ON_ANY -> Log.d(TAG, "onAny") } } }
LiveData
添加依赖库
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-beta01'
简介
LiveData 是可被观察的数据持有类。具有生命周期(Activity/Fragment/Service)感知的(确保active状态下接受data更新)。
LiveData是Jetpack提供的一种响应式编程组件,它可以包含任何类型的数据,并在数据发生变化的时候通知给观察者。
LiveData特别适合与ViewModel结合在一起使用,虽然它也可以单独用在别的地方,但是绝大多数情况下,它都是使用在ViewModel当中的。
-
背景 原文
Android开发中 MVX的开发架构设计,生命周期的感知对于Controller/Presenter/ViewModel不是天然可知。数据资源与泄露的问题。
-
2018年 google的AAC(Android Architecture Components)。一套组合的Jetpack组件库,使得ViewModel具有生命周期感知能力。同时也有了数据的感知能力(LiveData)
-
理解LiveData
不同于rxjava的观察模式,这里仅通知处于active状态的观察者。
一旦观察者回复Resume状态,就会收到最新的数据(有利有弊,特殊场景)
使用LiveData
LiveData是抽象类
MutableLiveData
//声明一个liveData val liveA = mutableLiveData<String>() //在需要的时候赋值 liveA.value = "some value of liveA" //在UI中,观察,在active状态下可以感知变化 val liveAObserver = Observer<String>{ value?.let{ //do something } } liveA.observe(vivewLifeCycleOwner,liveAObserver)
Transformations.map
//数据的来源多样,赋值于UI需要转换 liveA.map{ //转换规则 }
liveData的数据,不会通知inActive的观察者刷新数据,但是当observer恢复Resume的active后,也会得到最新的data
MediatorLiveData
中介者,媒介,将多个liveData的数据,合并处理成一个LiveData
mediator的liveData可以监听A,B两个数据源的变化,通过addSource后,并响应A/B的变化,转化为mediator的变化。
- 如果inactive下,A,B都变化,则resume后,也只接受最新的变化
SwitchMap
用于数据源的转化,多数据源的切换和控制
配合mediator的liveData使用,根据条件,选择数据源
示例
LiveDataActivity
class LiveDataActivity : AppCompatActivity() { val TAG = this.javaClass.simpleName // 步骤1 val testLiveData = MutableLiveData<String>() //步骤2 val liveMappedData = testLiveData.map { it.hashCode() } //步骤3 val liveData1 = MutableLiveData<Int>() val liveData2 = MutableLiveData<Int>() val mediatorLive = MediatorLiveData<Pair<String, String>>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_live_data) val liveDataFragment = LiveDataFragment() supportFragmentManager.beginTransaction() .add(R.id.fl_container_live, liveDataFragment) .commit() btn_create_fg_live.setOnClickListener { supportFragmentManager.beginTransaction() .attach(liveDataFragment) .commit() Log.d(TAG, "onCreate 显示 ${liveDataFragment.isVisible}") } btn_destroy_fg_live.setOnClickListener { supportFragmentManager.beginTransaction() .detach(liveDataFragment) .commit() Log.d(TAG, "onCreate 隐藏 ${liveDataFragment.isVisible}") } btn_change_live.setOnClickListener { testLiveData.value = "当前liveData的值为:${(10000..99999).random()}" } // 步骤1 testLiveData.observe(this, { tv_live_data_activity.text = it Log.d(TAG, "LiveData在LiveDataActivity中 $it") }) liveMappedData.observe(this, { tv_mapped_data_activity.text = it.toString() Log.d(TAG, "LiveData在LiveDataActivity中 map 后 $it") }) btn_change_live1.setOnClickListener { Log.d(TAG, "liveData1 = ${liveData1.value}") liveData1.value = (10000..99999).random() } btn_change_live2.setOnClickListener { Log.d(TAG, "liveData2 = ${liveData2.value}") liveData2.value = (10000..99999).random() } mediatorLive.addSource(liveData1) { Log.d(TAG, "live1 = $it") mediatorLive.value = "live1 = " to it.toString() } mediatorLive.addSource(liveData2) { Log.d(TAG, "live2 = $it") mediatorLive.value = "live2 = " to it.toString() } } }
activity_live_data.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".fourth.LiveDataActivity"> <TextView android:id="@+id/tv_live_data_activity" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="当前liveData的值:" android:textColor="#000000" android:textSize="20dp" tools:text="当前liveData的值:" /> <TextView android:id="@+id/tv_mapped_data_activity" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="Map后liveData值:" android:textColor="#000000" android:textSize="20dp" tools:text="Map后liveData值:" /> <FrameLayout android:id="@+id/fl_container_live" android:layout_width="match_parent" android:layout_height="300dp" android:background="@color/colorAccent" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/btn_create_fg_live" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="显示Fragment" /> <Button android:id="@+id/btn_destroy_fg_live" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="隐藏Fragment" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/btn_change_live" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="改变Live值" /> <Button android:id="@+id/btn_change_live1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="改变Live1值" /> <Button android:id="@+id/btn_change_live2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="改变Live2值" /> </LinearLayout> </LinearLayout>
LiveDataFragment
class LiveDataFragment : Fragment() { val TAG = this.javaClass.simpleName override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(TAG, "onCreate") } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_live_data, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) (requireActivity() as LiveDataActivity).apply { // 步骤1 testLiveData.observe(viewLifecycleOwner, { tv_live_fg.text = it Log.i(TAG, "LiveData在LiveDataFragment中 $it") Log.d(TAG, "LiveData在LiveDataFragment中 map 后 ${tv_live_fg.isVisible}") }) // 如果这里用this的话就有问题 liveMappedData.observe(viewLifecycleOwner, { tv_mapped_live_fg.text = it.toString() Log.d( TAG, "LiveData在LiveDataFragment中 map 后 $it + ${tv_mapped_live_fg.isVisible}" ) }) mediatorLive.observe(viewLifecycleOwner, { tv_media_live_fg.text = it.toString() Log.d( TAG, "LiveData在LiveDataFragment中 mediatorLive 后 $it + ${tv_media_live_fg.isVisible}" ) }) // 4 val swLive = mediatorLive.switchMap { if (it.second.toInt() % 2 == 0) liveData1 else liveData2 } swLive.observe(viewLifecycleOwner, { tv_switch_live_fg.text = it.toString() }) } Log.d(TAG, "onViewCreated") } // 步骤1 override fun onAttach(context: Context) { super.onAttach(context) Log.d(TAG, "onAttach") } override fun onPause() { super.onPause() Log.d(TAG, "onPause") } override fun onStop() { super.onStop() Log.d(TAG, "onStop") } override fun onDestroy() { super.onDestroy() Log.d(TAG, "onDestroy") } }
fragment_live_data.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".fourth.LiveDataFragment"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="This is fragment" android:textColor="#000000" android:textSize="20sp" /> <TextView android:id="@+id/tv_live_fg" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="tv_live_fg" android:textColor="#000000" android:textSize="20sp" /> <TextView android:id="@+id/tv_mapped_live_fg" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="tv_mapped_live_fg1" android:textColor="#000000" android:textSize="20sp" /> <TextView android:id="@+id/tv_media_live_fg" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="tv_media_live_fg" android:textColor="#000000" android:textSize="20sp" /> <TextView android:id="@+id/tv_switch_live_fg" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textColor="#000000" android:textSize="20sp" /> </LinearLayout>