四篇文章带你快速入门Jetpck(上)之Lifecycle,Li
四篇文章带你快速入门Jetpck(上)之Lifecycle,LiveData
Jetpack
Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者精力集中编写重要的代码。
image.png官方推荐架构
img请注意,每个组件仅依赖于其下一级的组件。例如,Activity 和 Fragment 仅依赖于视图模型。存储区是唯一依赖于其他多个类的类;在本例中,存储区依赖于持久性数据模型和远程后端数据源。
Lifecycle
感知Activity的生命周期并不复杂,但问题在于,在一个Activity中去感知它的生命周期非常简单,而如果要在一个非Activity的类中去感知Activity的生命周期,应该怎么办呢?
Lifecycles组件就是为了解决这个问题而出现的,它可以让任何一个类都能轻松感知到Activity的生命周期,同时又不需要在Activity中编写大量的逻辑处理。
UI的生命周期
activity&fragment生命周期图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状态图
image.png示例
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当中的。
-
背景 原文
imgAndroid开发中 MVX的开发架构设计,生命周期的感知对于Controller/Presenter/ViewModel不是天然可知。数据资源与泄露的问题。
MVX的生命周期传递 -
2018年 google的AAC(Android Architecture Components)。一套组合的Jetpack组件库,使得ViewModel具有生命周期感知能力。同时也有了数据的感知能力(LiveData)
img
-
理解LiveData
img不同于
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{
//转换规则
}
img
img==liveData的数据,不会通知inActive的观察者刷新数据,但是当observer恢复Resume的active后,也会得到最新的data==
MediatorLiveData
中介者,媒介,将多个liveData的数据,合并处理成一个LiveData
imgmediator的liveData可以监听A,B两个数据源的变化,通过addSource后,并响应A/B的变化,转化为mediator的变化。
- 如果inactive下,A,B都变化,则resume后,也只接受最新的变化
SwitchMap
用于数据源的转化,多数据源的切换和控制
img配合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>