Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android為什麼要使用MVP?使用RXJAVA完成簡單的MVP架構之旅

Android為什麼要使用MVP?使用RXJAVA完成簡單的MVP架構之旅

編輯:關於Android編程

什麼是MVP?為什麼要使用MVP設計模式?

因為要讓別人看得懂我們的代碼,使代碼更利於維護,簡單講就是模塊化,使各個包下的類各在其位,各司其職。比如怎樣請求數據和它被用來干什麼是沒有關系的,所以要分離,這樣做的好處是:一,大量減少了UI層業務邏輯的代碼量。二,即使UI界面變了,也不會影響到業務層的代碼。使兩者充分解耦,也就達到了我們的目的了。

下圖是我理解的MVP的職責圖示:

\

對於它的解釋是 model把封裝好的數據通過接口回調給Presenter ,View 負責UI層例如Dialog,Empty data, no network,Datas done的顯示,讓Presenter持有View的引用,當model和view在presenter中匯合時,再由presenter去決定View 顯示的時機,也就是presenter的調度。這樣,model無需關心數據的流向,view不用關系數據的來源,activity也不用處理兩者之間的復雜關系,只需要在需要的地方設置“Adapter”就行了,更多的事交給Presenter去處理。

光說不練假把式,來嘗試一個簡單的功能

下面是一個采用MVP模式設計的功能非常的APP的效果圖,就是從網絡獲取一段字符串,在開始獲取的時候Toast:開始加載數據,在成功獲取到數據後,直接顯示到ListView,怎麼樣,簡單得不能再簡單了!

\

根據上面的內容,在寫代碼前,要先‘’分清職責‘’

首先,Model負責訪問數據接口,並把數據回調給Presenter 所以IHomeListLoad肯定會有loadData()這個方法去加載數據。所以它的代碼應該是這樣的


public interface IHomeListLoad { //加載數據 void loadData(Context context,ILoadDataComplete iLoadDataComplete); //數據加載後的回調 interface ILoadDataComplete{ void dataComing(List datas); void dataError(Exception e); void onStart(); } } 
在這裡我做了兩件事,一是加載數據,二是預制了ILoadDataComlete數據回調接口方便獲取數據。現在我不僅獲取了數據,並且還知道數據是什麼時候開始加載的,什麼時候結束的,加載是否出錯。。
然後View上場了,分析上面的Model層的需求過後,我知道View需要去展示數據是什麼時候開始加載的,什麼時候結束的,加載是否出錯。所以IMainView的代碼應該是這樣的

public interface IMainView { void showLoading(); void loadFinish(List datas); void loadError(Exception e); }
前面的Model雖然把數據請求下來了,但是它並不知道該把數據交給誰,因為它只負責去請求數據。View也並不知道該什麼時候去呈現各種定義好的視圖效果。這時候Presenter出場了,通通交給它去調度,如圖所示是它的一般調度關系。
\

	這就是我們所說的中間層的作用,它帶來好處是即使model和view發生變化,只要他們的調度關系還在,我就不會去改寫presenter的代碼,使其充分解耦。要知道,presenter可占了很大一部分代碼量O(∩_∩)O~。

讓我們來實現它

前面都是通過接口編寫了抽象方法,想好了操作步驟,接下來就去編寫它的實現類,但在這之前我們必須做一些准備工作,以提高代碼的復用性

一、封裝統一的HTTP RESOULT類 BaseHttpResponse

 

public class BaseHttpResponse {
    private String error_code;
    private String reason;
    private T result;//不確定的數據類型
    public String getError_code() {
        return error_code;
    }
    public void setError_code(String error_code) {
        this.error_code = error_code;
    }
    public String getReason() {
        return reason;
    }
    public void setReason(String reason) {
        this.reason = reason;
    }
    public T getResult() {
        return result;
    }
    public void setResult(T result) {
        this.result = result;
    }
}

 

二、繼承RXJAVA中Func1()類,並重寫Call()方法增加,對返回的數據重新整理。在BaseHttpResponse中,我們真正關心的數據只有當error_code=0時result的數據(這裡我規定error_code=0時,且result不為null表示獲取成功),所以如果我們不進行預處理的話使用RXJAVA時的map函數將是這樣處理數據的

 

