MVP+Rxjava

今天記錄一下自己的使用的分層框架买乃,也是現(xiàn)在的主流框架MVP+Rxjava锈死,好處就不多說了珍剑,直接主題吧掸宛。

流程圖.png

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

步驟一:view調(diào)用presenter的方法,通知presenter自己需要哪些數(shù)據(jù)

步驟二:presenter調(diào)用model的方法獲取數(shù)據(jù)

步驟三:model獲取到數(shù)據(jù)以后返回給presenter

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

上面的流程就可以清晰地看出presenter就是一個中間人的角色招拙,它與隔離了view和model唧瘾,已達到解耦的目的措译,view只負責(zé)顯示數(shù)據(jù),model只負責(zé)獲取數(shù)據(jù)(網(wǎng)絡(luò)或是本地)
具體如何實現(xiàn)請看我的github地址github

下面就根據(jù)github的內(nèi)容講解

代碼結(jié)構(gòu).png

代碼的結(jié)構(gòu)以功能模塊來劃分饰序,我這里是登錄模塊领虹,還是比較清晰地。

base

base包里面的內(nèi)容集中抽象了各層的基礎(chǔ)操作

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

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

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

public interface BaseModel {

}

model是一個空接口肚菠,因為每一個模塊使用到的數(shù)據(jù)會不一樣,具體的數(shù)據(jù)由他的實現(xiàn)類來確定罩缴,比如我們這里需要獲取網(wǎng)絡(luò)的User數(shù)據(jù)蚊逢,那么BaseModel的實現(xiàn)就應(yīng)該有g(shù)etUser()這樣的方法。

三箫章、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的內(nèi)容比較多烙荷,畢竟他是model和view的中間人,所以它要同時持有這兩個引用
這里使用了兩個泛型M檬寂、V终抽,BasePresenter并不知道m(xù)odel和view的具體類型,里面提供了一個初始化Model和view的方法桶至。

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

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

這里面的方法都是Login模塊需要用到的數(shù)據(jù)LoginPresenter的getUser會調(diào)用LoginModel的getUser逸寓,最后調(diào)用LoginView的setUser,也就是剛開始的流程圖的實現(xiàn)覆山。

好了竹伸,寫了那么多接口終于到了他們的實現(xiàn)類的時候了

首先看看LoginModel

public class LoginModel implements LoginContract.LoginModel {
    Retrofit retrofit;

    @Override
    public void getUser(String name, String password, final MVPListener mvpListener) {
        //做一些網(wǎng)絡(luò)請求
        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) {
                        //回調(diào)給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只負責(zé)獲取數(shù)據(jù),所以方法也簡單只有一個getUser()汹买,這里面用到了Retrofit和Rxjava佩伤,不會的請自行查閱(這個現(xiàn)在必須會)聊倔,當(dāng)然了我們這里只是講解MVP的實現(xiàn)晦毙,具體的網(wǎng)絡(luò)請求使用什么框架不重要生巡。
這里面還有一個回調(diào)接口MVPListener 是用來將獲取到的數(shù)據(jù)返回給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();
            }
        });
    }
}

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

最后看看view

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

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()由子類實現(xiàn)溃卡。
基類定義好之后就到了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();
        }
    }

    //由父類調(diào)用
    @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設(shè)置了確定的類型溢豆,因為是view所以要實現(xiàn)LoginView接口,里面的方法都很簡單瘸羡,

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

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

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


繼承關(guān)系圖.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末犹赖,一起剝皮案震驚了整個濱河市队他,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌峻村,老刑警劉巖漱挎,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雀哨,居然都是意外死亡磕谅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門雾棺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膊夹,“玉大人,你說我怎么就攤上這事捌浩》排伲” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵尸饺,是天一觀的道長进统。 經(jīng)常有香客問我助币,道長,這世上最難降的妖魔是什么螟碎? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任眉菱,我火速辦了婚禮,結(jié)果婚禮上掉分,老公的妹妹穿的比我還像新娘俭缓。我一直安慰自己,他們只是感情好酥郭,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布华坦。 她就那樣靜靜地躺著,像睡著了一般不从。 火紅的嫁衣襯著肌膚如雪惜姐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天椿息,我揣著相機與錄音歹袁,去河邊找鬼。 笑死撵颊,一個胖子當(dāng)著我的面吹牛宇攻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播倡勇,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼逞刷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了妻熊?” 一聲冷哼從身側(cè)響起夸浅,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扔役,沒想到半個月后帆喇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡亿胸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年坯钦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侈玄。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡婉刀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出序仙,到底是詐尸還是另有隱情突颊,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站律秃,受9級特大地震影響爬橡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜棒动,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一糙申、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧迁客,春花似錦郭宝、人聲如沸辞槐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽榄檬。三九已至卜范,卻和暖如春拳亿,著一層夾襖步出監(jiān)牢的瞬間迁匠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工子房, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舱殿,地道東北人奥裸。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像沪袭,于是被迫代替她去往敵國和親湾宙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容