Android入门学习——Retrofit+MVP模式学习

Android入门学习——Retrofit+MVP模式学习简单使用

最近闲着无聊的时候在网上随意闲逛的时候,Retrofit、RxJava、RxAndroid这几个词,顺带着okHttp、MVP出现的频率蛮高。这些东西都不会,就赶紧学习一下。RxJava和RxAndroid暂时先放弃。先学习Retrofit+MVP模式进行开发。


首先强烈感谢鸿洋大神和CSDN的另一位博主还不走A。Retrofit、okHttp和MVP,鸿洋大神的有四篇博客来讲解,鸿洋大神就不用再多说,篇篇博客都是高质量。还不走A的 Android mvp 架构的自述,这篇MVP的入门写的非常好,Demo很简单相对更容易理解。本篇博客就是在看了鸿洋大神和还不走A的博客后写的。如果出现啥错误,请一定在留言中指出。


一.简单介绍

网上看到有人比较,还给出了比较结果表格,说Retrofit+okhttp是目前Android最快的网络请求,Volley、xUtils3.0我也尝试了最简单的使用,感觉也都非常的快,不考究这个。Retrofit结合MVP模式能写出更简洁便于维护的代码。Retrofit目前已经是2.0版本。1.0版本的时候使用的时候还需要再添加okHttp以及内部依赖okio,到了2.0时,在Android Studio中使用只需要引入Retrofit一个库就可以。Retrofit下载文件并不算很友好,下载文件可以直接使用okHttp或者使用封装好的okHttp的工具库也可以。鸿洋大神做了一个很好使用很方便的封装。具体可以查看鸿洋大神的博客。

MVP(Model View Presenter)架构

  • Modle 业务逻辑和实体模型,进行一些数据的操作处理
  • View 对应于Activity、Fragment ,负责View的绘制以及用户交互
  • Presenter View和Model的中间“桥梁”,负责View和Model间的交互

这里引用一下鸿洋大神博客中的图

单单看着几行字也蛮好理解,看了别人写的Demo第一感觉就是原来几行代码搞定的事,现在需要写好几个类出来。理解并不算难理解,就是看过后自己还是啥都不会写。先照着葫芦画个瓢。以后慢慢多理解了。

二.具体Demo

这个Demo数据来源接口是易源中找的一个美女图库,使用还是相当简单方便的,看下文档应该就会了。Demo也特别简单,就是用RecyclerView来展示请求到的数据。效果图如下:

代码

由于对各种设计模式都不了解,这里也是照着葫芦画个瓢,包结构不合理或者错误希望指出。下面是整体的工程目录结构:

bean包就是请求返回的对象,Configs包中就是请求的url以及请求所需要键值对的名字。utils中就是RecyclerView的Adpater和ViewHolder的简单封装。若想学习,可以查看鸿洋大神的RecyclerViewAdapter的封装。


bean包下的DataBean代码这个DataBean没啥说的

public class DataBean {

    private int showapi_res_code;
    private String showapi_res_error;

    private ShowapiResBodyBean showapi_res_body;

    public int getShowapi_res_code() {
        return showapi_res_code;
    }

    public void setShowapi_res_code(int showapi_res_code) {
        this.showapi_res_code = showapi_res_code;
    }

    public String getShowapi_res_error() {
        return showapi_res_error;
    }

    public void setShowapi_res_error(String showapi_res_error) {
        this.showapi_res_error = showapi_res_error;
    }

    public ShowapiResBodyBean getShowapi_res_body() {
        return showapi_res_body;
    }

    public void setShowapi_res_body(ShowapiResBodyBean showapi_res_body) {
        this.showapi_res_body = showapi_res_body;
    }

    public static class ShowapiResBodyBean {
        private int code;
        private String msg;        

        private List<NewslistBean> newslist;

        public int getCode() {
            return code;
        }

        public void setCode(int code) {
            this.code = code;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        public List<NewslistBean> getNewslist() {
            return newslist;
        }

        public void setNewslist(List<NewslistBean> newslist) {
            this.newslist = newslist;
        }

        public static class NewslistBean {
            private String ctime;
            private String description;
            private String picUrl;
            private String title;
            private String url;

            public String getCtime() {
                return ctime;
            }

            public void setCtime(String ctime) {
                this.ctime = ctime;
            }

            public String getDescription() {
                return description;
            }

            public void setDescription(String description) {
                this.description = description;
            }

            public String getPicUrl() {
                return picUrl;
            }

            public void setPicUrl(String picUrl) {
                this.picUrl = picUrl;
            }

            public String getTitle() {
                return title;
            }

            public void setTitle(String title) {
                this.title = title;
            }

            public String getUrl() {
                return url;
            }

            public void setUrl(String url) {
                this.url = url;
            }
        }
    }
}

configs包下的AppConfigs类这个类就是请求数据的url,请求头键值对。这里需要注意的是URL_DATA这个网址。下面会说到。

public class AppConfigs {
    public static final String URL_DATA = "http://route.showapi.com/197-1/";
    public static final String APPID = "18107";
    public static final String APPID_NAME = "showapi_appid";

