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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,332评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,930评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,204评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,348评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,356评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,447评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,862评论 3 394
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,516评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,710评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,518评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,582评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,295评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,848评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,881评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,121评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,737评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,280评论 2 341

推荐阅读更多精彩内容