Lifecycle, LiveData, Jetpck (part 1

Jetpack

Jetpack is a suite of libraries that help developers follow best practices, reduce boilerplate code and write code that runs consistently across Android versions and devices, allowing developers to focus on the code that matters.

Official Recommendation Structure

Note that each component depends only on the component at the level below it. For example, activities and fragments rely only on the viewmodel. The storage area is the only class that depends on multiple other classes; In this case, the storage relies on a persistent data model and a remote back-end data source.

Lifecycle

Sensing the lifecycle of an Activity isn’t complicated, but the problem is that it’s very simple to do so in an Activity. What if you want to do so in a non-activity class?

The Lifecycles component is designed to solve this problem by making it easy for any class to sense the lifecycle of an Activity without having to write a lot of logic in the Activity.

UI lifecycle

Basic use of Lifecycles

Create a new MyObserver class and have it implement the LifecycleObserver interface, then use method annotations to be aware of the Activity lifecycle:

class MyObserver : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun activityStart(a) {
        Log.d("MyObserver"."activityStart")}@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun activityStop(a) {
        Log.d("MyObserver"."activityStop")}}Copy the code

Another approach: Implement the LifecycleEventObserver interface.

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 -> {
                    }
                }
            }
        })
Copy the code

Finally, call the addObserver() method in the Activity to observe the LifecycleOwner lifecycle:

class MainActivity : AppCompatActivity() {...override fun onCreate(savedInstanceState: Bundle?). {
        superOnCreate (savedInstanceState) the setContentView (R.l ayout. Activity_main)... Lifecycle. AddObserver (MyObserver ())}... }Copy the code

Lifecycle state diagram

The sample

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")
/ /}
}
Copy the code

LifecycleObject.kt

class LifecycleObject {
    val TAG = this.javaClass.simpleName
    private lateinit var player: MediaPlayer