    public static final String SECRECT = "9a973b7a357e4475b150af31da58be1e";
    public static final String SECRECT_NAME = "showapi_sign";

    public static final String NUM_NAME = "num";
    public static final String NUM = "40";

    public static final String PAGE_NAME = "page";
}
Model

这里Model主要就是负责数据的请求,从易源拿到请求的数据。使用的网络库就是Retrofit。先来看model包里的各个类。

onDataResponseListener接口

拿到请求数据结果的接口回调,有成功的方法和失败的方法。


public interface onDataResponseListener {
     void onSuccess(DataBean dataBean);
     void onFailed(String string);
}
RequestBiz接口

这个接口是提供一个请求数据的方法,并以onDataResponseListener为参数


public interface RequestBiz {
    void onRequest(onDataResponseListener onDataResponseListener);
}
RequestModel类

这个类是Model中最重要的一个类,负责使用Retrofit来进行请求数据。RequestModel实现一个RequestBiz这个接口。实现这个RequestBiz接口就必须实现其中的方法onRequest(onDataResponseListener onDataResponseListener)这个方法。参数中的接口就是model包中的另外一个接口onDataResponseListener,负责拿到请求的数据后进行接口回调。这个onDataResponseListener接口有两个方法,分别在使用Retrofit中CallBack中的onResponse和onFailure中调用,分别负责各自的任务。并在这个类中再创建一个cancelRequest()方法,用来取消网络请求。


public class RequestModel implements RequestBiz{
    private Call<DataBean> call;

    @Override
    public void onRequest(final onDataResponseListener onDataResponseListener) {
        Retrofit retrofit = new Retrofit.Builder().baseUrl(AppConfigs.URL_DATA).addConverterFactory(GsonConverterFactory.create()).build();
        GetDataService getDataService = retrofit.create(GetDataService.class);

        Map<String,String> map  = new HashMap<>();
        map.put(AppConfigs.APPID_NAME,AppConfigs.APPID);
        map.put(AppConfigs.SECRECT_NAME,AppConfigs.SECRECT);
        map.put(AppConfigs.PAGE_NAME,""+1);
        map.put(AppConfigs.NUM_NAME,AppConfigs.NUM);

        call = getDataService.getBeansData(map);
        call.enqueue(new Callback<DataBean>() {
            @Override
            public void onResponse(Call<DataBean> call, Response<DataBean> response) {
                DataBean dataBean = response.body();
                if (dataBean != null){
                    onDataResponseListener.onSuccess(dataBean);
                }
            }

            @Override
            public void onFailure(Call<DataBean> call, Throwable t) {
                onDataResponseListener.onFailed(t.getMessage());
            }
        });
    }

    public void  cancelRequest(){
        call.cancel();
    }
}
Retrofit最简单基础的Post请求用法

在RequestModel中使用到了Retrofit的Post方法。我个人的流程是:
1. 首先确定请求数据的bean类
2. 创建Retrofit所必须的接口(retrofitservice包下的GetDataService接口),接口内会使用注解
3. 建立Retrofit对象,进行数据的请求

第一步不再介绍,就是建立请求返回的解析数据的对象。
第二步,建立Retrofit所必须的接口,如下

public interface GetDataService {
    @FormUrlEncoded
    @POST(AppConfigs.URL_DATA)
    Call<DataBean> getBeansData(@FieldMap Map<String,String> map);
}

接口这里的方法必须要有返回值,Call

Retrofit retrofit = new Retrofit.Builder().baseUrl(AppConfigs.URL_DATA).addConverterFactory(GsonConverterFactory.create()).build();

这里需要注意的是,指定的baseUrl也就是AppConfigs.URL_DATA必须以/结尾。而且在GetDataService这个接口中,@POST或者@GET括号内是一个完整的url链接的话,这个baseUrl便无效。在@GET进行get请求的时候,@GET后跟的value和baseUrl会拼接成一个完整的url。
我这里指定了Converter.Factory为GsonConverterFactory。这里也可以使用其他的,我选择了使用GSON来解析对象。这里使用GsonConverterFactory需要在工程中加入converter-gson这个库。


GetDataService getDataService = retrofit.create(GetDataService.class);

利用反射创建GetDataService对象实例。至于如何具体创建的过程,暂时没有能力分析源码。以后有能力再进行分析。


Map<String,String> map  = new HashMap<>();
map.put(AppConfigs.APPID_NAME,AppConfigs.APPID);
map.put(AppConfigs.SECRECT_NAME,AppConfigs.SECRECT);
map.put(AppConfigs.PAGE_NAME,""+1);
map.put(AppConfigs.NUM_NAME,AppConfigs.NUM);

这里就是把请求头键值对放进map集合


call = getDataService.getBeansData(map);

建立Call的对象


call.enqueue()就是进行异步请求。call.execute()就是进行同步请求。


public void  cancelRequest(){
        call.cancel();
    }

这个方法就是取消网络请求

Presenter

Presenter就是将Model和View联系起来。

MVPPresenter类
public class MVPPresenter {
      private RequestModel requestModel;
      private MVPResponseView mvpResponseView;
      public  MVPPresenter (MVPResponseView mvpResponseView){
            this.mvpResponseView = mvpResponseView;
            requestModel = new RequestModel();
      }

