- Android网络框架使用过的包括HttpClient,HttpURLConnection,Volley,OKhttp等等,OKhttp是现在常用的用于网络访问的框架。
- 现在常常开发中是Okhttp配合Retrofit和RxJava一起使用,单独显得更加优雅。
- 本文就介绍下这三个配合使用,现在Retrofit默认就是使用的OKhttp作为网络访问框架。下次可以把OKhttp单独使用写一下。
RxJava、Retrofit配合网络访问的封装库
感兴趣的可以直接去下载使用
简单介绍下Retrofit的使用,直接使用的官网 1 的例子
1、定义访问服务器接口的接口文件
public interface GitHubService {
// 一个简单的get请求接口,Retrofit中使用@GET注解表示get请求
// 下面这个接口定义是返回一个Repo对象的List集合,{user}表示将请求参数填充在链接的此处
@GET("users/{user}/repos")
Call> listRepos(@Path("user") String user);
}
2、创建Retrofit对象
// 获取Retrofit对象,拼接接口的公有地址
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
3、获取接口对象
// 获取网络访问接口对象
GitHubService service = retrofit.create(GitHubService.class);
4、调用接口请求服务器数据,到这里整个网络访问流程就走完了
Call> repos = service.listRepos("octocat");
1、group/{id}/users次数的{id}表示将@Path修饰的参数插入这里
@GET("group/{id}/users")
Call> groupList(@Path("id") int groupId);
2、@Query("sort")表示根据sort这个参数到服务器去查询数据,就是我们平常访问接口的查询条件
@GET("group/{id}/users")
Call> groupList(@Path("id") int groupId, @Query("sort") String sort);
3、@QueryMap Map,表示使用Map插入一组查询条件,不用我们每个查询条件都去写一次
例如这种:
@GET("group/{id}/users")
Call> groupList(@Path("id") int groupId, @Query("sort") String sort,, @Query("age") int age);
或者更多的参数,我们就可以使用@QueryMap注解去修饰,然后传入我们需要给服务器的请求参数结合。使用方式如下
@GET("group/{id}/users")
Call> groupList(@Path("id") int groupId, @QueryMap Map options);
1、我们常用的使用json对象访问接口,@Body修饰我们上传的参数对象
@POST("users/new")
Call createUser(@Body User user);
2、使用表单上传参数,@Field修饰post请求的参数,如果参数过多Retrofit依然给我们
准备了跟get请求一样的上传多个参数的map注解@FieldMap,将请求参数封装成Map集合
@FormUrlEncoded
@POST("user/edit")
Call updateUser(@Field("first_name") String first, @Field("last_name") String last);
3、使用表单进行文件上传,@Part,@PartMap用于表单字段,适用于有文件上传的情况
@Multipart
@PUT("user/photo")
Call updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
文件上传后面再详细讲解,请往后看。
类型 | 注解 | 作用 |
---|---|---|
@POST | ||
@GET | ||
@PUT | ||
网络请求方法 | @DELETE | 对应HTTP请求中的所有方法,都指定接收一个网络地址 |
@PATH | ||
@HEAD | ||
@OPTIONS | ||
@HTTP | 用来替换上面七个注解,以及更多的功能 |
例如:
post或者其他请求的使用
@POST("file/batch/upload")
但是@HTTP的使用,指定请求方法,请求路径,指定是都包含Body请求体
@HTTP(method = "GET",path = "")
@HTTP(method = "GET",path = "",hasBody = true)
注解 | 作用 |
---|---|
@FormUrlEncoded | POST请求,标记为表单请求 |
@Multipart | 标记支持表单类型的文件上传 |
@Streaming | 表示以流的形式返回数据,返回数据较大的场景,一般用于文件下载 |
注解 | 作用 |
---|---|
@Headers | 添加在方法上固定的请求头 |
@Header | 将请求头作为请求参数,动态添加单个请求头 |
@HeaderMap | 动态添加一组请求头 |
@Body | 修饰非表单请求,我们常用的使用json对象请求接口 |
@Field | post表单请求,添加一个请求参数 |
@FieldMap | post表单请求,添加一组请求参数 |
@Query | get请求使用的表单字段,表示添加一个请求参数 |
@QueryMap | get请求使用的表单字段,表示添加一组请求参数 |
@Path | URL缺省值 |
@Url | 传入整个URL |
例如
1、@Url注解的使用,没在@GET请求传递URL地址,而是全部通过动态传入
@Streaming
@GET
fun download(@Url url: String): Observable
2、@Path修饰的参数,是填充到@GET({"study/{id}"})链接中{}修饰的链接处
@Streaming
@GET("{name}")
fun download1(@Path("name") name:String): Observable
3、下面这两种就是添加固定的一个或多个请求头的方式
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call> widgetList();
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call getUser(@Path("username") String username);
4、下面就是动态添加一个或一组请求头的方式
@GET("user")
Call getUser(@Header("Authorization") String authorization)
@GET("user")
Call getUser(@HeaderMap Map headers)
整个我使用的库的封装,我就懒得贴代码了,是真的太多了,贴上大家看着也烦,O(∩_∩)O哈哈~,我会把代码放在github上,有兴趣的可以下载来看看。
代码中我已经写得很清楚了,具体是干什么的,请求库我会放在github,调用的代码就参考下面的使用,我就没有去写例子了,直接从我写的项目中抽出来的。
implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
// 线程的切换
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
// 可以直接将json字符串返回回来
implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
// 返回Gson解析之后的对象
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
// 定义请求接口,Observable是RxJava中的操作符
@GET("${BaseHost.CORE}fx/area/sparkAreaList")
fun getSparkAreaList(@Query("fId") fId: Int): Observable>>
// 具体的调用,还是很简洁的
RetrofitManager
.instance // 获取RetrofitManager对象
.createService(ApiService::class.java) // 获取接口对象
.getSparkAreaList(fId) // 调用接口
.compose(SelfTransformer()) // 封装了线程切换的共有操作
.subscribe(SelfObserver(object : NetCallBack> { // 封装了数据的解析
override fun onSubscribe(disposable: Disposable) {
}
override fun onSuccess(t: MutableList) {
areaListener.onSuccess(t)
}
override fun onError(exception: ApiException) {
areaListener.onError(exception)
}
}))
// 表单类型的Post请求,这里返回值是直接返回Gson解析出来的对象
@FormUrlEncoded
@POST("user/login")
fun loginMap(@FieldMap map: MutableMap): Observable>
// 具体的调用,其实跟get没有多大差别
RetrofitManager
.instance
.createService(ApiService::class.java)
.loginMap(map)
.compose(SelfTransformer())
.subscribe(SelfObserver(object : NetCallBack {
override fun onSubscribe(disposable: Disposable) {
}
override fun onSuccess(user: User) {
userListener.onSuccess(user)
}
override fun onError(exception: ApiException) {
userListener.onError(exception)
}
}))
// 请求对象类型的,直接返回服务器告诉我们的json字符串
fun updateAd(@HeaderMap headMap: MutableMap, @Body updateProjectReq: UpdateProjectReq): Observable
// 这里的处理方式可以对比上面的代码,直接使用服务器返回的json我们自己做了单独的处理,这里我的参考代码也会放在github,有兴趣的可以下载下来看
RetrofitManager
.instance
.createService(ApiService::class.java)
.updateAd(headMap, updateProjectReq)
.map(HttpResultFunc1())
.compose(SelfTransformer1())
.subscribe(SelfObserver1(object : NetCallBack {
override fun onSubscribe(disposable: Disposable) {
}
override fun onSuccess(t: BaseDataF) {
updateAdListener.onSuccess(t)
}
override fun onError(exception: ApiException) {
updateAdListener.onError(exception)
}
}))
// 此处我自己使用的文件下载,直接传入下载链接用@Url修饰,@Streaming表示以流的形式返回
@Streaming
@GET
fun download(@Url url: String): Observable
// 具体的使用,具体的下载拦截管理之类的封装,去查看DownRetrofitManager,这里就是完整的处理逻辑,在ProgressListener 中处理下载进度
DownRetrofitManager
.instance
.createService(ApiService::class.java, object : ProgressListener {
@SuppressLint("SetTextI18n")
override fun onProgress(bytesRead: Long, contentLength: Long, done: Boolean) {
LogUtils.d("DownloadDialog", "当前进度;$bytesRead==>总的进度:$contentLength==>是否完成:$done")
isDownComplete = done
val progress = ((bytesRead.toFloat() / contentLength.toFloat()) * 100).toInt()
activity.runOnUiThread {
progressBar?.progress = progress
tvSize?.text = String.format(
activity.getString(R.string.down_size),
FileUtils.formatSize(bytesRead),
FileUtils.formatSize(contentLength)
)
tvRatio?.text = "$progress%"
LogUtils.d("DownloadDialog", "进度$progress")
}
}
})
.download(downUrl)
.map(DownResultFunc())
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer {
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
disposable = d
}
override fun onNext(t: InputStream) {
FileUtils.writeFileFromIs(downPath, t)
}
override fun onError(e: Throwable) {
ToastUtils.showShort(activity, e.message.toString())
}
})
//@Multipart
@POST("${BaseHost.BUSINESS}learn/batch")
fun uploadImg(@HeaderMap headMap: MutableMap, @Body requestBody: RequestBody): Observable>>
// 参数封装
val builder: MultipartBody.Builder = MultipartBody.Builder()
for (file in files) {
val requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file)
//val requestBody = RequestBody.create(MediaType.parse("application/form-data;charset=utf-8"), file)
val progressBody = ProgressRequestBody(requestBody, progressListener)
// 如下两个参数的封装,是跟服务器定义的两个参数
builder.addFormDataPart("type", Constants.UPLOAD_IMG_COVER)
// 解决文件名为中文,程序崩溃,在获取到文件名的时候记得解码
builder.addFormDataPart("file", URLEncoder.encode(file.name, "UTF-8"), progressBody)
}
下面调用的.uploadImg(headMap, requestBody),requestBody = builder.build()
// 具体调用
RetrofitManager
.instance
.createService(ApiService::class.java)
.uploadImg(headMap, requestBody)
.compose(SelfTransformer())
.subscribe(SelfObserver(object : NetCallBack> {
override fun onSubscribe(disposable: Disposable) {
}
override fun onSuccess(t: MutableList) {
uploadListener.onSuccess(t)
}
override fun onError(exception: ApiException) {
uploadListener.onError(exception)
}
}))
https://square.github.io/retrofit/ ↩︎