用到的库
1 2 3 4 5 6 7 8 9 10 11 def rxandroidVersion = '2.0.1' implementation "io.reactivex.rxjava2:rxandroid:$rxandroidVersion " def retrofitVersion = '2.8.0' implementation "com.squareup.retrofit2:retrofit:$retrofitVersion " implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion " implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion " implementation 'com.google.code.gson:gson:2.8.5' implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
服务器统一返回格式 当 error_code
不为 0
时,就不用解析 data
(其值为 null
),根据相应的错误码提示给用户;
1 2 3 4 { "err_code" : 1101 , "err_msg" : "参数name不能为空" }
当 error_code
为 0
时,就可以解析 data
,这个返回值通过实体类或列表来映射得到。
1 2 3 4 5 6 7 8 9 10 11 12 13 { "err_code" : 0 , "err_msg" : "ok" , "data" : { "id" : 1 , "name" : "lyloou" , "email" : "lyloou@qq.com" , "personal_signature" : "多么美好的太阳" , "gmt_create" : "2020-01-16T09:38:18.000+0000" , "gmt_modified" : "2020-03-14T11:27:58.000+0000" , "is_disabled" : false } }
定义返回类 通过 CResult 来接收返回的数据,data
的通过泛型来映射成对应实体类(用泛型的好处,是不用为每一个返回都建立一个类);
1 2 3 data class CResult <T >(var err_code: Int , var err_msg: String, var data : T?)
Retrofit 简单封装
具体用法参考:Retrofit 官网
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 val gson: Gson = GsonBuilder() .setDateFormat("yyyy-MM-dd HH:mm:ss" ) .setLenient() .create() fun Any.toJsonString () : String { return gson.toJson(this ) } object Network { private var headerPairs: (() -> List<Pair<String, String>>)? = null private val builder = Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()); fun withHeader (pairList: (() -> List <Pair <String , String >>)) : Network { this .headerPairs = pairList return this } fun <T> get (baseUrl: String , clazz: Class <T >) : T { val okHttpBuilder = OkHttpClient.Builder() headerPairs?.invoke()?.let { okHttpBuilder.addInterceptor(interceptor(it)) } return builder.baseUrl(baseUrl) .client(okHttpBuilder.build()) .build().create(clazz) } fun auth (userPassword: UserPassword ?) : List<Pair<String, String>> { userPassword?.let { return listOf( "Content-Type" to "application/json" , "Authorization" to it.password, "UserId" to it.userId.toString() ) } return emptyList() } } fun interceptor (headers: List <Pair <String , String>>) : (Interceptor.Chain) -> Response { return { val newBuilder = it.request().newBuilder() headers.forEach { header -> newBuilder.addHeader(header.first, header.second) } it.proceed(newBuilder.build()) } } fun <T> Observable<T> .defaultScheduler () : Observable<T> { return this .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) }
准备用户的类和 retrofit 接口 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 30 31 32 33 data class User ( val id: Long , val name: String, val email: String, @SerializedName("personal_signature" ) val personalSignature: String, @SerializedName("gmt_create" ) val gmtCreate: Date ) data class UserPassword ( val userId: Long , val name: String, val password: String ) enum class Url (val url: String) : Str { UserApi("http://127.0.0.1:8888/api/v1/user/" ), ; } fun Network.userApi () : UserApi { return get (Url.UserApi.url, UserApi::class .java) } fun Network.userWithAuthApi (userPassword: UserPassword ? = UserPasswordHelper.getUserPassword() ): UserApi { return withHeader { auth(userPassword) } .get (Url.UserApi.url, UserApi::class .java); }
使用方法 1 ,基于 Observable 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 30 31 32 33 34 35 36 interface UserApi { @POST("login" ) fun login (@Query("name" ) name: String , @Query("password" ) password: String ) : Observable<CResult<User?>> @POST("update" ) fun update (@Body user: User ) : Observable<CResult<String?>> } Network.userApi() .login(name, encodedPassword) .defaultScheduler() .subscribe({ if (it.err_code == 0 ) { } else { toast("错误代码:${it.err_code} ,错误信息:${it.err_msg} " ) } }, { toast("网络异常:${it.message} " ) }) Network.userApi() .update(user) .defaultScheduler() .subscribe({ if (it.err_code == 0 ) { } else { } }, { toast("网络异常:${it.message} " ) })
使用方法 2,基于 kotlin 的 coroutine 首先给 ViewModel 添加一个 apiForCResult 扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fun <T> ViewModel.apiForCResult ( block: suspend CoroutineScope .() -> CResult <T ?>, okFun: (T ?) -> Unit = {}, failFun: (String ) -> Unit = {} ) { viewModelScope.launch { try { val result = withContext(Dispatchers.IO, block) if (result.err_code == 0 ) { okFun(result.data ) } else { failFun(result.err_msg) } } catch (e: Exception) { failFun("${e.message} " ) } } }
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 interface UserApi { @POST("login" ) suspend fun login (@Query("name" ) name: String , @Query("password" ) password: String ) : CResult<User?> @POST("update" ) suspend fun update (@Body user: User ) : CResult<String?> } class LoginViewModel : ViewModel () { fun login ( name: String , password: String , okFun: (User ?) -> Unit , failFun: (String ) -> Unit ) { apiForCResult( { Network.userApi().login(name, password) }, okFun, failFun ) } fun update ( user: User , okFun: (String ?) -> Unit , failFun: (String ) -> Unit ) { apiForCResult( { Network.userWithAuthApi().update(user) }, okFun, failFun ) } } class UserActivity { override fun onOptionsItemSelected (item: MenuItem ) : Boolean { when (item.itemId) { R.id.login -> { viewModel.login(name, password, ::doSuccess, ::toast) } R.id.updaete -> { viewModel.update( user, {toast("更新成功" )}, {toast(it)} ) } } return super .onOptionsItemSelected(item) } }
总结 结合使用 kotlin 的扩展功能,可以简化许多冗余的代码。