MVP架构---网络框架切换

项目开发的过程中,随着版本不断迭代,可能要整体去换某一个框架,比如网络框架。常用的网络框架有retrofit、okhttp、okgo、RxUtls、volley,项目开发过程中,可能以前用的volley,现在我们要全部改成retrofit或者okhttp,用一行代码切换框架。以前的做法是每个代码用到这些框架的地方都要改,如果代码量很大,是一件非常痛苦的事情。不管是网络数据库还是图片加载,其实都是同一个思路,接下来会以网络请求为例。

这里我们引入一个隔离层的概念,这个隔离层的作用是帮助我们选择框架。可以用动态代理来实现

image.png

设想一个场景,客户要去买房,一般会找房产公司,房产公司会交代业务员去协助,那么业务员会去查找房源,这些房源是业主放出来的。最后,房产公司具备了卖房的能力。实际上是由业务员去完成这个卖房的功能的,他要跟业主进行交流需求。那么这个业务员,会持有这个业主的引用。那么右边用红色框圈起来的其实就是个动态代理。跟着上一篇的demo,新建一个library(专门干网络加载的事情)

为了方便,首先我们新建一个library,然后让MVP的工程依赖这个library,在library里面添加网络库和Gson解析库,因为真正去实现网络操作是在library里面。我们现在添加三种不同的网络请求库
implementation 'com.android.volley:volley:1.1.0'
implementation 'com.google.code.gson:gson:2.2.4'
implementation 'com.squareup.okhttp3:okhttp:3.4.1'
implementation 'org.xutils:xutils:3.3.36'

  • 新建一个IHttpProcessor(相当于房产公司)主要进行网络操作
/**
 * 房产公司
 */
public interface IHttpProcessor {
    /**
     * 网络操作  get post del update put select.......
     */
    void post(String url, Map<String, Object> params, ICallback callback);
}
  • 新建ICallBack顶层接口,IHttpProcessor接口里面的方法会用到,回调数据用于解析
/**
 * 顶层回调接口(json,xml,....)
 *
 */
public interface ICallback {
    void onSuccess(String result);
    void onFailure(String e);
}
  • 新建HttpCallback,主要是用户接收到json版本的数据后,解析成对应的对象
    这里需要注意一下,Result你就当成泛型吧
    请求成功后会给你返回result的String类型数据,我们需要把这个String数据转化成所对应的对象。
    Gson解析需要一个Class文件clz, Result objResult=(Result)gson.fromJson(result,clz);
    可以用analysisClassInfo得到参数化类型,通过反射拿到所需要的对象类型
    这样我们在HttpCallback<Result>这个类中,到时候传什么类型,就会回传给我什么类型的数据
/**
 * 回调接口的json版本的实现类
 * 用于把网络返回的json字符串转让换成对象(Result就是用户接收数据的类型)
 * //ResponceData就是Result
 */
public abstract class HttpCallback<Result> implements ICallback {
    @Override
    public void onSuccess(String result) {//result就是网络回来的数据
        //result把转换成用户需要的对象
        Gson gson=new Gson();
        //需要得到用户输入的对象对应的字节码是什么样的
        //得到用户接收数据的对象对应的class
        Class<?> clz=analysisClassInfo(this);

        Result objResult=(Result)gson.fromJson(result,clz);

        //把已经转好的对象,交给用户
        onSuccess(objResult);
    }

    public abstract void onSuccess(Result result);

    private Class<?> analysisClassInfo(Object object) {
        //getGenericSuperclass可以得到包含原始类型,参数化类型,数组,类型变量,基本数据
        Type genType=object.getClass().getGenericSuperclass();
        //获取参数化类型
        Type[] params=((ParameterizedType)genType).getActualTypeArguments();
        return (Class<?>)params[0];
    }

    @Override
    public void onFailure(String e) {
    }
}
  • 最后我们新建一个HttpHelper,相当于代理类(业务员),去执行房产公司的需求
    里面有个post请求,通过优化,以后get和post都可以通用。
/**
 * 代理类(业务员)
 */
public class HttpHelper implements IHttpProcessor{

    //单例
    private static HttpHelper instance;
    public static HttpHelper obtain(){
        synchronized (HttpHelper.class){
            if(instance==null){
                instance=new HttpHelper();
            }
        }
        return instance;
    }
    private HttpHelper(){}

    private static IHttpProcessor mIHttpProcessor;
    //定义一个API,用来设置代码的接口(业务员找到一个对应的有房的人)
    public static void init(IHttpProcessor iHttpProcessor){
        mIHttpProcessor=iHttpProcessor;
    }



    @Override
    public void post(String url, Map<String, Object> params, ICallback callback) {
        //http://www.aaa.bbb/index
        //user=jett&pwd=123
        //http://www.aaa.bbb/index?&user=jett&pwd=123
        String finalUrl=appendParams(url,params);
        mIHttpProcessor.post(finalUrl,params,callback);
    }

