Simplifying Android Network Requests with Kotlin Coroutines
By converting Android network calls from callback‑based listeners to Kotlin coroutines, the code becomes sequential, eliminates duplicated UI‑state checks, centralizes loading handling, supports parallel requests with async/await, and integrates with lifecycleScope for automatic cancellation, greatly improving readability and safety.
In the current Android app, network requests are performed using a callback pattern that invokes the main thread after completion. The typical request code includes loading state handling, URL and parameter definition, and a callback that checks canUpdateUi() before updating the UI.
<span style="color: rgb(86, 156, 214)">private</span> fun requestData() {<br/> onPageLoadingCompleted(LoadCompleteType.LOADING) <span style="color: rgb(87, 166, 74); font-style: italic">// 请求前显示loading状态</span><br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">url</span> = MainUrlConstants.getInstanse().hotCommentUniversalUrl <span style="color: rgb(87, 166, 74); font-style: italic">// 定义请求地址</span><br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">params</span> = mapOf(<span style="color: rgb(214, 157, 133)">"param1"</span> to <span style="color: rgb(214, 157, 133)">"hello"</span>, <span style="color: rgb(214, 157, 133)">"param2"</span> to <span style="color: rgb(214, 157, 133)">"world"</span>) <span style="color: rgb(87, 166, 74); font-style: italic">// 定义请求参数</span><br/> CommonRequestM.getData(url, params, CommentModel::class.java, object : IDataCallBack<CommentModel> {<br/> override fun onSuccess(data: CommentModel?) { <span style="color: rgb(87, 166, 74); font-style: italic">// 成功回调</span><br/> <span style="color: rgb(86, 156, 214)">if</span> (!canUpdateUi()) { <span style="color: rgb(87, 166, 74); font-style: italic">// 这一步判断页面是否已销毁,如果已销毁就不应该继续执行UI操作</span><br/> <span style="color: rgb(86, 156, 214)">return</span><br/> }<br/> onPageLoadingCompleted(LoadCompleteType.OK) <span style="color: rgb(87, 166, 74); font-style: italic">// 请求完成,停止loading状态</span><br/> refreshUI(data) <span style="color: rgb(87, 166, 74); font-style: italic">// ..接下来执行刷新UI操作</span><br/> }<br/><br/> override fun onError(code: Int, message: String?) { <span style="color: rgb(87, 166, 74); font-style: italic">// 失败回调</span><br/> <span style="color: rgb(86, 156, 214)">if</span> (!canUpdateUi()) { <span style="color: rgb(87, 166, 74); font-style: italic">// 同成功回调,做同样的页面状态判断</span><br/> <span style="color: rgb(86, 156, 214)">return</span><br/> }<br/> onPageLoadingCompleted(LoadCompleteType.OK) <span style="color: rgb(87, 166, 74); font-style: italic">// 同成功回调,也做停止loading操作</span><br/> CustomToast.showFailToast(message)<br/> }<br/> })<br/>}The duplicated logic—checking canUpdateUi() and stopping the loading indicator—appears in both success and error callbacks, making the code verbose and error‑prone.
By converting the request to a coroutine, the flow becomes sequential, eliminating the need for repeated UI‑state checks and allowing a single place to stop the loading UI.
<span style="color: rgb(86, 156, 214)">private</span> fun requestData() {<br/> onPageLoadingCompleted(LoadCompleteType.LOADING)<br/> mMainScope.launch { <span style="color: rgb(87, 166, 74); font-style: italic">// 创建一个协程</span><br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">url</span> = MainUrlConstants.getInstanse().hotCommentUniversalUrl <span style="color: rgb(87, 166, 74); font-style: italic">// 定义请求地址</span><br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">params</span> = mapOf(<span style="color: rgb(214, 157, 133)">"param1"</span> to <span style="color: rgb(214, 157, 133)">"hello"</span>, <span style="color: rgb(214, 157, 133)">"param2"</span> to <span style="color: rgb(214, 157, 133)">"world"</span>) <span style="color: rgb(87, 166, 74); font-style: italic">// 定义请求参数</span><br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">reqResult</span> = CoroutineRequest.getData(url, params, CommentModel::class.java) <span style="color: rgb(87, 166, 74); font-style: italic">// 使用挂起函数切到IO线程执行</span><br/> onPageLoadingCompleted(LoadCompleteType.OK) <span style="color: rgb(87, 166, 74); font-style: italic">// 挂起函数返回结果后不需要判断canUpdateUi,取消loading操作只要写一次</span><br/> <span style="color: rgb(86, 156, 214)">if</span> (reqResult.isSuccess()) { <span style="color: rgb(87, 166, 74); font-style: italic">// 成功回调</span><br/> refreshUI(reqResult.model)<br/> } <span style="color: rgb(86, 156, 214)">else</span> { <span style="color: rgb(87, 166, 74); font-style: italic">// 失败回调</span><br/> CustomToast.showFailToast(reqResult.msg)<br/> }<br/> }<br/>}Key advantages of the coroutine version:
The loading UI is stopped only once, after the suspend function returns.
The canUpdateUi() check is unnecessary because the coroutine is cancelled automatically when the Activity/Fragment is destroyed.
The code reads like a straightforward sequential algorithm, improving readability.
When only the result model is needed, the code can be further simplified:
mMainScope.launch {<br/> val url = <span style="color: rgb(248, 248, 242)">MainUrlConstants</span>.getInstanse().hotCommentUniversalUrl<br/> val params = mapOf(<span style="color: rgb(214, 157, 133)">"param1"</span> to <span style="color: rgb(214, 157, 133)">"hello"</span>, <span style="color: rgb(214, 157, 133)">"param2"</span> to <span style="color: rgb(214, 157, 133)">"world"</span>)<br/> val resultModel = <span style="color: rgb(248, 248, 242)">CoroutineRequest</span>.getData(url, params, <span style="color: rgb(248, 248, 242)">CommentModel</span>::<span style="color: rgb(86, 156, 214)">class</span>.java).model<br/> refreshUI(resultModel)<br/>}For scenarios requiring multiple concurrent requests, coroutines provide async and await to run requests in parallel and combine their results once all have completed.
<span style="color: rgb(86, 156, 214)">private</span> fun requestMainData() {<br/> mLoadingView.visibility = View.VISIBLE<br/> mMainScope.launch {<br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">preVoteRequest</span> = async(Dispatchers.IO) { requestPreVoteInfoSync() } <span style="color: rgb(87, 166, 74); font-style: italic">// 请求1,创建子协程,立即发起</span><br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">welfareRequest</span> = async(Dispatchers.IO) { requestWelfareInfoSync() } <span style="color: rgb(87, 166, 74); font-style: italic">// 请求2,创建另一个子协程,立即发起</span><br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">preVoteInfo</span> = preVoteRequest.await().model <span style="color: rgb(87, 166, 74); font-style: italic">// 调用await会挂起外层launch,等待请求1完成</span><br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">welfareInfo</span> = welfareRequest.await().model <span style="color: rgb(87, 166, 74); font-style: italic">// 两个请求同时进行,await会在对应请求完成后立即返回</span><br/> mLoadingView.visibility = View.GONE <span style="color: rgb(87, 166, 74); font-style: italic">// 此时两个请求均已返回,可统一刷新 UI</span><br/> initViewPager(welfareInfo != <span style="color: rgb(86, 156, 214)">null</span>)<br/> <span style="color: rgb(86, 156, 214)">if</span> (preVoteInfo != <span style="color: rgb(86, 156, 214)">null</span>) {<br/> <span style="color: rgb(87, 166, 74); font-style: italic">// 渲染 tab 内容</span><br/> } <span style="color: rgb(86, 156, 214)">else</span> {<br/> showNetworkError()<br/> }<br/> }<br/>}<br/><br/><span style="color: rgb(86, 156, 214)">private</span> fun requestPreVoteInfoSync(): SyncRequestResult<PreVoteInfo> {<br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">params</span> = mapOf(<span style="color: rgb(214, 157, 133)">"albumId"</span> to <span style="color: rgb(214, 157, 133)">"$mAlbumId"</span>)<br/> <span style="color: rgb(86, 156, 214)">return</span> CommonRequestM.getDataSync(<br/> <span style="color: rgb(214, 157, 133)">"${UrlConstants.getInstanse().monthlyVotePreInfo()}${System.currentTimeMillis()}"</span>,<br/> params, PreVoteInfo::class.java<br/> )<br/>}<br/><br/><span style="color: rgb(86, 156, 214)">private</span> fun requestWelfareInfoSync(): SyncRequestResult<MonthlyTicketAlbumWelfareVo> {<br/> <span style="color: rgb(78, 201, 176)">val</span> <span style="color: rgb(189, 99, 197)">url</span> = MainUrlConstants.getInstanse().queryListenWelfare(mAlbumId)<br/> val params: MutableMap<String, String> = ArrayMap()<br/> <span style="color: rgb(87, 166, 74); font-style: italic">// ..省略设置请求参数代码</span><br/> <span style="color: rgb(86, 156, 214)">return</span> CommonRequestM.getDataSync(url, params, MonthlyTicketAlbumWelfareVo::class.java)<br/>}To avoid manually creating and cancelling a coroutine scope for each Activity/Fragment, the AndroidX Lifecycle library provides lifecycleScope, which is automatically bound to the component’s lifecycle.
implementation(<span style="color: rgb(214, 157, 133)">"androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"</span>)Using the lifecycle‑aware scope simplifies the code:
lifecycleScope.launch {<br/> <span style="color: rgb(87, 166, 74); font-style: italic">// Execute coroutine work here</span><br/>}Overall, adopting Kotlin coroutines eliminates repetitive callback boilerplate, centralizes UI‑state handling, enables concurrent network calls with clear composition, and integrates seamlessly with Android’s lifecycle management.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ximalaya Technology Team
Official account of Ximalaya's technology team, sharing distilled technical experience and insights to grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
