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;    }}
原创粉丝点击