 .map(new Func1>, List>() {
                    @Override
                    public List call(BaseHttpResponse> baseHttpResponse) {
                        if("0".equals(baseHttpResponse.getError_code()))
                        {
                            if(baseHttpResponse.getResult() != null)
                            {
                                return baseHttpResponse.getResult();
                            }
                            else
                            {
                                //自定義異常類
                                throw new DataResponseException("數據為空");//DataResponseException
                            }

                        }
                        else
                        {
                            throw new DataResponseException("請求發生錯誤");
                        }
                    }
                })

 

(PS:此過程中拋出的異常是會回調到OnError函數中的,源碼中的注釋是@parame the exception encountered by the Observable)實在太麻煩,所以干脆重寫一下Func1()的方法算了!

 

public class BetterFunc implements Func1,T> {

    @Override
    public T call(BaseHttpResponse tBaseHttpResponse) {
        String error_code = tBaseHttpResponse.getError_code();
        String reason = tBaseHttpResponse.getReason();
        T result = tBaseHttpResponse.getResult();
        if("0".equals(error_code)){
            if(result !=null){
                return result; 
            }else {
                throw new DataResponseException("數據為空");
            }
            
        } else {
            throw new DataResponseException(reason);
        }
    }
}
前面的map函數就可以這麼寫

 

 

.map(new BetterFunc>())
我靠( ‵o′)凸,簡直不要太嗨!准備工作完成了接下來是真正的實現類

 

①Model implement

 

public class IHomeListLoadIml implements IHomeListLoad {
    @Override
    public void loadData(Context context, final ILoadDataComplete iLoadDataComplete) {
        RetrofitUtils.createApi(context, AnswerService.class)
                .getData("CN", "7568f1905fde469e90009614e8c167b0")
                .subscribeOn(Schedulers.io())
                .map(new BertterFunc>())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber>() {
                    @Override
                    public void onCompleted() {}
                    @Override
                    public void onStart() {
                        super.onStart();
                        iLoadDataComplete.onStart();
                    }
                    @Override
                    public void onError(Throwable e) {
                        iLoadDataComplete.dataError((Exception) e);
                    }

                    @Override
                    public void onNext(List xxxes) {
                        iLoadDataComplete.dataComing(xxxes);
                    }
                });
    }
}
②Viewimplement(肯定是Activity啊)

 

 

public class MainActivity extends AppCompatActivity implements IMainView {

    private ListView mListView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView= (ListView) findViewById(R.id.mList);
        //IMainView ---》presenter
        MainPresenter presenter = new MainPresenter(this,this);
        presenter.getData();
    }

    @Override
    public void showLoading() {
        Toast.makeText(this,"開始加載數據",Toast.LENGTH_LONG).show();
    }

    @Override
    public void loadFinish(List datas) {
        mListView.setAdapter(new HomeListAdapter(this,datas));
    }

    @Override
    public void loadError(Exception e) {
        Toast.makeText(this,"數據加載出錯",Toast.LENGTH_LONG).show();
    }
}

③Presenter實現調度,根據上面圖示的關系思路已經很清晰了

 

 

public class MainPresenter {
    private IMainView iMainView;
    private Context context;
    private IHomeListLoad iHomeListLoad = new IHomeListLoadIml();
    public MainPresenter(Context context,IMainView iMainView) {
        this.iMainView = iMainView;
        this.context = context;
    }
    //請求數據
    public void getData(){
        iHomeListLoad.loadData(context,new IHomeListLoad.ILoadDataComplete() {
            @Override
            public void dataComing(List datas) {
                //datas--->activity
                iMainView.loadFinish(datas);
            }
            @Override
            public void dataError(Exception e) {
                //view --->error
                iMainView.loadError(e);
            }
            @Override
            public void onStart() {
                //view --->dialog
                iMainView.showLoading();
            }
        });
    }
}

好了,到這裡一個簡單的MVP結構的功能就實現了。估計有些哥們都得瘋了了:我就一個ListView弄得這麼麻煩!是的,很麻煩這也是MVP架構的美中不足,增加了很多的類,但和它帶來的收益來看,確實可行的。況且有些時候學東西不一定是要把它的效果實現吧,還要感受其中的奧妙和樂趣!

 

哦,對了本文涉及到RXJAVA和Retrofit的用法也會抽時間整理一下留作筆記,代碼等我完善後也將隨後上傳至GITHUB並補上鏈接。個人能力受限,還請各位前輩不吝賜教!完

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved