一步一步帶你認識MVP+Retrofit+Rxjava並封裝(一)

一步一步帶你認識MVP+Retrofit+Rxjava並封裝(一)

一步一步帶你認識MVP+Retrofit+Rxjava並封裝(二)

序言:這原本是LZ一直想寫的一個系列的文章(哎呀,說的好像本身挺牛逼似的>_<)當下最流行的設計模式之一的MVP再配上當下最流行的網絡請求框架之一的Retrofit+Rxjava(這裏我也不引起戰爭了,PHP是世界上最好的語言grin: :grin: :grin: ),相信大部分人看簡書博客的時候都會常常看到高仿某某APP(基於MVP+Retrofit+Rxjava)類的文章,反正LZ是常常看到,好了,扯淡就扯到這裏,下面開始咱們的正題:

一、首先咱們來很不情願的簡單介紹一下MVP這個東東,爲什麼說不情願,首先,這些概念性的東西我本身都有點看不下去,其次網上講這個的東西實在是太多了。官方的解釋爲經典MVC模式的演化版,這裏咱們也不詳細講啥是MVC了,就是一種古老而又神奇的模式。講一個很簡單的栗子:

有一家早餐店,他們家賣包子、饅頭、油條、粥等等這些東西,他們須要用到最重要的東西是麪粉(原材料:大米),每次早餐店老闆都從一家麪粉店(大米店)訂購,而這些大米都是麪粉店老闆去農民伯伯那裏收購的。在這個小例子當中,農民伯伯的任務就是生產出大米,這其實就至關於MVP模式中的Model,大米就是實體,生產大米就是業務的邏輯;麪粉店老闆負責收購大米而後加工成麪粉,這其實就至關於MVP模式中的Presenter,負責完成農民伯伯和早餐店老闆這兩邊的間接交易;最後早餐店老闆的任務就是負責將麪粉作成各類各樣的早餐供你們享用,這其實就至關於MVP模式中的View,負責將麪粉作成各類各樣的食物呈如今你們眼前。好了,相信經過這個栗子你們應該對MVP模式有必定的瞭解了,下面來看一張圖:php

盜用洋神的圖片
盜用洋神的圖片

二、相信用過MVP的同志們都有體會,每一個人對於MVP模式的理解都不同,這樣致使寫出來的代碼也都風格迥異,可是思想都是同樣的(即下降ModelView之間的耦合度,使得代碼變的更清晰),因此即將學習MVP的小夥伴們看博客的時候不要驚慌,由於大家會看到各類的代碼,下面咱們一塊兒來看一下LZ寫的(寫的很差的地方歡迎指正):

(1)、首先咱們來看一下Model,這部分我理解的就是數據得到的地方,換句話說就是進行網絡請求的地方(或者本地數據的獲取),這裏我寫了一個Base類,將全部的Model請求數據相同的部分都放到了一塊兒:java

public class BaseModel extends BaseRetrofit {

    private static final String TAG = "BaseModel";

    protected CygApi mServletApi;   //全部的註解接口

    protected Map<String, String> mParams = new HashMap<>();

    public BaseModel() {
        super();
        mServletApi = mRetrofit.create(CygApi.class);
    }

    //獲取公共參數
    @Override
    protected Map<String, String> getCommonMap() {
        Map<String, String> commonMap = new HashMap<>();
        commonMap.put("user_id", String.valueOf(UserDao.getInstance().getUserId()));
        commonMap.put("token", String.valueOf(UserDao.getInstance().getToken()));
        return commonMap;
    }

    //添加一個參數
    protected void addParams(String key, String value) {
        if (TextUtils.isEmpty(key)) {
            Log.e(TAG, "the key is null");
            return;
        }
        mParams.put(key, value);
    }

    //添加多個參數
    protected void addParams(Map<String, String> params) {
        if (null == params) {
            Log.e(TAG, "the map is null");
            return;
        }
        mParams.putAll(params);
    }
}複製代碼

這裏網絡請求用的是Retrofit+RxJava,這一部分我打算放到下一篇再講,在這個Base類裏面主要添加了公共參數和添加普通參數。來看看一個登陸的Modelmysql

public class LoginModel extends BaseModel {

    public static LoginModel getInstance() {
        return getPresent(LoginModel.class);
    }

    public void execute(String phone, String password, Observer<User>     observer) {
        addParams("username", phone);
        addParams("password", password);
        Observable observable = mServletApi.getUserInfo(mParams).map(new HttpFunction());
        toSubscribe(observable, observer);
    }
}複製代碼

(2)、這裏我是用的單例模式(其實我也不知道這樣寫單例會不會有錯,用類去實例化一個對象>_<我以爲沒啥問題,哈哈哈),而後execute方法就是進行網絡請求了,在Presenter中調用這個方法就好了。而後咱們來看一下BasePresenterandroid

public class BasePresenter<VIEW> {

    private WeakReference<VIEW> mViews;

    protected void attachView(VIEW view) {
        mViews = new WeakReference<VIEW>(view);
    }

    protected VIEW getView() {
        return isViewAttached() ? mViews.get() : null;
    }

    private boolean isViewAttached() {
        return null != mViews && null != mViews.get();
    }

