MVP架构


目录

1)MVP简介
2)MVP实例


1)MVP简介

MVP模式将Activity中的业务逻辑全部剥离出来,Activity只做UI部分的处理,所有业务逻辑层由Presenter层去处理。

分层 说明
View Activity,Fragment,负责处理UI
Presenter 业务逻辑层,既能调用UI,又能请求数据源
Model 纯业务数据源的接口与实现

M层与V层无法直接通信,需要通过P层处理。

MVP

2)MVP实例

实现一个登录请求

假设我们来实现一个登录请求。


工程结构

-定义实体Bean

实体Bean
public class LoginReq {
    private String username;
    private String password;
    ...
}

public class LoginResponse {
    private String retcode;
    private String retinfo;
    private UserData userData;
    ...
}
public class UserData {
    private String name;
    private String age;
    ...
}

-Model层

Model层
  • Callback接口, 是Model给Presenter反馈数据信息的载体,需要在Callback中定义数据的各种反馈状态
//通过泛型T表示接受任意类型
public interface MvpCallback<T> {
    /**
     * 数据请求成功
     * @param data 请求到的数据
     */
    void onSuccess(T data);

    /**
     *  使用网络API接口请求方式时,虽然已经请求成功但是由
     *  于{@code msg}的原因无法正常返回数据。
     */
    void onFail(T data);

    /**
     * 请求数据失败,指在请求网络API接口请求方式时,出现无法联网、
     * 缺少权限,内存泄露等原因导致无法连接到请求数据源。
     */
    void onError();

    /**
     * 当请求数据结束时,无论请求结果是成功,失败或是抛出异常都会执行此方法给用户做处理,通常做网络
     * 请求时可以在此处隐藏“正在加载”的等待控件
     */
    void onComplete();
}
  • 登录业务接口,这里采用了ARouter-IProvider的服务依赖注入的方式去解耦服务,如不需要可不用继承IProvider
public interface ILoginBiz extends IProvider {

    //登录操作
    void login(LoginReq req, MvpCallback mvpCallback);

    //存储登录信息
    void saveUserdata(UserData userdata);
}
  • 登录业务实现,
@Route(path = "/service/login", name="登录服务")
public class LoginBiz implements ILoginBiz {

    @Override
    public void login(LoginReq req, final MvpCallback mvpCallback) {
        Map<String,String> param = new HashMap<String, String>();
        //调用公共组件库的OkHttpUtil进行网络API请求
        OkHttpUtil.sendRequestPost(param, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //模拟2秒休眠
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //回调onComplete
                mvpCallback.onComplete();
                //模拟response数据
                UserData userData = new UserData("涂高峰","18");
                LoginResponse loginResponse = new LoginResponse("000000","登录成功",userData);
                //回调onSuccess
                mvpCallback.onSuccess(loginResponse.getUserData());
            }
        });
    }

    @Override
    public void saveUserdata(UserData userdata) {

    }

    @Override
    public void init(Context context) {
    }
}

-View层,View接口是Activity层和Presenter层的中间层,它用于根据具体业务需要,为Presenter层提供调用Activity中具体UI操作的方法。

View层
  • IMvpView,抽象出的公共View
public interface IMvpView {

    /**
     * 显示正在加载view
     */
    void showLoading();

    /**
     * 关闭正在加载view
     */
    void hideLoading();

    /**
     * 显示提示
     * @param msg
     */
    void showToast(String msg);
}
  • ILoginView,登录业务View,继承了IMvpView。
public interface ILoginView extends IMvpView {

    /**
     * 登录请求成功提示
     */
    void loginSuccess(UserData userData);

    /**
     * 登录请求失败提示
     */
    void loginFail(String text);
}

-Presenter层,具体的业务逻辑处理Java类,不含任何Android-API操作,负责请求数据源,并对数据源反馈做处理。

Presenter层
  • MvpPresenter,抽象出公共的Presenter,
public class MvpPresenter<V extends IMvpView> {

    /**
     * 绑定的view
     */
    private V mvpView;

    /**
     * 绑定view,一般在初始化中调用该方法
     */
    public void attachView(V view){
        this.mvpView = view;
    }

    /**
     * 断开view,一般在onDestroy中调用
     * 对于Activity异常销毁导致mvpView空指针
     * 采用attachView和detachView,将view与activity生命周期绑定
     */
    public void detachView(){
        this.mvpView = null;
    }

    /**
     * 是否与View建立连接
     * 每次调用业务请求的时候都要出先调用方法检查是否与View建立连接
     */
    public boolean isViewAttached(){
        return this.mvpView != null;
    }

    /**
     * 获取连接的view
     */
    public V getView(){
        return this.mvpView;
    }

    /**
     * 获取连接的view名称
     */
    public String getViewName(){
        return this.mvpView.getClass().getSimpleName();
    }
}
  • LoginPresenter,登录业务Presenter,继承了MvpPresenter。
public class LoginPresenter extends MvpPresenter<ILoginView> {
    //依赖注入发现服务
    @Autowired(name = "/service/login")
    LoginBiz loginBiz;

    public LoginPresenter() {
//        this.loginBiz = new LoginBiz();
        //依赖注入
        ARouter.getInstance().inject(this);
    }

    public void login(LoginReq req){

        if (!isViewAttached()){
            //如果没有View引用就不加载数据
            return;
        }

        getView().showLoading();

        loginBiz.login(req, new MvpCallback() {
            @Override
            public void onSuccess(final Object data) {
                if (isViewAttached()){
                    MainLooper.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            getView().loginSuccess((UserData) data);
                        }
                    });
                }
            }

            @Override
            public void onFail(Object data) {

            }

            @Override
            public void onError() {

            }

            @Override
            public void onComplete() {
                if (isViewAttached()){
                    MainLooper.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            getView().hideLoading();
                        }
                    });
                }
            }
        });
    }
}

-Activity,登录业务Activity,实现了ILoginView

public class MainActivity extends AppCompatActivity implements ILoginView,View.OnClickListener{

    private LoginPresenter loginPresenter;
    private TextView txt;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txt = findViewById(R.id.txt);
        progressBar = findViewById(R.id.progressBar);
        findViewById(R.id.success_btn).setOnClickListener(this);

        //初始化Presenter
        loginPresenter = new LoginPresenter();
        //绑定View引用
        loginPresenter.attachView(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //断开View引用
        loginPresenter.detachView();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.success_btn:
                LoginReq loginReq = new LoginReq("tgf","123");
                loginPresenter.login(loginReq);
                break;
        }
    }

    @Override
    public void showLoading() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void showToast(String msg) {
        Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
    }

    @Override
    public void loginSuccess(UserData userData) {
        txt.setText("姓名: "+userData.getName() + "年龄"+ userData.getAge());
    }

    @Override
    public void loginFail(String text) {
    }
}

-其他工具类

public class MainLooper extends Handler {
    private static MainLooper instance = new MainLooper(Looper.getMainLooper());

    protected MainLooper(Looper looper) {
        super(looper);
    }

    public static MainLooper getInstance() {
        return instance;
    }

    public static void runOnUiThread(Runnable runnable) {
        if(Looper.getMainLooper().equals(Looper.myLooper())) {
            runnable.run();
        } else {
            instance.post(runnable);
        }
    }
}

参考资料

谷歌官方架构蓝图Android Architecture Blueprints
Android MVP架构搭建

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

推荐阅读更多精彩内容