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

36次阅读

共计 3392 个字符,预计需要花费 9 分钟才能阅读完成。

背景

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

正文完
 0