如何在Android Worker中完成Kotlin流
我正在调查Kotlin Flow在我当前Android应用程序中的使用情况 我的应用程序通过改造API调用从远程服务器检索数据 其中一些API在500个项目页面中返回50000个数据项目 每个API响应都包含一个HTTP链接头,其中包含下一页完整的URL 这些呼叫可能需要2秒钟才能完成 为了减少运行时间,我使用了Kotlin流来并发处理每个页面 在进行下一页API调用的同时调用数据 我的流程定义如下:如何在Android Worker中完成Kotlin流,android,kotlin-flow,Android,Kotlin Flow,我正在调查Kotlin Flow在我当前Android应用程序中的使用情况 我的应用程序通过改造API调用从远程服务器检索数据 其中一些API在500个项目页面中返回50000个数据项目 每个API响应都包含一个HTTP链接头,其中包含下一页完整的URL 这些呼叫可能需要2秒钟才能完成 为了减少运行时间,我使用了Kotlin流来并发处理每个页面 在进行下一页API调用的同时调用数据 我的流程定义如下: private val persistenceThreadPool = Executors.n
private val persistenceThreadPool = Executors.newFixedThreadPool(3).asCoroutineDispatcher()
private val internalWorkWorkState = MutableStateFlow<Response<List<MyPage>>?>(null)
private val workWorkState = internalWorkWorkState.asStateFlow()
private val myJob: Job
init {
myJob = GlobalScope.launch(persistenceThreadPool) {
workWorkState.collect { page ->
if (page == null) {
} else managePage(page!!)
}
}
}
private val persistenceThreadPool=Executors.newFixedThreadPool(3.ascoroutineddispatcher())
private val internalWorkWorkState=MutableStateFlow(null)
private val workstate=internalworkstate.asStateFlow()
我的工作:工作
初始化{
myJob=GlobalScope.launch(persistenceThreadPool){
workstate.collect{page->
如果(第==null页){
}else managePage(第页!!)
}
}
}
我的递归函数定义如下,用于获取所有页面:-
private suspend fun managePages(accessToken: String, response: Response<List<MyPage>>) {
when {
result != null -> return
response.isSuccessful -> internalWorkWorkState.emit(response)
else -> {
manageError(response.errorBody())
result = Result.failure()
return
}
}
response.headers().filter { it.first == HTTP_HEADER_LINK && it.second.contains(REL_NEXT) }.forEach {
val parts = it.second.split(OPEN_ANGLE, CLOSE_ANGLE)
if (parts.size >= 2) {
managePages(accessToken, service.myApiCall(accessToken, parts[1]))
}
}
}
private suspend fun managePage(response: Response<List<MyPage>>) {
val pages = response.body()
pages?.let {
persistResponse(it)
}
}
private suspend fun persistResponse(myPage: List<MyPage>) {
val myPageDOs = ArrayList<MyPageDO>()
myPage.forEach { page ->
myPageDOs.add(page.mapDO())
}
database.myPageDAO().insertAsync(myPageDOs)
}
private页面(accessToken:String,response:response){
什么时候{
结果!=null->return
response.issusccessful->internalworkstate.emit(响应)
其他->{
manageError(response.errorBody())
result=result.failure()
返回
}
}
response.headers().filter{it.first==HTTP\u HEADER\u LINK&&it.second.contains(REL\u NEXT)}.forEach{
val parts=it.second.split(打开角度、关闭角度)
如果(零件尺寸>=2){
managePages(accessToken,service.myApiCall(accessToken,parts[1]))
}
}
}
专用暂停页面(响应:响应){
val pages=response.body()
页面?让我们{
持续响应(it)
}
}
私有暂停响应(myPage:列表){
val myPageDOs=ArrayList()
myPage.forEach{page->
myPageDOs.add(page.mapDO())
}
database.myPageDAO().insertAsync(myPageDOs)
}
我的许多问题是
private val persistenceThreadPool = Executors.newFixedThreadPool(3).asCoroutineDispatcher()
private val completed = CompletableDeferred<Int>()
private val channel = Channel<Response<List<MyPage>>?>(UNLIMITED)
private val channelFlow = channel.consumeAsFlow().flowOn(persistenceThreadPool)
private val frank: Job
init {
frank = GlobalScope.launch(persistenceThreadPool) {
channelFlow.collect { page ->
if (page == null) {
completed.complete(totalItems)
} else managePage(page!!)
}
}
}
...
...
...
channel.send(null)
completed.await()
return result ?: Result.success(outputData)
private val persistenceThreadPool=Executors.newFixedThreadPool(3.ascoroutineddispatcher())
private val completed=CompletableDeferred()
专用val通道=通道(无限制)
private val channelFlow=channel.consumeAsFlow().floon(persistenceThreadPool)
二等兵瓦尔·弗兰克:工作
初始化{
frank=GlobalScope.launch(persistenceThreadPool){
channelFlow.collect{page->
如果(第==null页){
已完成。已完成(总计项)
}else managePage(第页!!)
}
}
}
...
...
...
channel.send(空)
已完成。等待()
返回结果?:result.success(outputData)
我不喜欢依赖于
CompletableDeferred
,有没有比这更好的方法来了解流何时完成了所有事情?有几种方法可以实现所需的行为。我建议使用专门为并行分解设计的。它还提供了良好的即时取消和错误处理行为。与之相结合使实现变得非常简单。从概念上讲,实现可能如下所示:
suspend fun fetchAllPages() {
coroutineScope {
val channel = Channel<MyPage>(Channel.UNLIMITED)
launch(Dispatchers.IO){ loadData(channel) }
launch(Dispatchers.IO){ processData(channel) }
}
}
suspend fun loadData(sendChannel: SendChannel<MyPage>){
while(hasMoreData()){
sendChannel.send(loadPage())
}
sendChannel.close()
}
suspend fun processData(channel: ReceiveChannel<MyPage>){
for(page in channel){
// process page
}
}
suspend fun fetchAllPages(){
共线镜{
val通道=通道(通道无限)
启动(Dispatchers.IO){loadData(channel)}
启动(Dispatchers.IO){processData(channel)}
}
}
暂停趣味加载数据(sendChannel:sendChannel){
while(hasMoreData()){
sendChannel.send(loadPage())
}
sendChannel.close()
}
暂停处理数据(通道:接收通道){
用于(频道中的页面){
//进程页
}
}
其工作方式如下:
coroutineScope
暂停,直到所有子项都完成。因此,您不再需要CompletableDeferred
loadData()
循环加载页面并将其发布到频道中。加载所有页面后,它会立即关闭频道processData
逐个从频道获取项目并对其进行处理。一旦处理完所有项目(且通道已关闭),该循环将立即结束suspend fun getData():Flow=Flow{
var页面数据:列表
变量pageUrl:字符串?=“bla”
while(pageUrl!=null){
TODO(“从pageUrl获取pageData并将pageUrl更改为下一页”)
emitAll(页面数据)
}
}
.floon(Dispatchers.IO/*不需要线程池执行器,IO会自动执行它*/)
.缓冲区(3)
您可以像普通流、迭代等一样使用它。如果您想知道输出的总长度,应该使用可变闭包变量在使用者上计算它。注意,您不需要在任何地方使用GlobalScope(理想情况下永远都不需要)