    public static String appendParams(String url, Map<String,Object> params) {
        if(params==null || params.isEmpty()){
            return url;
        }
        StringBuilder urlBuilder=new StringBuilder(url);
        if(urlBuilder.indexOf("?")<=0){
            urlBuilder.append("?");
        }else{
            if(!urlBuilder.toString().endsWith("?")){
                urlBuilder.append("&");
            }
        }
        for(Map.Entry<String,Object> entry:params.entrySet()){
            urlBuilder.append("&"+entry.getKey())
                    .append("=")
                    .append(encode(entry.getValue().toString()));
        }
        return urlBuilder.toString();
    }
    private static String encode(String str){
        try {
            return URLEncoder.encode(str,"utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }
}

这样通过上面四个类,我们的一个代理层的网络框架就实现了。那么我们新建几个具体的网络请求实现层,比如okhttp,Vollry,Xutls,注意了,这里这三个是我们的备选的网络请求,我们还可以写很多。

okhttp(具体实现层)
public class OkHttpProcessor implements IHttpProcessor{
    private OkHttpClient mOkHttpClient;
    private Handler myHandler;

    public OkHttpProcessor(){
        mOkHttpClient=new OkHttpClient();
        myHandler=new Handler();
    }

    private RequestBody appendBody(Map<String,Object> params){
        FormBody.Builder body=new FormBody.Builder();
        if(params==null || params.isEmpty()){
            return body.build();
        }
        for(Map.Entry<String,Object> entry:params.entrySet()){
            body.add(entry.getKey(),entry.getValue().toString());
        }
        return body.build();
    }

    @Override
    public void post(String url, Map<String, Object> params,final ICallback callback) {
        RequestBody requestBody=appendBody(params);
        Request request=new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
               final String result=response.body().string();
               if(response.isSuccessful()){
                   myHandler.post(new Runnable() {
                       @Override
                       public void run() {
                           callback.onSuccess(result);
                       }
                   });
               }

            }
        });
    }
}
volley(具体实现层)
public class VolleyProcessor implements IHttpProcessor{

    private static RequestQueue mQueue=null;

    public VolleyProcessor(Context context){
        mQueue= Volley.newRequestQueue(context);
    }

    @Override
    public void post(String url, Map<String, Object> params,final ICallback callback) {
        StringRequest stringRequest=new StringRequest(
                Request.Method.POST,
                url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        callback.onSuccess(response);

                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {

            }
        }
        );
        mQueue.add(stringRequest);
    }
}
XUtilsProcessor(具体实现层)
public class XUtilsProcessor implements IHttpProcessor{
    public XUtilsProcessor(Application app){
        x.Ext.init(app);
    }
    @Override
    public void post(String url, Map<String, Object> params,final ICallback callback) {
        RequestParams requestParams=new RequestParams(url);
        x.http().post(requestParams, new Callback.CommonCallback<String>() {
            @Override
            public void onSuccess(String result) {
                callback.onSuccess(result);
            }

            @Override
            public void onError(Throwable ex, boolean isOnCallback) {

            }

            @Override
            public void onCancelled(CancelledException cex) {

            }

            @Override
            public void onFinished() {

            }
        });
    }
}

以上提供了三个网络请求的预选方案,那么我们怎在APP代码里面使用呢?
首先在Application里面选定好一个网络框架。需要哪个就打开哪个,就一行代码,这里初始化后就能拿到一个实例对象。全局实例化后后续方便直接调用。

public class MyApplication extends Application{

    @Override
    public void onCreate(){
        super.onCreate();
        //进行框架的选择
        HttpHelper.init(new VolleyProcessor(this));
//        HttpHelper.init(new OkHttpProcessor());
//        HttpHelper.init(new XUtilsProcessor(this));
        /*HttpHelper.init(new RetrofitProcessor());*/
    }
}

接着我们在真正的model层去利用这个动态代理进行真正的网络请求。
假设我们要去加载这个URL的数据
https://v.juhe.cn/historyWeather/citys?&province_id=2&key=bb52107206585ab074f5e59a8c73875b

image.png

新建一个RespnceData类去进行gsonformat

public class ResponceData{

    /**
     * resultcode : 112
     * reason : 当前可请求的次数不足
     * result : null
     * error_code : 10012
     */

    private String resultcode;
    private String reason;
    private Object result;
    private int error_code;

    public String getResultcode(){
        return resultcode;
    }

    public void setResultcode(String resultcode){
        this.resultcode = resultcode;
    }

    public String getReason(){
        return reason;
    }

    public void setReason(String reason){
        this.reason = reason;
    }

    public Object getResult(){
        return result;
    }

    public void setResult(Object result){
        this.result = result;
    }

    public int getError_code(){
        return error_code;
    }

    public void setError_code(int error_code){
        this.error_code = error_code;
    }
}

现在我们在GirlModel里面去进行真正的网络请求

image.png
image.png

证明真正的进行网络加载了,这里进行网络请求的代码不用变,如果想用不同的网络框架,可以在Application进行选择。如果未来某一天,有一个X类型网络请求框架,我们可以在library里面新建一个叫XProcessor的类,然后把具体的请求实现写进去,在Application里面初始化选择X,而其他代码不需要变化。这就是具体讲了一个使用代理类进行网络请求框架进行变化的一个思路,比如图片的加载等等是一样的道理。

链接:https://pan.baidu.com/s/1It-bVbf_OjhbSuHc8MCCqA
提取码:b71r

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容