Retrofit2是square公司出品的一个网络请求库,目前非常流行,特别适合于rest请求。网上也有不少介绍该库的文章,但别人的终究是别人的,还需要转化为自己的才行。正所谓“纸上得来终觉浅,绝知此事要躬行”,本着学习的态度笔者对retroift2的用法进行了下列研究,主要包括以下几个方面
- get请求
- post请求(包括key/value,以及body)
- 文件上传(进度监听)
- 文件下载
- 与RxJava整合
单独使用Retrofit2
引入类库
1 2 3
| compile 'com.squareup.retrofit2:retrofit:2.0.0' compile 'com.squareup.retrofit2:converter-gson:2.0.0' compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
|
构造http接口类
Retrofit2可以根据一个服务接口类,利用jdk动态代理生成它的相应实现。
1 2 3 4 5
| retrofit=new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); myService=retrofit.create(MyService.class);
|
生成了代理类之后,就可以进行相应请求了
Get请求
1 2
| @GET("rest/findUserForGet") Call<User> findUserForGet(@Query("id") int id, @Query("username") String username,@Query("address") String address);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Call<User> userCall=myService.findUserForGet(12,"张明明","北京海淀区"); userCall.enqueue(new Callback<User>() { @Override public void onResponse(Call<User> call, Response<User> response) { //主线程 Log.e(TAG,Thread.currentThread().getName()); Log.e(TAG,response.body().toString());
}
@Override public void onFailure(Call<User> call, Throwable t) { Log.e(TAG,t.getMessage()); } });
|
需要注意Query注解不能丢,即使形参和请求的key相同也要加上,否则报错;另外回调函数发生在主线程,可以进行UI相关的操作
Post请求(key/value)
1 2 3
| @FormUrlEncoded @POST("rest/findUserForPost") Call<ResponseBody> findUserForPost(@Field("id") int id, @Field("username") String username,@Field("address") String address);
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| Call<ResponseBody> userCall=myService.findUserForPost(9,"陈玄功","恶人谷"); userCall.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { Log.e(TAG,getResponsString(response.body()));
}
@Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(TAG,t.getMessage()); } });
|
Post请求(body体)
1 2
| @POST("rest/postBodyJson") Call<User> postBodyJson(@Body User user);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| User user=new User(); user.setId(2); user.setUsername("李明"); user.setBirthday("1995-09-06 09-09-08"); user.setSex("1"); Call<User> userCall=myService.postBodyJson(user); userCall.enqueue(new Callback<User>() { @Override public void onResponse(Call<User> call, Response<User> response) { Log.e(TAG,response.body().toString()); }
@Override public void onFailure(Call<User> call, Throwable t) { Log.e(TAG,t.getMessage()); } });
|
此种方式会默认加上Content-Type: application/json; charset=UTF-8
的请求头,即以JSON格式请求,再以JSON格式响应。
单个文件上传
1 2 3 4
| @Multipart @POST("rest/upload") Call<ResponseBody> upload(@Part("username") RequestBody username,@Part("address") RequestBody address, @Part MultipartBody.Part file);
|
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
| File file=new File(Environment.getExternalStorageDirectory(),"测试01.jpg");
RequestBody username = RequestBody.create( MediaType.parse("multipart/form-data"), "jim");
RequestBody address = RequestBody.create( MediaType.parse("multipart/form-data"), "天津市");
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
CountingRequestBody countingRequestBody=new CountingRequestBody(requestFile, new CountingRequestBody.Listener() { @Override public void onRequestProgress(long bytesWritten, long contentLength) { Log.e(TAG,contentLength+":"+bytesWritten); } });
MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), countingRequestBody);
Call<ResponseBody> userCall=myService.upload(username, address, body); userCall.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { Log.e(TAG, getResponsString(response.body()));
}
@Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(TAG, t.getMessage()); } });
|
多文件上传
1 2 3
| @Multipart @POST("rest/upload") Call<ResponseBody> uploads(@PartMap Map<String, RequestBody> params);
|
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
| Map<String,RequestBody> params=new LinkedHashMap<>(); File file1=new File(Environment.getExternalStorageDirectory(),"测试01.jpg"); RequestBody filebody1 =RequestBody.create(MediaType.parse("multipart/form-data"), file1); CountingRequestBody countingRequestBody1=new CountingRequestBody(filebody1, new CountingRequestBody.Listener() { @Override public void onRequestProgress(long bytesWritten, long contentLength) { Log.e(TAG,"file1:"+contentLength+":"+bytesWritten); } }); params.put("file\";filename=\""+file1.getName(),countingRequestBody1);
File file2=new File(Environment.getExternalStorageDirectory(),"girl.jpg"); RequestBody filebody2 =RequestBody.create(MediaType.parse("multipart/form-data"), file2); CountingRequestBody countingRequestBody2=new CountingRequestBody(filebody2, new CountingRequestBody.Listener() { @Override public void onRequestProgress(long bytesWritten, long contentLength) { Log.e(TAG,"file2:"+contentLength+":"+bytesWritten); } }); params.put("file\";filename=\""+file2.getName(),countingRequestBody2);
File file3=new File(Environment.getExternalStorageDirectory(),"测试02.jpg"); RequestBody filebody3 =RequestBody.create(MediaType.parse("multipart/form-data"), file3); CountingRequestBody countingRequestBody3=new CountingRequestBody(filebody3, new CountingRequestBody.Listener() { @Override public void onRequestProgress(long bytesWritten, long contentLength) { Log.e(TAG,"file3:"+contentLength+":"+bytesWritten); } }); params.put("file\";filename=\""+file3.getName(),countingRequestBody3);
params.put("username", RequestBody.create( MediaType.parse("multipart/form-data"), "jim")); params.put("address", RequestBody.create( MediaType.parse("multipart/form-data"), "天津市"));
Call<ResponseBody> userCall=myService.uploads(params); userCall.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { Log.e(TAG, getResponsString(response.body()));
}
@Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(TAG, t.getMessage()); } });
|
文件下载
1 2 3
| @Streaming @GET("image/{filename}") Call<ResponseBody> downFile(@Path("filename") String fileName);
|
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
| Call<ResponseBody> userCall=myService.downFile(fname); userCall.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { try {
String fileName=Environment.getExternalStorageDirectory()+"/"+fname; FileOutputStream fos=new FileOutputStream(fileName); InputStream is=response.body().byteStream();
byte[] buf=new byte[1024]; int len; while ((len=is.read(buf))!=-1){ fos.write(buf,0,len); } is.close(); fos.close();
}catch (Exception ex){ Log.e(TAG,ex.getMessage()); } Log.e(TAG,"success");
}
@Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(TAG,t.getMessage()); } });
|
开启OKHttp的日志拦截
Retrofit2底层还是使用的OKHttp,可以使用其相关的一些特性,比如开启日志拦截,此时就不能使用Retrofit2默认的OKHttp实例,需要自己单独构造,完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); httpClient.addInterceptor(logging);
retrofit=new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(httpClient.build()) .build();
|
开启日志后,会记录request和response的相关信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 05-14 03:00:42.128 5212-25519/com.bryan D/OkHttp: --> POST http://192.168.1.104:8080/mobile/rest/postBodyJson http/1.1 05-14 03:00:42.128 5212-25519/com.bryan D/OkHttp: Content-Type: application/json; charset=UTF-8 05-14 03:00:42.128 5212-25519/com.bryan D/OkHttp: Content-Length: 71 05-14 03:00:42.128 5212-25519/com.bryan D/OkHttp: {"birthday":"1995-09-06 09-09-08","id":2,"sex":"1","username":"李明"} 05-14 03:00:42.128 5212-25519/com.bryan D/OkHttp: --> END POST (71-byte body) 05-14 03:00:42.207 5212-25519/com.bryan D/OkHttp: <-- 200 OK http://192.168.1.104:8080/mobile/rest/postBodyJson (79ms) 05-14 03:00:42.207 5212-25519/com.bryan D/OkHttp: Server: Apache-Coyote/1.1 05-14 03:00:42.207 5212-25519/com.bryan D/OkHttp: Access-Control-Allow-Origin: * 05-14 03:00:42.207 5212-25519/com.bryan D/OkHttp: Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept 05-14 03:00:42.207 5212-25519/com.bryan D/OkHttp: Content-Type: application/json;charset=UTF-8 05-14 03:00:42.207 5212-25519/com.bryan D/OkHttp: Transfer-Encoding: chunked 05-14 03:00:42.207 5212-25519/com.bryan D/OkHttp: Date: Sat, 14 May 2016 07:00:42 GMT 05-14 03:00:42.207 5212-25519/com.bryan D/OkHttp: OkHttp-Sent-Millis: 1463209242134 05-14 03:00:42.207 5212-25519/com.bryan D/OkHttp: OkHttp-Received-Millis: 1463209242206 05-14 03:00:42.208 5212-25519/com.bryan D/OkHttp: {"id":2,"username":"李明","sex":"1","birthday":"1995-09-06","address":null} 05-14 03:00:42.208 5212-25519/com.bryan D/OkHttp: <-- END HTTP (77-byte body)
|
Retrofit2与RxJava整合
引入类库
1 2 3 4 5
| compile 'io.reactivex:rxandroid:1.1.0' compile 'com.squareup.retrofit2:retrofit:2.0.0' compile 'com.squareup.retrofit2:converter-gson:2.0.0' compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0' compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
|
构造http接口类
Retrofit2可以根据一个服务接口类,利用jdk动态代理生成它的相应实现。
1 2 3 4 5 6 7
| retrofit=new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(httpClient.build()) .build(); myService=retrofit.create(MyRxService.class);
|
Get请求
1 2
| @GET("rest/findUserForGet") Observable<User> findUserForGet(@Query("id") int id, @Query("username") String username,@Query("address") String address);
|
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
| myService.findUserForGet(12,"张明明","北京海淀区") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<User>() {
@Override public void onStart() { super.onStart(); Log.e(TAG,"onStart"); }
@Override public void onCompleted() { Log.e(TAG,"onCompleted"); }
@Override public void onError(Throwable e) { Log.e(TAG,e.getMessage()); }
@Override public void onNext(User user) { Log.e(TAG,user.toString()); } });
|
Post请求(key/value)
1 2 3
| @FormUrlEncoded @POST("rest/findUserForPost") Observable<ResponseBody> findUserForPost(@Field("id") int id, @Field("username") String username, @Field("address") String address);
|
实现和Get类似
多文件上传
1 2 3
| @Multipart @POST("rest/upload") Observable<ResponseBody> uploads(@PartMap Map<String, RequestBody> params);
|
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
| myService.uploads(params) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<ResponseBody>() {
@Override public void onStart() { super.onStart(); Log.e(TAG, "onStart"); }
@Override public void onCompleted() { Log.e(TAG, "onCompleted"); }
@Override public void onError(Throwable e) { Log.e(TAG, e.getMessage()); }
@Override public void onNext(ResponseBody body) { Log.e(TAG, getResponsString(body)); } });
|
文件下载
1 2 3
| @Streaming @GET("image/{filename}") Observable<ResponseBody> downFile(@Path("filename") String fileName);
|
总结
无论是单独使用Retrofit2还是整合RxJava一起使用,请求体的构造部分并没有多大变化,主要区别是RxJava支持链式写法,可以对response作更为复杂的处理。现实业务中大多数也是两者结合起来使用。
Github Demo