Android 网络框架 Retrofit2
概述
Retrofit是一个OkHttp网络请求框架的封装库,Retrofit通过注解配置网络参数,可以按照我们的规则去构造实际的HTTP请求,能够灵活设置URL、头部、请求体、返回值等,是目前最优雅的一个网络框架。
添加依赖
implementation 'com.squareup.retrofit2:retrofit:2.2.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'//添加对RxJava的支持 implementation 'com.squareup.retrofit2:converter-gson:2.1.0' //添加Json数据的支持 <uses-permission android:name="android.permission.INTERNET"/>//网络请求必要的权限
Retrofit基本使用三部曲
创建实例
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://id1.option****.cc:***1/")//Retrofit2 的baseUlr 必须以 /(斜线) 结束 .build(); RetrofitService service = retrofit.create(RetrofitService.class);//创建接口的代理对象
定义接口
public interface RetrofitService { @GET("/pursuit/getPursuitInfo") Call<ResponseBody> getBlog(@Query("id") int id); @GET("/pursuit/getPursuitInfo/{id}") Call<ResponseBody> getBlog2(@Path("id") String id); }
创建 同步/异步 回调
//异步的回调 Call<ResponseBody> call = service.getBlog(123); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) { try { Log.i("返回信息",response.body().string()+"");//打印返回信息 } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { t.printStackTrace();//错误信息 } }); /*同步的回调,如果不是在一个Activity或者一个Fragment中去执行,那么也就意味着,你可以不需要开启子线程去执行网络请求。如果是在主线程,就必须开启子线程来进行同步请求 使用call.execute()同步请求,只能调用一次。如果要多次使用这个方法,需要 call.clone()重新生成新的Call实例*/ new Thread(new Runnable() { @Override public void run() { try { retrofit2.Response<ResponseBody> response = call.execute(); response.body(); } catch (IOException e) { e.printStackTrace(); } } });
HTTP协议中的常用请求
GET: 请求指定的页面信息,以?分割URL和传输数据,参数之间以&相连,最多只能是1024字节,并返回实体主体,该操作用于获取信息而非修改信息
HEAD: 只请求页面的首部。
POST: POST表示可能修改变服务器上的资源的请求,.POST的安全性要比GET的安全性高
PUT: 从客户端向服务器传送的数据取代指定文档的内容。
Retrofit注解的重点详解
注解汇总
需要理解并灵活搭配使用
基本请求
Responsebody是Retrofit网络请求回来的原始数据类。get方式可以在url后面串联
@Path注解用于替换请求参数,@Query注解相当于url后面的串联,他们可以同时使用
请看下面第三种表达式:
@GET("/pursuit/getPursuitInfo?id=123") Call<ResponseBody> getCall(); @POST("/pursuit/getPursuitInfo") Call<ResponseBody> getBlog(@Query("id") int id); @GET("/pursuit/getPursuitInfo/{id}") Call<ResponseBody> getBlog2(@Path("id") String id);
自定义请求
需要用到@HTTP注解
/* method:网络请求的方法,注意大小写 * path:网络请求地址路径 * hasBody:是否有请求体 * {id} 表示是一个变量*/ @HTTP(method = "GET", path = "/pursuit/getPursuitInfo/{id}", hasBody = false) Call<ResponseBody> getCall(@Path("id") int id);
请求体非Form表单
@Body是Post方式提交非Form的表单
@POST("/pursuit/getPursuitInfo")
Observable<String> getPursuitInfo(@Body User user);
请求体是Form表单
如果请求的Form表单,需要用@FormUrlEncoded标注,@Field注解用于表单字段,它和@FieldMap都需要@FormUrlEncoded配合使用
@Field和@Query都是表单字段,@FieldMap和@Query都是批量增加表单的提交域。区别如上图,一个拼接在URL上适用于get方式,一个体现在请求体上试用于post方式,区分好使用场景。
@POST("/pursuit/getPursuitInfo")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
设置请求头
两种请求头的,@Header用于添加不固定的请求头,作用于方法的参数。@Headers用于添加固定的请求头,作用于方法
@Headers("Authorization: authorization") @GET("/pursuit/getPursuitInfo") Call<ResponseBody> getUser(); @GET("/pursuit/getPursuitInfo") Call<ResponseBody> getUser(@Header("Authorization") String authorization);
动态替换URL
用@Url替换已经设置的baseUrl
@GET public Call<ResponseBody> profilePicture(@Url String url);
图片上传
@POST("user/updateAvatar.do")
@Multipart
Call<ResponseBody> upload(@Part("upload1\"; filename=\"image1.jpg\"") RequestBody imgs );
文件上传
发送form-encoded的数据,用于有文件上传的场景时要用 @Multipart 注解,@Part和@PartMap适用于有文件上传的情况
@POST("mobile/upload")
@Multipart
Call<ResponseBody> upload(@Part MultipartBody.Part file);
文件上传相关阅读
※以上就是所有参数注解的用法,需要实践灵活试用。
Retrofit与Gson
添加对gson的支持
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://id1.option****.cc:***1/")//Retrofit2 的baseUlr 必须以 /(斜线) 结束 .addConverterFactory(GsonConverterFactory.create())//添加对gson的支持 .build();
建立接口,返回Bean
public interface PostRoute { @Headers({"Content-Type: application/json","Accept: application/json"})//需要添加头 @POST("api/FlyRoute/Add") Call<FlyRouteBean> postFlyRoute(@Body RequestBody route);//传入的参数为RequestBody }
将Bean转换成json字符串
FlyRouteBean flyRouteBean=new FlyRouteBean(); flyRouteBean=initdata(flyRouteBean);//根据Bean类初始化一个需要提交的数据类 Gson gson=new Gson(); String route= gson.toJson(flyRouteBean);//通过Gson将Bean转化为Json字符串形式
提交json数据
RequestBody body=RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),route); //将json转换成RequestBody请求体 Call<FlyRouteBean> call=postRoute.postFlyRoute(body);//提交
Retrifit与RxJava
引入RxJava
compile 'io.reactivex.rxjava2:rxjava:2.1.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
添加对RxJava的支持
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://id1.option****.cc:***1/")//Retrofit2 的baseUlr 必须以 /(斜线) 结束 .addConverterFactory(GsonConverterFactory.create())//添加对gson的支持 .client(client)//添加okhttp的支持 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加对RxJava的支持 .build();
连接超时与开启okhttp的日志打印
private static final OkHttpClient client = new OkHttpClient.Builder(). addInterceptor(new HttpLoggingInterceptor().//开启日志信息 setLevel(HttpLoggingInterceptor.Level.BODY)). connectTimeout(600, TimeUnit.SECONDS).//连接超时 readTimeout(600, TimeUnit.SECONDS).//读取超时 writeTimeout(600, TimeUnit.SECONDS).build();//写入超时
建立接口,返回Observable被观察者
@GET("top250") Observable<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
建立观察者
Observable<String> observable =service.getTopMovie(0, 10) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<ResponseBody>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(ResponseBody responseBody) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
Retrofit的简单封装
retrofit工具类
public class WebManager { private static WebManager webManager; private RetrofitService retrofitService; public WebManager(Context context) { Retrofit retrofit = new Retrofit.Builder() .baseUrl(StaticConstant.HOST_PORT)//地址 .addConverterFactory(GsonConverterFactory.create())//添加gson支持 .client(client)//设置okhttp连接 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加RxJava的支持 .build(); retrofitService = retrofit.create(RetrofitService.class); } private static final OkHttpClient client = new OkHttpClient.Builder(). addInterceptor(new HttpLoggingInterceptor().//开启日志信息 setLevel(HttpLoggingInterceptor.Level.BODY)). connectTimeout(600, TimeUnit.SECONDS).//连接超时 readTimeout(600, TimeUnit.SECONDS).//读取超时 writeTimeout(600, TimeUnit.SECONDS).build();//写入超时 //获取单例 public static WebManager getInstance(Context context){ if(webManager == null){ webManager = new WebManager(context); } return webManager; } //上传xx信息,使用@Field注解 public void postInfoField(Callback<ResponseBody> callback, String str){ retrofitService.getWarrant("queryApplicationForm",null,str) .enqueue(callback); } //上传xx信息,使用@FieldMap注解 public void postInfoFieldMap(Callback<ResponseBody> callback, String num,String name){ Map<String, Object> map=new HashMap<>(); map.put("hotelCode",num); map.put("hotelName",name); retrofitService.getUnlicensedNum(map).enqueue(callback); } //上传xx信息,使用@Body 注解 public void postMultipartBody(Callback<ResponseBody> callback, String key,File file, String name){ MultipartBody.Builder builder= new MultipartBody.Builder().setType(MultipartBody.FORM); if(file.exists()) { builder.addFormDataPart("access_token", key); builder.addFormDataPart("name", name); builder.addFormDataPart("image", ImageUtil.imageToBase64(file.getPath())); builder.addFormDataPart("image_type", "BASE64"); } retrofitService.getUnlicensedCheck(builder.build()).enqueue(callback); } //上传xx信息,带文件,使用@Body 注解 public void postMultipartBody2(Callback<ResponseBody> callback, HotelInfo hotelInfo,String pictureCardPath,String picturePath){ Gson gson=new Gson(); String json= gson.toJson(hotelInfo); File file = new File(pictureCardPath); File file2 = new File(picturePath); MultipartBody.Builder builder= new MultipartBody.Builder().setType(MultipartBody.FORM); if(!file.exists()&&!file2.exists()) { try { builder.addFormDataPart("info", json); builder.addFormDataPart("file",file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file)); builder.addFormDataPart("file2",file2.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file2)); retrofitService.getHotelInfoFile(builder.build()).enqueue(callback); } catch (Exception e) { e.printStackTrace(); Log.i("WebManager","上传异常"); } } } }
retrofit服务类
public interface RetrofitService { @FormUrlEncoded @POST("/Hotels/ExpressServlet") Call<ResponseBody> getWarrant(@Field("type") String type, @Field("E_Code") String code, @Field("applicationFormId") String id); @FormUrlEncoded @POST("Hotels/NoRegisterServlet") Call<ResponseBody> getUnlicensedNum(@FieldMap Map<String,Object> fieldMap); @POST("/rest/2.0/face/v3/person/verify") Call<ResponseBody> getUnlicensedCheck(@Body MultipartBody builder); @Headers("Connection: close" ) @POST("/Hotels/UploadFileImgServlet") Call<ResponseBody> getHotelInfoFile(@Body MultipartBody builder); }
RxJava相关阅读
Retrofit代码混淆配置
-dontwarn retrofit.** -keep class retrofit.** { *; } -keepattributes Signature -keepattributes Exceptions