How to handle error states with LiveData?

后端 未结 7 601
暗喜
暗喜 2020-12-07 14:10

The new LiveData can be used as a replacement for RxJava\'s observables in some scenarios. However, unlike Observable, LiveData has no callback for

7条回答
  •  轮回少年
    2020-12-07 14:47

    Just some implementation of the method from Chris Cook's answer:

    At first, we need the object that will contain response data and exceptions:

    /**
     * A generic class that holds a value with its loading status.
     *
     * @see Sample apps for Android Architecture Components
     */
    data class Resource(val status: Status, val data: T?, val exception: Throwable?) {
        enum class Status {
            LOADING,
            SUCCESS,
            ERROR,
        }
    
        companion object {
            fun  success(data: T?): Resource {
                return Resource(Status.SUCCESS, data, null)
            }
    
            fun  error(exception: Throwable): Resource {
                return Resource(Status.ERROR, null, exception)
            }
    
            fun  loading(): Resource {
                return Resource(Status.LOADING, null, null)
            }
        }
    }
    
    

    And then my own invention - AsyncExecutor.

    This small class do 3 important things:

    1. Return standard convenient LiveData object.
    2. Call provided callback asynchronously.
    3. Takes the result of the callback or catch any exception and put it to the LiveData.
    
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    
    class AsyncExecutor {
        companion object {
            fun  run(callback: () -> T): LiveData> {
                val resourceData: MutableLiveData> = MutableLiveData()
    
                Thread(Runnable {
                    try {
                        resourceData.postValue(Resource.loading())
                        val callResult: T = callback()
                        resourceData.postValue(Resource.success(callResult))
                    } catch (e: Throwable) {
                        resourceData.postValue(Resource.error(e))
                    }
                }).start()
    
                return resourceData
            }
        }
    }
    
    

    Then you can create a LiveData in your ViewModel, contains the result of your callback or exception:

    
    class GalleryViewModel : ViewModel() {
        val myData: LiveData>
    
        init {
            myData = AsyncExecutor.run {
                // here you can do your synchronous operation and just throw any exceptions
                return MyData()
            }
        }
    }
    
    

    And then you can get your data and any exceptions in the UI:

    
    class GalleryFragment : Fragment() {
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            galleryViewModel = ViewModelProviders.of(this).get(GalleryViewModel::class.java)
           
           // ...
    
            // Subscribe to the data:
            galleryViewModel.myData.observe(viewLifecycleOwner, Observer {
                when {
                    it.status === Resource.Status.LOADING -> {
                        println("Data is loading...")
                    }
                    it.status === Resource.Status.ERROR -> {
                        it.exception!!.printStackTrace()
                    }
                    it.status === Resource.Status.SUCCESS -> {
                        println("Data has been received: " + it.data!!.someField)
                    }
                }
            })
    
            return root
        }
    }
    
    

提交回复
热议问题