day24 stateflow和shareflow是如何取代livedata的,聊聊use case吧!!

记得我们说的特性吧,stateflow会在旧值和新值相同的情况下不做更新,但有时我们需要在每次retry某些动作,比如重新连线、重新载入等等

这时,我们就需要用shareFlow了,我这边直接跟别人借例子

class Biller (
    private val context: Context,
) : PurchasesUpdatedListener, BillingClientStateListener {
    
    private var billingClient: BillingClient =
        BillingClient.newBuilder(context)
            .setListener(this)
            .enablePendingPurchases()
            .build()
        
    private val billingClientStatus = MutableSharedFlow<Int>(
        replay = 1,
        onBufferOverflow = BufferOverflow.DROP_OLDEST
    )
    
    override fun onBillingSetupFinished(result: BillingResult) {
        billingClientStatus.tryEmit(result.responseCode)
    }

    override fun onBillingServiceDisconnected() {
        billingClientStatus.tryEmit(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED)
    }
    
    // ...
    
    // Suspend until billingClientStatus == BillingClient.BillingResponseCode.OK
    private suspend fun requireBillingClientSetup(): Boolean =
        withTimeoutOrNull(TIMEOUT_MILLIS) {
            billingClientStatus.first { it == BillingClient.BillingResponseCode.OK }
            true
        } ?: false
   
    init {
        billingClientStatus.tryEmit(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED)
        billingClientStatus.observe(ProcessLifecycleOwner.get()) {
            when (it) {
                BillingClient.BillingResponseCode.OK -> with (billingClient) {
                    updateSkuPrices()
                    handlePurchases()
                }
                else -> {
                    delay(RETRY_MILLIS)
                    billingClient.startConnection(this@Biller)
                }
            }
        }
    }

    private companion object {
        private const val TIMEOUT_MILLIS = 2000L
        private const val RETRY_MILLIS = 3000L
    }
}

这边是以 Google's Billing Client library作为范例,那直接切重点出来讲,在when 判断时,如果result不是ok,那就会先延迟几秒再连线,很重要,这边是需要扗失败的情况下做某些事情,而这个某些事情会触发下一个result

我直接借例子,因为我想不到好例子,socket会自动重连,ui不用更新,webview不用reload,如果你们有好的例子或情境也可以告诉我

jast有写一篇关於singleLiveEvent转换到stateflow的文章,开门

stateflow

来个取代liveData吧,这篇的范例和前一篇一样,没甚麽好讲的

唯一要注意的就是stateflow不会更新和旧值一样的新值,其余细节,就看前一篇吧

//viewModel
private val _postStateFlow = MutableStateFlow(Post(0,0,"",""))
val postStateFlow = _postStateFlow

init {
    viewModelScope.launch {
        repo.postFlow.collect {
            _postStateFlow.value = it
        }
    }
}
//fragment
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED){
        viewModel.postStateFlow.collect {
            Timber.d(it.toString())
        }
    }
}

stateflow with databinding

让stateflow能够大放厥词说livedata已经被取代的功能,无非就是databinding了,尽管我自己也什麽再用databinding的功能,但或许是某些开发者的首选,这边就当作棒各位开个门,写个简单的范例

首先layout

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="viewModel"
            type="com.kenny.androidplayground.databinding.DBindingViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".databinding.DataBindingFragment">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{viewModel.name}"
            android:textSize="36sp"
            android:background="#333"
            app:layout_constraintTop_toTopOf="parent"
            android:id="@+id/name_field"
            />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            android:id="@+id/typing_field"
            android:text="@={viewModel.newTypingName}"
            />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
class DataBindingFragment : Fragment() {

    private lateinit var binding:FragmentDataBindingBinding
    private val viewModel by viewModels<DBindingViewModel>()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_data_binding, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        binding = FragmentDataBindingBinding.bind(view)
        binding.viewModel = viewModel
        binding.lifecycleOwner = viewLifecycleOwner
    }
}

到这边都还是一般databinding的写法(根据我几个月前的记忆),解说一下,在xml里面,我把editText和Textview的text设置为viewModel里面的变数

且在fragment这边指定了viewModel的instance给xml,那麽最後完成viewModel吧

class DBindingViewModel: ViewModel() {

    private val _name = MutableStateFlow<String?>(null)
    val name: StateFlow<String?> = _name
    val newTypingName = MutableStateFlow<String?>(null)

    init {
        viewModelScope.launch {
            newTypingName
                .map {
                    "edtext enter: $it"
                }
                .collect {
                    _name.value = it
                }
        }
    }
}

连结

必看

should-we-choose-kotlins-stateflow-or-sharedflow-to-substitute-for-android-s-livedata

jast

LiveData:还没普及就让我去世?我去你的 Kotlin 协程


<<:  DAY27 - 网站正式上线前的准备

>>:  Day 25 PTT八卦版爬取

Day23 设定Alerts

今日我们要来使用Kibana内的警报功能,看如何设定Alert让我们能收到异常的通知。 设定Aler...

[Day11] Flutter - StatelessWidget & StatfulWidget 差别

前言 Hi, 我是鱼板伯爵今天要讲 StatelessWidget & StatfulWid...

暴力攻击(Brute Force Attack)

-图片来源:Toussaint Ilboudo 我碰到了有关卢克小组中的暴力攻击事件的帖子,并恭敬...

Day29|常见的三种工作流程 - Git flow、GitHub Flow 与 Gitlab Flow

在制作专案时,大多都是与他人共同协作,当一起开发的人越来越多时,就更需要有一套规则或模式来进行合作,...

Day20 NiFi - 与 GCP Cloud Storage 对接设定

今天开始会带大家来操作一下 NiFi 如何来与 GCP 相关的服务做整合与设定,首先会先介绍 Goo...