rxjava2+rxandroid2+retrofit2 封装网络请求
来源:互联网 发布:慧典电子病历数据库 编辑:程序博客网 时间:2024/06/04 18:25
1.最近在学习 rxjava2+rxandroid2+retrofit2 封装网络请求 ,学了好久了,一头的包,今天就把之前学习的整理下。
注意:1.rxjava,rxandroid 更新到2.0以上,用法都不一样。
2.retrofit2 ,会自动去拉去okhttp3,所以不需要我们去添加依赖
Retrofit 是什么,官方文档解释说明,是一个封装好的网络请求客户端,也就是类似与我们安卓装的DefaultHttpClient,只不过Retrofit ,更强大。
Retrofit 的使用:
如何进行网络请求,首先我们要建一个AppService,接口类,主要是用来声明,进行网络请求要
带入的参数,比如公共参数,上传文件,自定义参数,比如登录(用户名,用户密码)
public interface AppService { @GET("api/") Call<BaseResultEntity<GetCarCount>> getCarCount( ); @POST("api?") Call<BaseResultEntity> getCarCount( @QueryMap Map<String, String> options );}
我们可以看到上面用到了注解,@POST,@GET,Retrofit 支持多种注解方法,我们慢慢道来
@POST
post请求 ,我们公司用的是post请求@GET
get请求@PUT
上传文件专用@DELETE
删除文件专用@Header/@Headers
用于设置请求头部参数
例.
设置单个请求头参数:
@Headers("Cache-Control: max-age=640000")@GET("user")Call<User> getUser()
也可以设置多个请求头参数:
@Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App"})@GET("user")Call<User> getUser()
一般情况,也没有必要去需要请求头部参数,所不会改变
如果真想自定义请求头部,也没有必要在这里写,要不然每次都要注解一下,麻烦很。
我们可以自定义一个拦截器,加入请求当中。
OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.connectTimeout(DEFAULT_OUT_TIME, TimeUnit.SECONDS); //手动创建一个OkHttpClient并设置超时时间 builder.addInterceptor(new HeadersInterceptor());
HeadersInterceptor类
package com.dk.basepack.bseaapplication.network;import java.io.IOException;import okhttp3.Interceptor;import okhttp3.Request;import okhttp3.Response;/** * Created by Administrator on 2017/10/12. */public class HeadersInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); Request request = original.newBuilder() .header("User-Agent", "ttdevs") .header("Content-Type", "application/json; charset=utf-8") .header("Accept", "application/json") .header("token", "abcdefg_ttdevs_hijklmn") .header("user_key", "ttdevs") .method(original.method(), original.body()) .build(); long t1 = System.nanoTime(); String requestHeader = String.format(">>>>>Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()); System.out.println(requestHeader); Response response = chain.proceed(request); long t2 = System.nanoTime(); System.out.println(String.format(">>>>>Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6d, response.headers())); System.out.println("====================================================="); return response; }}
现在来讲解,注解@POST ,@GET 后面括号放得是什么内容:
我们在进行网络请求的时候,实际上跟浏览器请求一样,
URL:
http://write.blog.csdn.net/index.php/api&cat_id=68&date=2017-10-20 16:49:33&direct=true&method=mobileapi.goods.get_all_list&page_no=1&son_object=json&task=5a71964ed1884d9da93c1ffd560bc7af&sign=B2D328C19E486B95D9BCDAA01D4F0295
http://write.blog.csdn.net/index.php/ 也是网站的根节点
api? 是根节点下的某一个分支,例,webapi,所以是多变的分支,设置 @POST(“api?”),相当于
http://write.blog.csdn.net/index.php/api
在下面例子中我们可以看到,我放的”api?”,
@POST("api?") Call<BaseResultEntity> getCarCount( @QueryMap Map<String, String> options );
我们在请求网络请求的时候肯定会自定义传入参数,比如 登录,需要加入,用户名,用户密码参数,如何添加。
先来说下GET 请求传参数的方法:
1.不带参数
@GET("News") Call<NewsBean> getItem();
相当于:http://102.10.10.132/api/News
2.带入url修改某一个节点
@Path(”要修改那个一个节点名字”) ,String newsId ; 声明类型,传入实参替换
@GET("News/{newsId}") Call<NewsBean> getItem(@Path("newsId") String newsId);
相当于:http://102.10.10.132/api/News/1
http://102.10.10.132/api/News/{资讯id}
3.URL带入参数
@GET("News") Call<NewsBean> getItem(@Query("newsId") String newsId);
相当于:
http://102.10.10.132/api/News?newsId=1
4. URL 带入多个参数
@GET("News") Call<NewsBean> getItem(@QueryMap Map<String, String> map);
相当于:
http://102.10.10.132/api/News?newsId={资讯id}&type={类型}…
@QueryMap,@Query 最好只在get请求中用(为什么稍后讲解),@Path可以在post,get请求使用
post 注解使用讲解:
@Path 在post请求中使用:
@FormUrlEncoded @POST("Comments/{newsId}") Call<Comment> reportComment( @Path("newsId") String commentId, @Field("reason") String reason);
相当于:
http://102.10.10.132/api/Comments/1
http://102.10.10.132/api/Comments/{newsId}
post请求 单个参数使用
post请求是看不到参数的,@Field 用于提交单个字段表单,一定要加 @FormUrlEncoded
@FormUrlEncoded @POST("Comments") Observable<BaseResultEntity> onUpdateVersion( @Field("name") String name );
相当于:
http://102.10.10.132/api/Comments
post请求提交多个表单字段
@FieldMap 用于支持post请求,提交多个表单字段
@FormUrlEncoded @POST("Comments") Observable<BaseResultEntity> onUpdateVersion( @FieldMap Map<String, String> options );
相当于:
http://102.10.10.132/api/Comments
post请求 提交一个实体参数
@Body 会将对象转换成json上传到服务器
@POST("Comments") Call<Comment> reportComment( @Body CommentBean bean);
相当于:
http://102.10.10.132/api/Comments
可以看到 post请求 ,中 @Body ,@FieldMap,@Field,看不到参数,如果你post请求中用了get请求中的注解,@QueryMap,@Query ,就会直接加入到URL上显示出来,access_token=1234123直接加入请求的后面。如果你想post请求肯定是不想别人看到你的参数,所以建议 post请求,用post请求注解。
@POST("Comments/{newsId}") Call<Comment> reportComment( @Path("newsId") String commentId, @Query("access_token") String access_token, @Body CommentBean bean);
相当于:
http://102.10.10.132/api/Comments/1?access_token=1234123
http://102.10.10.132/api/Comments/{newsId}?access_token={access_token}
rxjava2+rxandroid2+retrofit2 封装网络请求 使用,以post请求为例,进行封装
使用:
GetCarCountInput countInput=new GetCarCountInput(); countInput.setMethod("mobileapi.cart.get_list_group_by_tip");//API方法 ApiMnager.getInstance().getCarCount(countInput).subscribe( new Observer<BaseResultEntity<GetCarCount>>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(BaseResultEntity<GetCarCount> aseResultEntity) { Log.i("GetCarCountTask","onNext"); } @Override public void onError(Throwable e) { Log.i("GetCarCountTask","onError"+e.getMessage()); } @Override public void onComplete() { } });
GetCarCountInput 是用来设置自定义参数,还有共有参数,在正常的的项目中,网络请求是有共有参数的,options 这个map集合就是用来设置共有参数,还有装有自定义参数,如何简单的,传入参数呢
public interface AppService { @FormUrlEncoded @POST("api") Observable<BaseResultEntity<GetCarCount>> getCarCount( @FieldMap Map<String, String> options ); @FormUrlEncoded @POST("api") Observable<BaseResultEntity<UpdateVersion>> onUpdateVersion( @FieldMap Map<String, String> options );}
可以看到GetCarCountInput 继承了BaseInput 重写了这个方法,getData()
public class GetCarCountInput extends BaseInput { @Override public Map<String, String> getData() { Gson gson = new Gson(); Type type = new TypeToken<Map<String, String>>() { }.getType(); return gson.fromJson(gson.toJson(this), type); }}
这段代码的作用就是将这个GetCarCountInput 的属性转换为Map
Gson gson = new Gson(); Type type = new TypeToken<Map<String, String>>() { }.getType(); return gson.fromJson(gson.toJson(this), type);
可以像这样重写
package com.dk.basepack.bseaapplication.input;import com.google.gson.Gson;import com.google.gson.reflect.TypeToken;import java.lang.reflect.Type;import java.util.Map;/** * Created by Administrator on 2017/10/19. */public class UpdateVersionInput extends BaseInput { @Override public Map<String, String> getData() { Gson gson = new Gson(); Type type = new TypeToken<Map<String, String>>() { }.getType(); return gson.fromJson(gson.toJson(this), type); } private String os; public void setOs(String os) { this.os = os; } public String getOs() { return os; }}
使用:
UpdateVersionInput countInput=new UpdateVersionInput(); countInput.setMethod("mobileapi.app.version"); countInput.setOs("android"); ApiMnager.getInstance().onUpdateVersion(countInput).subscribe( new Observer<BaseResultEntity<UpdateVersion> >() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(BaseResultEntity<UpdateVersion> baseResultEntity) { } @Override public void onError(Throwable e) { Log.i("GetCarCountTask","onError"+e.getMessage()); } @Override public void onComplete() { } });
共有参数在BaseInput 已经初始化完毕,可以看到,我这里有四个共有参数
private String date;//传入时间 private String direct; private String method;//方法 private String task;
package com.dk.basepack.bseaapplication.input;import android.text.TextUtils;import java.text.SimpleDateFormat;import java.util.HashMap;import java.util.Map;import java.util.UUID;/** * Created by Administrator on 2017/10/19. */public abstract class BaseInput { private String date; private String direct; private String method; private String task; private static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss"); public String getDate() { return df.format(System.currentTimeMillis()); } public void setDate(String date) { this.date = date; } public String getDirect() { return "true"; } public void setDirect(String direct) { this.direct = direct; } public String getMethod() { return method; } public String getTask() { return getRandomString(); } public void setTask(String task) { this.task = task; } public void setMethod(String method) { this.method = method; } public HashMap<String, String> getProperties() { HashMap<String, String> map = new HashMap<String, String>(); //调用时很可能是子类对象,必须使用getMethod,而不是getDeclaredMethod来获取方法。 map.put("date",getDate()); map.put("direct", getDirect()); map.put("method", getMethod()); map.put("task", getTask()); return map; } public static String getRandomString() { UUID uuid = UUID.randomUUID(); return uuid.toString().replace("-", ""); } public abstract Map<String, String> getData(); /** * 类型转换输出,String 转 int 类型 输出 * @param inputStr * @return */ public static int onTypeOutputStringToInt(String inputStr) { if (TextUtils.isEmpty(inputStr)) { inputStr="0"; } return Integer.parseInt(inputStr); } /** * 类型转换输出 String 类型转 布尔值类型输出 * @param inputStr * @return */ public static boolean onTypeOutputStringToBoolean(String inputStr) { if (TextUtils.isEmpty(inputStr)) { inputStr="false"; } return Boolean.parseBoolean(inputStr); }}
通过上面的写法就可以将自定义参数和共有参数(每次网络请求必传的参数),通过map集合装起来
封装一个ApiMnager类,来管理自己网络请求,doSign()签名使用的
public Observable<BaseResultEntity<GetCarCount>> getCarCount(GetCarCountInput input) { return toObservable(appService.getCarCount(doSign(input))); }
package com.dk.basepack.bseaapplication.network;import com.dk.basepack.bseaapplication.input.GetCarCountInput;import com.dk.basepack.bseaapplication.input.UpdateVersionInput;import com.dk.basepack.bseaapplication.resultbean.GetCarCount;import com.dk.basepack.bseaapplication.resultbean.UpdateVersion;import io.reactivex.Observable;/** * Created by Administrator on 2017/10/11. */public class ApiMnager<T> extends BaseApiMnager{ private volatile static ApiMnager mnager=null; private final AppService appService; private ApiMnager() { appService = ApiCore.init().createService(AppService.class); } public static ApiMnager getInstance() { if (mnager == null) { synchronized (ApiMnager.class) { if (mnager == null) { mnager = new ApiMnager(); } } } return mnager; } public Observable<BaseResultEntity<GetCarCount>> getCarCount(GetCarCountInput input) { return toObservable(appService.getCarCount(doSign(input))); } public Observable<BaseResultEntity<UpdateVersion>> onUpdateVersion(UpdateVersionInput input) { return toObservable(appService.onUpdateVersion(doSign(input))); }}
package com.dk.basepack.bseaapplication.network;import android.support.annotation.NonNull;import android.text.TextUtils;import android.util.Log;import com.dk.basepack.bseaapplication.input.BaseInput;import com.dk.basepack.bseaapplication.resultbean.LogoutLogin;import com.dk.basepack.bseaapplication.util.RxBus;import com.google.gson.Gson;import com.socks.library.KLog;import java.util.HashMap;import java.util.Map;import java.util.TreeMap;import io.reactivex.Observable;import io.reactivex.android.schedulers.AndroidSchedulers;import io.reactivex.functions.Function;import io.reactivex.schedulers.Schedulers;/** * Created by Administrator on 2017/10/11. */public class BaseApiMnager { private String serviceToken=ApiCore.TOKEN; private Map<String, String> postBody; private String bodySign; protected <T> Observable<T> toObservable(Observable<T> o) { return o.subscribeOn(Schedulers.io())//网络请求在子线程,所以是在io线程,避免阻塞线程 .unsubscribeOn(Schedulers.io())//取消请求的的时候在 io 线程,避免阻塞线程 .observeOn(AndroidSchedulers.mainThread()); } /** * 进行网络请求参数签名 * @param object * @return */ public String signBody(Object object) { if(object instanceof BaseInput){ TreeMap<String, String> needSignMap = new TreeMap<>(); //解析参数,放入TreeMap排序 BaseInput input = (BaseInput) object; HashMap<String, String> map = input.getProperties(); for (String key :map.keySet()){ String value = map.get(key); needSignMap.put(key, value); } needSignMap.putAll(input.getData()); postBody=null; postBody = needSignMap; //组合待签名字符串 bodySign = ""; for (String key :postBody.keySet()){ bodySign +=key; bodySign += postBody.get(key); } //签名 bodySign = Md5.getMD5(bodySign).toUpperCase(); bodySign += serviceToken; bodySign = Md5.getMD5(bodySign).toUpperCase(); postBody.put("sign", bodySign); outputNetworkRequestUrl(); return bodySign; } return null; } /** * 输出网络请求的URL */ public void outputNetworkRequestUrl() { String url=""; int i=0; for (String key :postBody.keySet()){ i++; url+=key+"="+ postBody.get(key)+(i==postBody.size()?"":"&"); } KLog.i("Network_Request_url==>",ApiCore.BASE_URL+"api"+"?"+url); } /** * 签名异常抛出异常 * @param input */ protected Map<String, String> doSign(BaseInput input) { String signBody= signBody(input); if (signBody==null) { return null; }else { return postBody; } }}
package com.dk.basepack.bseaapplication.network;import android.util.Log;import com.dk.basepack.bseaapplication.BuildConfig;import com.google.gson.Gson;import com.google.gson.GsonBuilder;import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;import java.io.IOException;import java.util.concurrent.TimeUnit;import okhttp3.Interceptor;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;import retrofit2.Retrofit;import retrofit2.converter.gson.GsonConverterFactory;/** * Created by Administrator on 2017/10/11. */public class ApiCore { private final static Gson gson = new GsonBuilder() .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") .serializeNulls() .create(); private static final int DEFAULT_OUT_TIME = 30; public static final String BASE_URL="你自己的网络请求地址"; public static final String TOKEN="服务器的唯一标识"; private final Retrofit mRetrofit; public static ApiCore init() { return new ApiCore(); } private ApiCore() { OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.connectTimeout(DEFAULT_OUT_TIME, TimeUnit.SECONDS); //手动创建一个OkHttpClient并设置超时时间 // builder.addInterceptor(new HeadersInterceptor()); builder.addInterceptor(new ResponseInterceptor());//添加结果拦截器 if (BuildConfig.DEBUG)//debug 情况下输出日志 { builder.interceptors().add(new LoggingInterceptor()); } //RxJava2 mRetrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .client(builder.build()) .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//RxJava2 .build(); } public <T> T createService(final Class<T> clz) { return mRetrofit.create(clz); }}
请求结果返回截器
package com.dk.basepack.bseaapplication.network;import android.text.TextUtils;import com.dk.basepack.bseaapplication.resultbean.LogoutLogin;import com.dk.basepack.bseaapplication.util.RxBus;import com.socks.library.KLog;import org.json.JSONException;import org.json.JSONObject;import java.io.IOException;import okhttp3.Interceptor;import okhttp3.MediaType;import okhttp3.Request;import okhttp3.Response;import okhttp3.ResponseBody;/** * 请求结果拦截器 * * 作用: * 1.用来拦截网络请求结果,打印出来,然后处理不规范的请求结果 * 2.退出登录发送信号拦截 */public class ResponseInterceptor implements Interceptor { private String emptyString = ":\"\""; private String emptyObject = ":{}"; private String emptyArray = ":[]"; private String newChars = ":null"; @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); ResponseBody responseBody = response.body(); if (responseBody != null) { String json = responseBody.string(); KLog.i("responseJson",json); MediaType contentType = responseBody.contentType(); if (!json.contains(emptyString)) { json= handleJosnData(json); ResponseBody body = ResponseBody.create(contentType, json); return response.newBuilder().body(body).build(); } else { json= handleJosnData(json); String replace = json.replace(emptyString, newChars); String replace1 = replace.replace(emptyObject, newChars); String replace2 = replace1.replace(emptyArray, newChars); ResponseBody body = ResponseBody.create(contentType, replace2); return response.newBuilder().body(body).build(); } } return response; } /** * json 里面的data 不规范,返回的 下面这个格式,data 对应的是string ,真是搞死人,在这里做特殊处理下 * { "rsp": "fail", "res": "need_login", "data": "请重新登录", "timestamp": 1508813312 } * @param json */ private String handleJosnData(String json) { JSONObject jsonO=null; if(!TextUtils.isEmpty(json)) { try { jsonO=new JSONObject(json); } catch (JSONException e) { e.printStackTrace(); } String rsp= jsonO.optString("rsp"); String res = jsonO.optString("res"); String data = jsonO.optString("data"); if ("fail".equals(rsp)&&"need_login".equals(res))//退出登录处理 { RxBus.getInstance().post(new LogoutLogin()); } if (!TextUtils.isEmpty(data)&&!data.contains("{")&&!data.contains("}"))//如果不是用{} 包裹起来说明返回的就是一个string { jsonO.remove("data"); try { JSONObject jsondata=new JSONObject(); jsondata.put("msg",data); jsonO.put("data",jsondata); } catch (JSONException e) { e.printStackTrace(); } } KLog.i("handleJosnData",jsonO.toString()); } return jsonO.toString(); }}
服务器返回的json 标准的示例,T ,是泛型,
package com.dk.basepack.bseaapplication.network;/** * Created by Administrator on 2017/10/12. */public class BaseResultEntity<T> { private String rsp; private T data; private String res; private String timestamp; public String getRsp() { return rsp; } public void setRsp(String rsp) { this.rsp = rsp; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getRes() { return res; } public void setRes(String res) { this.res = res; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; }}
- rxjava2+rxandroid2+retrofit2 封装网络请求
- Retrofit2+Rxjava2网络请求框架的封装
- Retrofit2+RxAndroid2进行简单的网络请求
- RxJava2+Retrofit2实现网络请求和解析封装
- RxJava2+Retrofit2网络请求框架封装及使用
- 简单实现RxJava2+Okhttp+Retrofit2的网络请求框架封装
- Retrofit2+RxJava2进行网络请求
- Rxjava2和Retrofit2网络库的封装
- rxjava2+rxandroid2+retrofit2 cookie如何维护
- Android RxJava2+Retrofit2搭建网络请求框架
- RxJava2和Retrofit2实现简单网络请求
- Retrofit2+Rxjava2如何主动取消网络请求
- retrofit2+rxjava2封装解析
- 简单封装retrofit2+rxjava2
- Rxjava2+okhttp3+Retrofit2封装
- 给你一个原汁原味的Retrofit2+Rxjava2的请求封装
- 优雅地封装使用rxjava2+retrofit2发送http请求
- Android使用Retrofit2.0和RxJava2.0处理网络请求
- Grunt的一天
- 在Matlab下训练级联目标检测器
- 用eclipse启动tomcat项目默认发布地址
- CentOs6.5 x64 安装源码包MySql5.6.38教程
- OV5640开机自动配置IP编写
- rxjava2+rxandroid2+retrofit2 封装网络请求
- adb connect
- 【Python自学】03.字符串和编码(上)
- MAC系统下编译FFmpeg-Android (从编译到移植Androidstudio成功)(一)
- AndroidStudio生成签名证书SHA1值
- PHP常用功能块
- Hikaricp源码解读(3)——ConcurrentBag介绍
- 四、变量
- 赢政天下 Adobe CC 2018 大师版 v8.0#2 特别版