      public void getListData(){
            requestModel.onRequest(new onDataResponseListener() {
                  @Override
                  public void onSuccess(DataBean dataBean) {
                        mvpResponseView.setListData(dataBean);
                  }

                  @Override
                  public void onFailed(String string) {
                        mvpResponseView.showFailedMessage(string);
                  }
            });
      }

      public void cancel(){
            requestModel.cancelRequest();
      }
}

说Presenter是Model和View的桥梁,所以在MVPPresenter这个类中首先拿到Model中的RequestModel和View中的MVPResponseView对象。其中MVPResponseView对象通过构造方法传参的形式创建。而RequestModel则直接new出一个对象。
创建两个方法getListData()和cancel()。分别用来拿到请求数据和取消网络请求。

View

这里首先就遇到一个问题,RecyclerView的Adapter是属于View层还是Presenter层,最后给放在了Presenter层。若是这里不合理欢迎指出。
View层就是对应的Activity以及Fragment。

MVPResponseView接口
public interface MVPResponseView {
    void setListData(DataBean dataBean);
    void showFailedMessage(String message);
}

这个接口提供两个方法,一个用来处理拿到的请求数据,一个处理请求错误

MainActivity
public class MainActivity extends AppCompatActivity implements MVPResponseView {
    private MVPPresenter mvpPresenter;
    private RvAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mvpPresenter = new MVPPresenter(this);

        initView();
    }

    private void initView() {
        RecyclerView rv = (RecyclerView) findViewById(R.id.rv_main_activity);
        GridLayoutManager glm = new GridLayoutManager(MainActivity.this,2);
        rv.setLayoutManager(glm);

        RecyclerItemDecoration recyclerItemDecration = new RecyclerItemDecoration(16);
        rv.addItemDecoration(recyclerItemDecration);
        adapter = new RvAdapter(rv,R.layout.item_img_fragment_layout);
        rv.setAdapter(adapter);

        //从网路请求数据
        mvpPresenter.getListData();
    }

    @Override
    public void setListData(DataBean dataBean) {
        List<DataBean.ShowapiResBodyBean.NewslistBean> dataList = new ArrayList<>();
        DataBean.ShowapiResBodyBean showapiResBodyBean = dataBean.getShowapi_res_body();
        if (showapiResBodyBean != null){
            dataList.addAll(showapiResBodyBean.getNewslist());
            adapter.setDataList(dataList);
        }
    }

    @Override
    public void showFailedMessage(String message) {
        Toast.makeText(MainActivity.this,message,Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mvpPresenter.cancel();
    }
}

在MainActivity中进行控件的初始化后,首先考虑拿到Presenter对象。这样Model中拿到的数据就可以通过Presenter在View中使用。
MainActicity实现MVPResponseView这个接口。利用接口回调来处理拿到的数据。
通过MainActivity的代码可以看出,网络请求就使用到了一行代码mvpPresenter.getListData() 。这样以来,MainActivity中的代码基本就是初始化控件代码和View中接口回调的代码。大大减少了Activity中的代码。

最后

使用MVP模式确实好处不少。大大提高了代码的维护性。以前曾经出现过一个Activity600多行代码的情况。使用MVP模式确实大大减少这个情况的发生。写完这篇博客对MVP模式也开始有了一点点了解。但对于Adapter和ViewHolder还不知道怎么更好的处理,还需要发大功夫继续学习。
源码有任何错误感谢留言指出

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值