关于kotlin:Android-app-中这样用flow更方便巧用flow实现polling

背景

在app开发过程中,实现polling逻辑也是很常见的。当然在挪动端利用应用polling解决会影响利用的性能。比方polling解决减少了网络申请的次数,服务端压力减少。polling解决也耗费了更多的网络流量。然而利用polling的场景还是有的。有时是否抉择polling要思考很多综合的因素,比方咱们能够应用长连贯代替polling,然而长连贯在服务端和客户端的开发成本绝对要更高些,如果polling只是实现相似的跟帖等性能,咱们齐全能够应用polling实现,而不是抉择代价更高的长连贯计划。上面会分应用flow和不应用flow两种形式实现polling并比照两种形式的优缺点。

不应用flow

咱们应用线程解决polling申请,首先咱们定义了一个polling thread。

    class PollingThread: Thread() {
        override fun run() {
            var successBlock : (PollingData)->Unit = {
                Log.d("PollingThread","successBlock $it")
            }
            var failBlock:(Exception)->Unit ={
                Log.d("PollingThread","failBlock $it")
            }
            while (isInterrupted) {
                pollingApi.call(successBlock, failBlock)
                Thread.sleep(5000)
            }
        }
    }

在run办法中实现了polling接口的调用,并且接口的调用在while循环中。这里假如polling的工夫距离是5秒钟,所以这里调用线程的sleep办法暂停线程的执行,5秒后再次调用polling接口。polling接口的调用是异步过程,所以这里设置了两个回调,一个用于接管胜利的数据,一个用于接管失败的异样。如果在回调中更新了画面,咱们还要思考如何保障回调在ui线程执行,并且回调中不更新隐没的页面元素。

class PollingThread(val lifecycleOwner: LifecycleOwner): Thread() {
       override fun run() {
           var successBlock : (PollingData)->Unit = {
               Handler(Looper.getMainLooper()).post {
                   if(lifecycleOwner.lifecycle.currentState >= Lifecycle.State.RESUMED) {
                       Log.d("PollingThread", "successBlock $it")
                   }
               }
           }
           var failBlock:(Exception)->Unit ={
               Handler(Looper.getMainLooper()).post {
                   if(lifecycleOwner.lifecycle.currentState >= Lifecycle.State.RESUMED) {
                       Log.d("PollingThread", "failBlock $it")
                   }
               }
           }
           while (isInterrupted) {
               pollingApi.call(successBlock, failBlock)
               Thread.sleep(5000)
           }
       }
   }

这段代码减少了回调的线程切换和ui画面无效判断。应用Handler切换线程到ui线程,lifecycler判断ui画面的有效性。

polling线程曾经定义实现,下一步咱们还要在适当的机会启动polling线程和进行polling线程。

 var pollingThread:PollingThread? = null
 
 override fun onResume() {
     super.onResume()
     pollingThread = PollingThread().run { 
         start()
         this
     }
 }
 
 override fun onPause() {
     super.onPause()
     pollingThread?.interrupt()
     pollingThread = null
 }

这里定义了一个变量pollingThread用于保留启动的polling线程,咱们在onResume办法中启动polling线程,在onPause办法中进行线程。通过这样解决后polling就能够工作了。

应用flow

首先咱们须要定义一个polling flow。

 private val pollingFlow = flow {
     while (true) { 
         emit(serverApi.getPollingData())
        delay(2000)
     }
 }

在flow中应用了while循环实现有限轮训,申请的网络接口被定义成了挂起函数,轮训距离通过协程的delay办法实现。比照不应用flow的形式,polling flow 有本人的一些劣势。①无线轮训管制更加简略,不须要简单逻辑判断,因为flow 中的轮训逻辑中有挂起函数的调用,当收集polling flow的协程被勾销时,挂起函数会抛出勾销异样,这样就达到了轮训逻辑管制的目标了。②因为调用服务器的接口函数是挂起函数,所以这里防止了应用callback 办法。

咱们如何控制线程切换,如何轮训异样呢?

 private val pollingFlow = flow {
        while (true) {
            emit(serverApi.getPollingData())
            delay(5000)
        }
    }.flowOn(Dispatchers.IO).retryWhen { cause, attempt ->
        Log.d("polling flow ", "retryWhen cause $cause attempt $attempt")
        delay(5000)
        true
    }.onEach {
        Log.d("polling flow ", "onEach $it")
    }

 lifecycleScope.launchWhenResumed { pollingFlow.collect() }

咱们能够通过flowOn办法切换线程,保障了轮训执行的线程在io线程。在polling flow收集的时候应用默认的ui线程。这样保障了flowOn办法前的局部执行在io线程,flowOn办法后的局部执行在ui线程,进而达到线程切换的目标。这里应用retryWhen办法解决轮训异样,当有异样产生时,延时polling工夫距离后进行重试。

咱们调用了lifecycleScope.launchWhenResumed办法收集flow,这样保障了polling flow只在画面被唤醒的状态下被收集。launchWhenResumed办法是通过切断音讯散发来达到挂起的目标,如果在launchWhenResumed办法中又启动了协程进行轮训操作,那么阻止音讯散发并不能进行launchWhenResumed办法外部启动协程的轮训操作。在lifecycle-runtime-ktx 2.4.0版本中引入了lifecycle.repeatOnLifecycle办法,这个办法能够依据生命周期进行勾销和重启。因为它实现的是协程勾销和协程重启,所以在这个办法外部启动的协程也会被勾销和重启,进而解决了画面挂起时子协程不被勾销而引起的泄露问题。

总结

flow能够充分利用协程的结构化异步的劣势实现异步轮训,防止应用启动线程形式进行轮训操作。
线程轮训的形式中延时操作阻塞了线程,flow中的延时操作挂起协程但不阻塞线程,所以flow节俭了线程资源,协程挂起时线程还能够解决其余的工作。
创立线程的代价比启动协程的代价更高,并且线程的治理更加麻烦,咱们要时刻关怀线程状态,控制线程的启动与进行。然而协程依仗结构化异步的特点,用户不须要投入过多的经验治理协程的启动和进行。
应用flow能够通过申明的形式定义polling解决流程,代码逻辑简略清晰。比方通过flow的retryWhen申明重试解决,通过catch捕捉polling异样,通过flowOn办法进行线程切换等。
我的公众号曾经开明,公众号会同步公布。
欢送关注我的公众号

————————————————
版权申明:本文为CSDN博主「mjlong123123」的原创文章,遵循CC 4.0 BY-SA版权协定,转载请附上原文出处链接及本申明。
原文链接:https://blog.csdn.net/mjlong1…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理