背景

在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...