多次API请求之顺序请求
例:先上传用户头像图片,后更新用户信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| suspend fun uploadFile(fileUri: Uri) = withContext(coroutineContext + Dispatchers.IO) { val pfd = contentResolver.openFileDescriptor(fileUri, "r") ?: return@withContext MissingDataError var tmpFile: File? = null val mediaType = contentResolver.getType(fileUri)?.toMediaType() val requestBody: RequestBody = if (pfd.statSize in 0..(5 * 1024 * 1024)) { val byteArray = FileInputStream(pfd.fileDescriptor).use { it.readBytes() } byteArray.toRequestBody(mediaType) } else { tmpFile = File(cacheDir, System.currentTimeMillis().toString()) FileInputStream(pfd.fileDescriptor).use { fis -> tmpFile.outputStream().use { fos -> fis.copyTo(fos) } } tmpFile.asRequestBody(mediaType) } pfd.close()
val service = ApiClient.getRetrofit().create<UploadService>() dataFromNet { service.uploadImg(template, MultipartBody.Part.createFormData("file", "filename", requestBody)) }.also { tmpFile?.delete() } }
|
上面是上传图片的代码。suspend函数只能在另一个suspend函数或协程内调用。每一个suspend函数都有一个coroutineContext,coroutineContext是会向下传递的。在协程内被调用,这个coroutineContext就是协程的coroutineContext。在suspend函数内被调用,这个coroutineContext就是suspend函数的coroutineContext。
withContext(coroutineContext + Dispatchers.IO)
我们看看**uploadFile()**用的两个context有什么作用?
第一个coroutineContext是suspend函数的,就是上一级传递下来的,这个函数的调用起点会是ViewModel,coroutineContext可能是viewModelScope.coroutineContext,也可能是viewModelScope的子协程context,所以当ViewModel执行clear()后,coroutineContext会被取消,上传图片的请求也会被取消。
第二个Dispatchers.IO是指定函数体在IO线程执行,因为涉及到文件读写,所以最好在IO线程内执行
suspend函数是可以被挂起的,不会阻塞主线程(除非你使用主线程执行函数)
最后,使用示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| fun updateUserInfo(imageUri: Uri): LiveData<Resource<Any>> { return liveData(context) { emit(InProgress) val result = uploadFile(imageUri) if (result is Resource.Success) { if (result.data != null) { emit(dataFromNet { service.updateUserInfo(result.data.img_id.toRequestBody()) }) } else { emit(MissingDataError) } } else { emit(result) } } }
|
多次请求之并发请求
没有实际用例,下面是一些模拟的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| private suspend fun multiCall() = withContext(coroutineContext) { val deferred0 = async { dataFromNet { service.getData0() } } val deferred1 = async { dataFromNet { service.getData1() } } val result0 = deferred0.await() val result1 = deferred1.await() if (result0 is Resource.Success && result1 is Resource.Success) { Resource.Success(result0.data) } else if (result0 is Resource.Error) { result0 } else { result1 } }
|
若要发起并发请求,则需要async来实现。看看async.await()的注释
Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete
dataFromNet(基于retrofit)是一个异步的请求,async.await()不会阻塞主线程。
如果需要,可以通过withContext(coroutineContext + Dispatchers.IO)在函数体内或async{}内做一些耗时操作。
最后,使用示例如下:
1 2 3 4 5 6
| fun multiCallTest(): LiveData<Resource<List<String>>> { return liveData(context) { emit(InProgress) emit(multiCall()) } }
|