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還不知道怎麼更好的處理,還需要發大功夫繼續學習。
源碼有任何錯誤感謝留言指出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章