Android中使用Kotlin协程代替RxJava封装网络请求

现在的Android项目普遍使用Retrofit+RxJava的组合实现网络接口请求与数据的展现。这一功能通过Kotlin语言的协程功能也可以很方便的实现。

相比较而言,RxJava功能过于强大,如果仅用于封装网络请求,有些杀鸡用牛刀的感觉。使用Kotlin的协程实现这个需求代码更精简,逻辑也更清晰一些。

以下是一个完整的例子。使用Retrofit结合Kotlin协程,实现网络请求。
点击Activity中的按钮,请求V2ex网站的openAPI,成功后在界面中显示结果字段。

Activity中的代码如下:

class MainActivity : AppCompatActivity() {

    var loadDataJob: Job? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        test_button.setOnClickListener {
            showResult("")
            loadDataJob?.cancel()  // 取消之前的加载任务
            loadDataJob = loadData("Livid")  // “http://www.v2ex.com/api/members/show.json?username=Livid”
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        loadDataJob?.cancel()  // 取消加载任务
    }

    private fun showResult(resStr: String) {
        test_result.text = resStr
    }

    private fun loadData(username: String): Job {
        return executeRequest<UserInfo>(
            //  请求调用
            request = {
                userApiManager.getUserInfo(username)
            },
            //  成功回调
            onSuccess = {
                showResult(it.bio)
            },
            //  失败回调
            onFail = {
                it.printStackTrace()
            })
    }
}

loadData函数中,通过execeteRequest方法返回Job对象,executeRequest通过lambda参数分别指定了网络请求,成功与失败时的逻辑操作。

UserApiManager类中实现了Retrofit调用的简单封装:

@Nullable
public UserInfo getUserInfo(String name) throws Exception {
    // 类初始化时创建Retrofit以及接口api的实例
    /*
    okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(5000, TimeUnit.MILLISECONDS)
                .readTimeout(5000, TimeUnit.MILLISECONDS)
                .retryOnConnectionFailure(true).build();
    retrofitBuilder =
        new Retrofit.Builder().baseUrl("http://www.v2ex.com")
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient);

    retrofit = retrofitBuilder.build();
    mApiService = retrofit.create(UserApiService.class);
    */
        
    Response<UserInfo> result = mApiService.getUserInfo(name).execute();  // 同步请求
    if (result.isSuccessful()) {
        return result.body();
    } else if (result.code() == 404) {
        throw new Exception("404 Not Found");
    }
    return null;
}

由于我们要通过协程实现异步加载,因此在网络请求中,使用了execute同步方法。

在点击按钮时,先取消前一次加载请求,再发起新请求;同时在Activity的onDestroy生命周期中,也进行了取消加载请求的处理。

executeRequest利用Kotlin协程实现了网络请求封装:

fun <T> executeRequest(request: suspend () -> T?, onSuccess: (T) -> Unit = {}, onFail: (Throwable) -> Unit = {}): Job {
    val uiScope = CoroutineScope(Dispatchers.Main)  // UI主线程的CoroutineScope
    return uiScope.launch {
        try {
            val res: T? = withContext(Dispatchers.IO) { request() }  // IO线程中执行网络请求,成功后返回这里继续执行
            res?.let {
                onSuccess(it)
            }
        } catch (e: CancellationException) {
            Log.e("executeRequest", "job cancelled")
        } catch (e: Exception) {
            Log.e("executeRequest", "request caused exception")
            onFail(e)
        }
    }
}

首先在UI线程中启动协程,当执行到withContext后,request代码块将切换到调度器分配的IO线程上执行,同时executeRequest函数让出控制权,因此UI线程不会阻塞。当request请求完成后,结果赋值给 res 变量,UI线程再次回到executeRequest中,继续执行后续部分。

多次点击界面按钮,将打印

job cancelled

更改一个错误的url地址,将看到

request caused exception

通过协程,在顺序执行的函数中,实现了回调函数的效果,代码逻辑更清晰了。在调用时相对RxJava也更加简洁。

参考文章
Android Coroutine Recipes

你可能感兴趣的:(Android,kotlin)