MVP+Rxjava

今天記錄一下自己的使用的分層框架,也是現在的主流框架MVP+Rxjava,好處就不多說了,直接主題吧。

流程圖.png

首先對照圖片來總體看一下mvp是怎么工作的

步驟一:view調用presenter的方法,通知presenter自己需要哪些數據

步驟二:presenter調用model的方法獲取數據

步驟三:model獲取到數據以后返回給presenter

步驟四:presenter獲取到數據以后再傳遞給view進行顯示

上面的流程就可以清晰地看出presenter就是一個中間人的角色,它與隔離了view和model,已達到解耦的目的,view只負責顯示數據,model只負責獲取數據(網絡或是本地)
具體如何實現請看我的github地址github

下面就根據github的內容講解

代碼結構.png

代碼的結構以功能模塊來劃分,我這里是登錄模塊,還是比較清晰地。

base

base包里面的內容集中抽象了各層的基礎操作

一、view
View也就是Activity(或是Fragment)
這里有一個BaseView是所有View的接口,定義了大部分view會有的操作,來看代碼:

public interface BaseView {
    //顯示等待框
    void showLoading();
    //顯示錯誤提示
    void showError();
}

接口里面只有兩個方法,一般的view都會有這兩個操作,如果你有更多可以添加更多方法。
二、model

public interface BaseModel {

}

model是一個空接口,因為每一個模塊使用到的數據會不一樣,具體的數據由他的實現類來確定,比如我們這里需要獲取網絡的User數據,那么BaseModel的實現就應該有getUser()這樣的方法。

三、presenter

public abstract class BasePresenter<M, V> {
    public M model;
    V view;
    public WeakReference<V> mViewRef;

    public void attachModelView(M pModel, V pView) {
        this.model = pModel;
        mViewRef = new WeakReference<>(pView);
    }

    public boolean isAttach() {
        return mViewRef != null && mViewRef.get() != null;
    }

    public V getView() {
        if (isAttach()) {
            return mViewRef.get();
        } else {
            return null;
        }
    }

    public void onDettach() {
        if (null != mViewRef) {
            mViewRef.clear();
            mViewRef = null;
        }
    }

}

presenter的內容比較多,畢竟他是model和view的中間人,所以它要同時持有這兩個引用
這里使用了兩個泛型M、V,BasePresenter并不知道model和view的具體類型,里面提供了一個初始化Model和view的方法。

到了這里base寫好了,下面就來到了我們的Login模塊
登錄的邏輯很簡單,輸入用戶名和密碼,顯示登錄結果(失敗或是成功),成功會返回User對象
LoginContract里面定義了兩個接口和一個抽象類,你也可以分開了寫,但是就會造成類太多開起來亂。

public interface LoginContract {
    interface LoginView extends BaseView{
        //在LoginPresenter中調用這個方法顯示數據
        void setUser(TestData<User> user);
    }
    interface LoginModel extends BaseModel{
        //獲取數據庫的數據
        void getUser(String name , String password, MVPListener listener);
    }
    abstract class LoginPresenter extends BasePresenter<LoginModel,LoginView>{
        //調用LoginModel的getUser方法獲取數據,傳遞給LoginView顯示,起到中間人的作用
       abstract void getUser(String name,String password);
    }
}

這里面的方法都是Login模塊需要用到的數據LoginPresenter的getUser會調用LoginModel的getUser,最后調用LoginView的setUser,也就是剛開始的流程圖的實現。

好了,寫了那么多接口終于到了他們的實現類的時候了

首先看看LoginModel

public class LoginModel implements LoginContract.LoginModel {
    Retrofit retrofit;

    @Override
    public void getUser(String name, String password, final MVPListener mvpListener) {
        //做一些網絡請求
        initHttp();
        retrofit.create(ApiService.class).reLogin(name, password).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<TestData<User>>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(TestData<User> value) {
                        //回調給presenter
                        mvpListener.onSuccess(value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        mvpListener.onError();
                    }

                    @Override
                    public void onComplete() {

                    }
                });


    }

    public void initHttp() {
        retrofit = new Retrofit.Builder().baseUrl("http://wnd.agri114.cn/wndms/")
                .client(new OkHttpClient())
                .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().setDateFormat("yyyy-MM-dd").create()))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }
}

