基于Retrofit2.6+kotlin协程
基础 导入 1 2 3 4 5 6 # retrofit implementation 'com.squareup.retrofit2:retrofit:2.6.0' # gson解析器,可以换成其他的json解析 implementation 'com.squareup.retrofit2:converter-gson:2.64.0' # 与kotlin协程结合,可以换成rxJava+rxAndroid implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
编写接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 interface ArticleApi { @Headers(WindConstants.CONTENT_TYPE_JSON, WindConstants.APP_ID, WindConstants.API_KEY) @GET("1/classes/ArticleModel" ) suspend fun getAllArticle (@Query("skip" ) skip: Int = 0 , @Query("order" ) order: String = "-createdAt" ) : ApiBean.ApiListResponse<ArticleModel> @Headers(WindConstants.CONTENT_TYPE_JSON, WindConstants.APP_ID, WindConstants.API_KEY) @GET("1/classes/ArticleModel" ) suspend fun getArticleOfAuthor (@Query("where" ) where : String , @Query("order" ) order: String = "-createdAt" ) : ApiBean.ApiListResponse<ArticleModel> @Headers(WindConstants.CONTENT_TYPE_JSON, WindConstants.APP_ID, WindConstants.API_KEY) @POST("1/classes/ArticleModel" ) suspend fun addNewArticle (@Body jsonStr: RequestBody ) : ApiBean.AddResponse @Headers(WindConstants.CONTENT_TYPE_JSON, WindConstants.APP_ID, WindConstants.API_KEY) @GET("1/classes/ArticleModel/{objectId}" ) suspend fun getArticle (@Path("objectId" ) objectId: String ) : ArticleModel }
创建retrofit实例 1 2 3 4 5 6 7 8 9 10 11 12 13 object ApiManager { private val retrofit: Retrofit = Retrofit.Builder() .baseUrl(PrivateConstants.BMOB_API_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) .build() val articleApi = retrofit.create(ArticleApi::class.java) val pictureApi = retrofit.create(PictureApi::class.java) val userApi = retrofit.create(UserApi::class.java) val commentApi = retrofit.create(CommentApi::class.java) }
调用接口方法 在api的直接方法之外再封装一层,处理返回数据,把需要的数据部分传给回调进行下一步操作,因为kotlin可以使用方法作为参数因此这里可以不用写回调函数了。
1 2 3 4 suspend fun getAllArticle(next: (List<ArticleModel>) -> Unit) { val response = ApiManager.articleApi.getAllArticle() next(response.results) }
最外层直接调用,可以在UI层,在MVVM架构中应该在Repository层编写一个方法在其中调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fun loadArticleList() { // 使用数据库(可选) if (articleList == null) { articleList = articleDao.loadAll() } CoroutineScope(Dispatchers.IO).launch { ArticleService.getAllArticle { articles -> // 对结果进行数据处理 articles.forEach { it.lastUpdateTime = System.currentTimeMillis() } // 保存结果或进行其他操作 articleDao.clear() articleDao.saveAll(articles) } } }
最后在UI层通过viewModel调用该方法。
配置 基础链接与解析器 1 2 3 4 5 private val retrofit: Retrofit = Retrofit.Builder() .baseUrl(PrivateConstants.BMOB_API_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) .build()
使用注解-固定参数 在接口方法上用注解的方式添加header:
1 @Headers(WindConstants.CONTENT_TYPE_JSON, WindConstants.APP_ID, WindConstants.API_KEY)
其中:
1 2 3 const val CONTENT_TYPE_JSON = "Content-Type:application/json" const val APP_ID = "X-Bmob-Application-Id:${PrivateConstants.APP_ID}" const val API_KEY = "X-Bmob-REST-API-Key:${PrivateConstants.API_KEY}"
使用注解-可变参数 使用注解但把header作为方法参数传递:
1 2 3 4 5 6 7 // 更新用户信息 @Headers(WindConstants.CONTENT_TYPE_JSON, WindConstants.APP_ID, WindConstants.API_KEY) // 固定header @PUT("1/users/{objectId}") suspend fun saveUser(@Header("X-Bmob-Session-Token") header: String, // 添加key为X-Bmob-Session-Token的header,value为传递的参数值 @Path("objectId") objectId: String, @Body jsonStr: RequestBody): ApiBean.UpdateResponse
拦截器除了添加header还能对传入的参数进行一系列处理:
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 // 定义一个拦截器 private val httpClientBuilder = OkHttpClient.Builder().addInterceptor { chain -> // 拦截器拦截的初始请求 val paramsMap = baseParamsMap.clone() as HashMap<String, String> val oldRequest = chain.request() // 拿到旧的请求内容 val oldUrl = oldRequest.url() // 旧的请求链接 val queryKeys = oldUrl.queryParameterNames() // 获取链接中带的参数 for ((index, key) in queryKeys.withIndex()) { paramsMap[key] = oldUrl.queryParameterValue(index) } // 在旧的链接上添加新的参数 val urlBuilder = oldUrl.newBuilder() .addQueryParameter("appId", AppInfo.instance.appID) .addQueryParameter("appKey", AppInfo.instance.appKey) .addQueryParameter("deviceId", EncodeUtil.deviceID) .addQueryParameter("requestId", requestId) .addQueryParameter("systemVersion", Build.VERSION.SDK_INT.toString()) .addQueryParameter("platformString", Build.BRAND + " " + Build.MODEL) .addQueryParameter("channelId", "abcd") .addQueryParameter("v", versionCode) // 给旧的请求添加新的链接生成新的请求 val newRequest = oldRequest.newBuilder() .header("User-Agent", "Your-App-Name") // 自定义添加header .header("Accept", "application/vnd.yourapi.v1.full+json") .method(oldRequest.method(), oldRequest.body()) // method和body保持原样 .url(urlBuilder.build()) .build() if (isDebug) { Log.i("mojulist", newRequest.url().url().toString()) } chain.proceed(newRequest) // 请求继续 }
因为Retrofit底层使用的是okhttp,可以自定义一个OkHttpClient,在实例化Retrofit时指定。因此可以通过自定义这个Client来实现自定义api请求。