    protected void detachView() {
        if (null != mViews) {
            mViews.clear();
            mViews = null;
        }
    }
}複製代碼

BasePresenter裏面我只是關注了View,按照MVP模式的理解,咱們應該在這個裏面同時關注ViewModel,確實,不少demo都是這樣乾的,可是LZ前面是用的單例來寫Model,因此在BasePresenter裏面就暫時先關注View,還有一點須要說明的是,這裏對View使用的弱引用,咱們都知道View一般來講都是很大隻的存在,爲了防止內存泄漏,使用弱引用來及時釋放內存。來看看一個登陸的LoginPresentergit

public class LoginPresenter extends BasePresenter<LoginView<User>> {

    public LoginPresenter(LoginView<User> loginView) {
        attachView(loginView);
    }


    public void getUserInfo(BaseImpl baseImpl) {
        LoginModel.getInstance().execute(getView().getUserName(), getView().getPassword(), new CygBaseObserver<User>(baseImpl, "正在登陸") {
            @Override
            protected void onBaseNext(User data) {
                UserInfo userInfo = new UserInfo();
                userInfo.setId(data.getId());
                userInfo.setUsername(getView().getUserName());
                userInfo.setToken(data.getToken());
                UserDao.getInstance().deleteAll(UserInfo.class);
                UserDao.getInstance().insertObject(userInfo);
                getView().onRequestSuccessData(data);
            }
        });
    }

    public void toMainActivity(Activity activity) {
        activity.startActivity(new Intent(activity, MainActivity.class));
    }
}複製代碼

(3)、在登陸的LoginPresenter中調用LoginModel進行網絡請求,只返回一個成功的回調,失敗的回調咱們在內部處理掉了,而後在回調成功以後作相應的數據操做(該回調給View的就回調給View,該存本地的就存本地)。而後來看看咱們的Viewgithub

public class LoginActivity extends BaseActivity<LoginPresenter> implements LoginView<User> {

    @BindView(R.id.al_et_user_name)
    TextInputEditText alEtUserName;
    @BindView(R.id.al_et_password)
    TextInputEditText alEtPassword;

    @Override
    protected int layoutRes() {
        return R.layout.activity_login;
    }

    @Override
    protected LoginPresenter createPresenter() {
        return new LoginPresenter(this);
    }

    @Override
    protected void initView() {

    }

    @Override
    public String getUserName() {
        return alEtUserName.getText().toString().trim();
    }

    @Override
    public String getPassword() {
        return alEtPassword.getText().toString().trim();
    }

    @OnClick(R.id.al_btn_login)
    public void onViewClicked() {
        if (TextUtils.isEmpty(getUserName())) {
            alEtPassword.setError("用戶名不能爲空");
            return;
        }
        if (TextUtils.isEmpty(getPassword())) {
            alEtPassword.setError("密碼不能爲空");
            return;
        }
        mPresenter.getUserInfo(this);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        moveTaskToBack(true);
    }

    @Override
    public void onRequestSuccessData(User data) {
        mPresenter.toMainActivity(this);
    }
}複製代碼

View中,就是初始化Presenter,而後各類調Presenter中的方法,這裏原本是能夠在Presenter中直接調用toMainActivity()方法的,爲了演示成功回調以後再回調給View,這裏我就多作了一步操做。好了,接下來咱們來看看接口:sql

public interface BaseRequestContract<T> {

    void onRequestSuccessData(T data);

}複製代碼

這裏寫了一個Base接口,因爲大多時候咱們只關注成功的回調數據,這裏我也只寫了一個成功回調的方法(若是你有其餘的需求,你能夠在這裏加一些公共的方法),若是你有須要的話你能夠在子類中寫錯誤回調的接口,接着咱們來看看登陸的接口有哪些方法:後端

public interface LoginView<T> extends BaseRequestContract<T>{

    String getUserName();

    String getPassword();

}複製代碼

這裏我須要得到用戶的輸入信息,因此只簡單定義了兩個方法用來獲取用戶名和密碼。到這裏咱們的MVP模式就簡單封裝的差很少了,接下來咱們來看一下最終的效果吧:設計模式

這裏用eclipse+tomcat+mysql簡單寫了一個登陸接口,這一部分LZ在以前的博客中有詳細講解,若有興趣,請移步:tomcat

android開發怎麼少的了後端(上)

android開發怎麼少的了後端(中)

android開發怎麼少的了後端(下)

好了,MVP的基本封裝就講到這裏,下一節咱們再來說一下Retrofit+Rxjava的簡單封裝及使用,這裏先奉上代碼:

MVP 主工程代碼

MVP module工程代碼

可能不少人就會問了,爲何會有兩份呢,這裏我給你們看看個人項目工程

LZ把跟主項目無關的邏輯都寫到module中去了,這樣也是爲了更好的重用代碼。若是你直接去下載兩個文件的話,那麼請你下載以後把cygmodule-master文件夾的名字改爲cygmodule,或者你也能夠在主工程中將setting.gradle中的String jackchengPath = rootDir.absolutePath + '/../cygmodule'更換成你的路徑

公衆號:Android技術經驗分享
公衆號:Android技術經驗分享
相關文章
相關標籤/搜索