    fun onCreate(a) {
        player = MediaPlayer()
        Log.d(TAG, "onCreate")}fun onResume(a) {
        thread {
            SystemClock.sleep(3000)
            player.start()
            Log.d(TAG, "onResume")}}fun onPause(a) {
        player.stop()
        Log.d(TAG, "onPause")}fun onDestroy(a) {
        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(a) {
        player = MediaPlayer()
        Log.d(TAG, "onCreate")}@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResume(a) {
        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(a) {
        player.stop()
        Log.d(TAG, "onPause")}@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy(a) {
        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")}}}Copy the code

LiveData

Adding a dependency library

implementation 'androidx. Lifecycle: lifecycle - livedata - KTX: 2.3.0 - beta01'
Copy the code

Introduction to the

LiveData is an observable data-holding class. Have a life cycle (the Activity/fragments/Service) of perception (make sure that the active state to accept data update).

LiveData is a responsive programming component provided by Jetpack that can contain any type of data and notify observers when the data changes.

LiveData is particularly well suited to use in conjunction with the ViewModel, and although it can be used elsewhere on its own, most of the time it is used within the ViewModel.

  1. The original background

    Android development architecture design in developing MVX, the perception of the life cycle for the Controller/Presenter/ViewModel is not natural to be seen. The problem of data resources and leakage.

  2. 2018 Google AAC (Android Architecture Components). A combined set of Jetpack component libraries that make the ViewModel lifecycle aware. You also have the data awareness (LiveData)

  • Understand LiveData

    Unlike the observation mode in RxJava, only observers in the active state are notified.

    Once the observer replies to Resume status, it will receive the latest data (pros and cons, special scenarios).

Using LiveData

LiveData is abstract

MutableLiveData

// Declare a liveData
val liveA = mutableLiveData<String>()
// Assign as needed
liveA.value = "some value of liveA"

// In the UI, observe, in the active state can sense changes
valliveAObserver = Observer<String>{ value? .let{//do something
    }
}
liveA.observe(vivewLifeCycleOwner,liveAObserver)
Copy the code

Transformations.map

// Data comes from a variety of sources. Assigning values to the UI requires conversion
liveA.map{
    // Convert rules
}
Copy the code

==liveData. The inActive observer will not be notified to refresh the data, but will get the latest data== when the observer resumes the active status of Resume

MediatorLiveData

The intermediary, the medium, merges the data of multiple liveData into one liveData

LiveData of Mediator can listen to the changes of two data sources, A and B, and respond to the changes of A/B after addSource, transforming it into changes of Mediator.

  • If inactive, both A and B change, then after resume, only the latest change is accepted

SwitchMap

For data source transformation, switching and control of multiple data sources

Use it with Mediator’s liveData, and select the data source according to the conditions

The sample

LiveDataActivity

class LiveDataActivity : AppCompatActivity() {
    val TAG = this.javaClass.simpleName

    / / step 1
    val testLiveData = MutableLiveData<String>()

    / / step 2
    val liveMappedData = testLiveData.map {
        it.hashCode()
    }


    / / step 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 shows${liveDataFragment.isVisible}")
        }
        btn_destroy_fg_live.setOnClickListener {
            supportFragmentManager.beginTransaction()
                .detach(liveDataFragment)
                .commit()
            Log.d(TAG, "OnCreate hidden${liveDataFragment.isVisible}")
        }
        btn_change_live.setOnClickListener {
            testLiveData.value = "The current liveData value is:The ${(10000.99999.).random()}"
        }
        / / step 1
        testLiveData.observe(this, {
            tv_live_data_activity.text = it
            Log.d(TAG, "In LiveDataActivity LiveData$it")
        })
        liveMappedData.observe(this, {
            tv_mapped_data_activity.text = it.toString()
            Log.d(TAG, "LiveData after map in LiveDataActivity$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()
        }

    }
}
Copy the code

activity_live_data.xml


      
<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="Current liveData value :"
        android:textColor="# 000000"
        android:textSize="20dp"
        tools:text="Current liveData value :" />

    <TextView
        android:id="@+id/tv_mapped_data_activity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="LiveData value after Map :"
        android:textColor="# 000000"
        android:textSize="20dp"
        tools:text="LiveData value after Map :" />


    <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="Display fragments" />

        <Button
            android:id="@+id/btn_destroy_fg_live"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Hidden fragments" />

    </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="Change the Live value" />

        <Button
            android:id="@+id/btn_change_live1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Change the Live1 value" />

        <Button
            android:id="@+id/btn_change_live2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Change the Live2 value" />


    </LinearLayout>
</LinearLayout>
Copy the code

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 {
            / / step 1
            testLiveData.observe(viewLifecycleOwner, {
                tv_live_fg.text = it
                Log.i(TAG, "In LiveDataFragment LiveData$it")
                Log.d(TAG, "LiveData is mapped in LiveDataFragment${tv_live_fg.isVisible}")})// If this is used, there is a problem
            liveMappedData.observe(viewLifecycleOwner, {
                tv_mapped_live_fg.text = it.toString()
                Log.d(
                    TAG,
                    "LiveData is mapped in LiveDataFragment$it  +   ${tv_mapped_live_fg.isVisible}"
                )
            })
            mediatorLive.observe(viewLifecycleOwner, {
                tv_media_live_fg.text = it.toString()
                Log.d(
                    TAG,
                    "LiveData after mediatorLive in LiveDataFragment$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")}/ / step 1
    override fun onAttach(context: Context) {
        super.onAttach(context)
        Log.d(TAG, "onAttach")}override fun onPause(a) {
        super.onPause()
        Log.d(TAG, "onPause")}override fun onStop(a) {
        super.onStop()
        Log.d(TAG, "onStop")}override fun onDestroy(a) {
        super.onDestroy()
        Log.d(TAG, "onDestroy")}}Copy the code

fragment_live_data.xml


      
<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>
Copy the code