LoginModel只負責獲取數據,所以方法也簡單只有一個getUser(),這里面用到了Retrofit和Rxjava,不會的請自行查閱(這個現在必須會),當然了我們這里只是講解MVP的實現,具體的網絡請求使用什么框架不重要。
這里面還有一個回調接口MVPListener 是用來將獲取到的數據返回給presenter的,也就是流程圖的步驟三。

public interface MVPListener<T> {
    void onSuccess(T login);

    void onError();
}

兩個方法,不多解釋。這里的地址是可以使用的,大家免費測試,用戶名是18805174084,密碼111111

再來看看LoginPresenter

public class LoginPresenter extends LoginContract.LoginPresenter {


    @Override
    void getUser(String name,String password) {
        final LoginContract.LoginView view = getView();
        view.showLoading();
        model.getUser(name, password, new MVPListener() {
            @Override
            public void onSuccess(Object login) {
                if(login instanceof  TestData){
                    view.setUser((TestData<User>) login);
                }

            }

            @Override
            public void onError() {
                 view.showError();
            }
        });
    }
}

方法也是非常的簡單,就是調用Model的getUser方法讓view顯示(注意presenter持有model和view的引用,具體請看BasePresenter)

最后看看view

我們的activity基本都會有一個baseactivity,這里我在baseactivity里面增加一些內容

public abstract class BaseActivity<T extends BasePresenter, M extends BaseModel> extends AppCompatActivity {
    public T mPresenter;
    public M mModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        mPresenter = CreatUtil.getT(this, 0);
        mModel = CreatUtil.getT(this,1);
        mPresenter.attachModelView(mModel,this);
        initView();
    }

    public abstract void initView();

    public abstract int getLayoutId();
}

這里就是mvp工作開始的地方,BaseActivity<T extends BasePresenter, M extends BaseModel>定義了兩個泛型并且在onCreate方法中初始化他們,初始化使用反射原理

  public static <T> T getT(Object o, int i) {

        try {
            return ((Class<T>) ((ParameterizedType) (o.getClass().getGenericSuperclass())).getActualTypeArguments()[i]).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

然后初始化presenter的Model和View,至于initView()和getLayoutId()由子類實現。
基類定義好之后就到了LoginActivity

public class LoginActivity extends BaseActivity<LoginPresenter, LoginModel> implements LoginContract.LoginView {
    AutoCompleteTextView name;
    EditText password;
    Button mButton;

    @Override
    public void showLoading() {
        Toast.makeText(this, "showLoading", Toast.LENGTH_LONG).show();
    }

    @Override
    public void showError() {
        Toast.makeText(this, "Error", Toast.LENGTH_LONG).show();
    }

    @Override
    public void setUser(TestData<User> user) {
        if (user != null) {
            Toast.makeText(this, user.getUser().getTel()+"", Toast.LENGTH_LONG).show();
        }
    }

    //由父類調用
    @Override
    public void initView() {
        name = (AutoCompleteTextView) findViewById(R.id.email);
        password = (EditText) findViewById(R.id.password);
        mButton = (Button) findViewById(R.id.email_sign_in_button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mPresenter.getUser(name.getText().toString(), password.getText().toString());
            }
        });

    }

    @Override
    public int getLayoutId() {
        return R.layout.activity_login;
    }
}

繼承了BaseActivity并且給Model和View設置了確定的類型,因為是view所以要實現LoginView接口,里面的方法都很簡單,

注:這里講的是MVP的工作流程,所以對于代碼的安全檢查并沒有做

其他的模塊照著login模塊就可以了

最后是一張接口和類的關系圖
在登錄模塊我把兩個接口和一個抽象類放在了一起,其他模塊建議也這樣做


繼承關系圖.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容