mvp关联activity生命周期_Android MVP架构从入门到精通-真枪实弹

2023-11-12

Android MVP架构从入门到精通-真枪实弹

d42bf24f-ca20-4d4b-ba7c-643d4cf87fab

一. 前言

你是否遇到过Activity/Fragment中成百上千行代码,完全无法维护,看着头疼?

你是否遇到过因后台接口还未写而你不能先写代码逻辑的情况?

你是否遇到过用MVC架构写的项目进行单元测试时的深深无奈?

如果你现在还是用MVC架构模式在写项目,请先转到MVP模式!

二. MVC架构

MVC架构模式最初生根于服务器端的Web开发,后来渐渐能够胜任客户端Web开发,再后来因Android项目由XML和Activity/Fragment组成,慢慢的Android开发者开始使用类似MVC的架构模式开发应用.

19f2964c-cc78-4fb6-a6e3-100f35be4e64

M层:模型层(model),主要是实体类,数据库,网络等存在的层面,model将新的数据发送到view层,用户得到数据响应.

V层:视图层(view),一般指XML为代表的视图界面.显示来源于model层的数据.用户的点击操作等事件从view层传递到controller层.

C层:控制层(controller),一般以Activity/Fragment为代表.C层主要是连接V层和M层的,C层收到V层发送过来的事件请求,从M层获取数据,展示给V层.

从上图可以看出M层和V层有连接关系,而Activity有时候既充当了控制层又充当了视图层,导致项目维护比较麻烦.

1. MVC架构优缺点

A. 缺点

M层和V层有连接关系,没有解耦,导致维护困难.

Activity/Fragment中的代码过多,难以维护.

Activity中有很多关于视图UI的显示代码,因此View视图和Activity控制器并不是完全分离的,当Activity类业务过多的时候,会变得难以管理和维护.尤其是当UI的状态数据,跟持久化的数据混杂在一起,变得极为混乱.

B. 优点

控制层和View层都在Activity中进行操作,数据操作方便.

模块职责划分明确.主要划分层M,V,C三个模块.

三. MVP架构

55eb70f5-4c6a-4757-bbfe-07c82ab946ed

MVP,即是Model,View,Presenter架构模式.看起来类似MVC,其实不然.从上图能看到Model层和View层没有相连接,完全解耦.

用户触碰界面触发事件,View层把事件通知Presenter层,Presenter层通知Model层处理这个事件,Model层处理后把结果发送到Presenter层,Presenter层再通知View层,最后View层做出改变.这是一整套流程.

M层:模型层(Model),此层和MVC中的M层作用类似.

V层:视图层(View),在MVC中V层只包含XML文件,而MVP中V层包含XML,Activity和Fragment三者.理论上V层不涉及任何逻辑,只负责界面的改变,尽量把逻辑处理放到M层.

P层:通知层(Presenter),P层的主要作用就是连接V层和M层,起到一个通知传递数据的作用.

1. MVP架构优缺点

A. 缺点

MVP中接口过多.

每一个功能,相比于MVC要多写好几个文件.

如果某一个界面中需要请求多个服务器接口,这个界面文件中会实现很多的回调接口,导致代码繁杂.

如果更改了数据源和请求中参数,会导致更多的代码修改.

额外的代码复杂度及学习成本.

B. 优点

模块职责划分明显,层次清晰,接口功能清晰.

Model层和View层分离,解耦.修改View而不影响Model.

功能复用度高,方便.一个Presenter可以复用于多个View,而不用更改Presenter的逻辑.

有利于测试驱动开发,以前的Android开发是难以进行单元测试.

如果后台接口还未写好,但已知返回数据类型的情况下,完全可以写出此接口完整的功能.

四. MVP架构实战(真枪实弹)

1. MVP三层代码简单书写

接下来笔者从简到繁,一点一点的堆砌MVP的整个架构.先看一下XML布局,布局中一个Button按钮和一个TextView控件,用户点击按钮后,Presenter层通知Model层请求处理网络数据,处理后Model层把结果数据发送给Presenter层,Presenter层再通知View层,然后View层改变TextView显示的内容.

20b8197824457f480553d817ebd1a5ec.gif

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_gravity="center"

android:gravity="center"

android:orientation="vertical"

tools:context=".view.SingleInterfaceActivity">

android:id="@+id/button"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="点击" />

android:id="@+id/textView"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginTop="100px"

android:text="请点击上方按钮获取数据" />

接下来是Activity代码,里面就是获取Button和TextView控件,然后对Button做监听,先简单的这样写,一会慢慢的增加代码.

public class SingleInterfaceActivity extends AppCompatActivity {

private Button button;

private TextView textView;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_single_interface);

button = findViewById(R.id.button);

textView = findViewById(R.id.textView);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

}

});

}

}

下面是Model层代码.本次网络请求用的是wanandroid网站的开放api,其中的文章首页列表接口.SingleInterfaceModel文件里面有一个方法getData,第一个参数curPage意思是获取第几页的数据,第二个参数callback是Model层通知Presenter层的回调.

public class SingleInterfaceModel {

public void getData(int curPage, final Callback callback) {

NetUtils.getRetrofit()

.create(Api.class)

.getData(curPage)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Subscriber() {

@Override

public void onCompleted() {

LP.w("completed");

}

@Override

public void onError(Throwable e) {

callback.onFail("出现错误");

}

@Override

public void onNext(ArticleListBean bean) {

if (null == bean) {

callback.onFail("出现错误");

} else if (bean.errorCode != 0) {

callback.onFail(bean.errorMsg);

} else {

callback.onSuccess(bean);

}

}

});

}

}

Callback文件内容如下.里面一个成功一个失败的回调接口,参数全是泛型,为啥使用泛型笔者就不用说了吧.

public interface Callback {

void onSuccess(K data);

void onFail(V data);

}

再接下来是Presenter层的代码.SingleInterfacePresenter类构造函数中直接new了一个Model层对象,用于Presenter层对Model层的调用.然后SingleInterfacePresenter类的方法getData用于与Model的互相连接.

public class SingleInterfacePresenter {

private final SingleInterfaceModel singleInterfaceModel;

public SingleInterfacePresenter() {

this.singleInterfaceModel = new SingleInterfaceModel();

}

public void getData(int curPage) {

singleInterfaceModel.getData(curPage, new Callback() {

@Override

public void onSuccess(ArticleListBean loginResultBean) {

//如果Model层请求数据成功,则此处应执行通知View层的代码

}

@Override

public void onFail(String errorMsg) {

//如果Model层请求数据失败,则此处应执行通知View层的代码

}

});

}

}

至此,MVP三层简单的部分代码算是完成.那么怎样进行整个流程的相互调用呢.我们把刚开始的SingleInterfaceActivity代码改一下,让SingleInterfaceActivity持有Presenter层的对象,这样View层就可以调用Presenter层了.修改后代码如下.

public class SingleInterfaceActivity extends AppCompatActivity {

private Button button;

private TextView textView;

private SingleInterfacePresenter singleInterfacePresenter;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_single_interface);

button = findViewById(R.id.button);

textView = findViewById(R.id.textView);

singleInterfacePresenter = new SingleInterfacePresenter();

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

singleInterfacePresenter.getData(0);

}

});

}

}

从以上所有代码可以看出,当用户点击按钮后,View层按钮的监听事件执行调用了Presenter层对象的getData方法,此时,Presenter层对象的getData方法调用了Model层对象的getData方法,Model层对象的getData方法中执行了网络请求和逻辑处理,把成功或失败的结果通过Callback接口回调给了Presenter层,然后Presenter层再通知View层改变界面.但此时SingleInterfacePresenter类中收到Model层的结果后无法通知View层,因为SingleInterfacePresenter未持有View层的对象.如下代码的注释中有说明.(如果此时点击按钮,下方代码LP.w()处会打印出网络请求成功的log)

public class SingleInterfacePresenter {

private final SingleInterfaceModel singleInterfaceModel;

public SingleInterfacePresenter() {

this.singleInterfaceModel = new SingleInterfaceModel();

}

public void getData(int curPage) {

singleInterfaceModel.getData(curPage, new Callback() {

@Override

public void onSuccess(ArticleListBean loginResultBean) {

//如果Model层请求数据成功,则此处应执行通知View层的代码

//LP.w()是一个简单的log打印

LP.w(loginResultBean.toString());

}

@Override

public void onFail(String errorMsg) {

//如果Model层请求数据失败,则此处应执行通知View层的代码

}

});

}

}

代码写到这里,笔者先把这些代码提交到github(https://github.com/serge66/MVPDemo),github上会有一次提交记录,如果想看此时的代码,可以根据提交记录"第一次修改"克隆此时的代码.

2. P层V层沟通桥梁

现在P层未持有V层对象,不能通知V层改变界面,那么就继续演变MVP架构.

在MVP架构中,我们要为每个Activity/Fragment写一个接口,这个接口需要让Presenter层持有,P层通过这个接口去通知V层更改界面.接口中包含了成功和失败的回调,这个接口Activity/Fragment要去实现,最终P层才能通知V层.

public interface SingleInterfaceIView {

void showArticleSuccess(ArticleListBean bean);

void showArticleFail(String errorMsg);

}

一个完整的项目以后肯定会有许多功能界面,那么我们应该抽出一个IView公共接口,让所有的Activity/Fragment都间接实现它.IVew公共接口是用于给View层的接口继承的,注意,不是View本身继承.因为它定义的是接口的规范, 而其他接口才是定义的类的规范(这句话请仔细理解).

/**

* @Description: 公共接口 是用于给View的接口继承的,注意,不是View本身继承。

* 因为它定义的是接口的规范, 而其他接口才是定义的类的规范

* @Author: lishengjiejob@163.com

* @Time: 2018/11/22 17:26

*/

public interface IView {

}

这个接口中可以写一些所有Activigy/Fragment共用的方法,我们把SingleInterfaceIView继承IView接口.

public interface SingleInterfaceIView extends IView {

void showArticleSuccess(ArticleListBean bean);

void showArticleFail(String errorMsg);

}

同理Model层和Presenter层也是如此.

public interface IModel {

}

public interface IPresenter {

}

现在项目中Model层是一个SingleInterfaceModel类,这个类对象被P层持有,对于面向对象设计来讲,利用接口达到解耦目的已经人尽皆知,那我们就要对SingleInterfaceModel类再写一个可继承的接口.代码如下.

public interface ISingleInterfaceModel extends IModel {

void getData(int curPage, final Callback callback);

}

如此,SingleInterfaceModel类的修改如下.

public class SingleInterfaceModel implements ISingleInterfaceModel {

@Override

public void getData(int curPage, final Callback callback) {

NetUtils.getRetrofit()

.create(Api.class)

.getData(curPage)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Subscriber() {

@Override

public void onCompleted() {

LP.w("completed");

}

@Override

public void onError(Throwable e) {

callback.onFail("出现错误");

}

@Override

public void onNext(ArticleListBean bean) {

if (null == bean) {

callback.onFail("出现错误");

} else if (bean.errorCode != 0) {

callback.onFail(bean.errorMsg);

} else {

callback.onSuccess(bean);

}

}

});

}

}

同理,View层持有P层对象,我们也需要对P层进行改造.但是下面的代码却没有像ISingleInterfaceModel接口继承IModel一样继承IPresenter,这点需要注意,笔者把IPresenter的继承放在了其他处,后面会讲解.

public interface ISingleInterfacePresenter {

void getData(int curPage);

}

然后SingleInterfacePresenter类的修改如下:

public class SingleInterfacePresenter implements ISingleInterfacePresenter {

private final ISingleInterfaceModel singleInterfaceModel;

public SingleInterfacePresenter() {

this.singleInterfaceModel = new SingleInterfaceModel();

}

@Override

public void getData(int curPage) {

singleInterfaceModel.getData(curPage, new Callback() {

@Override

public void onSuccess(ArticleListBean loginResultBean) {

//如果Model层请求数据成功,则此处应执行通知View层的代码

//LP.w()是一个简单的log打印

LP.w(loginResultBean.toString());

}

@Override

public void onFail(String errorMsg) {

//如果Model层请求数据失败,则此处应执行通知View层的代码

LP.w(errorMsg);

}

});

}

}

3. 生命周期适配

至此,MVP三层每层的接口都写好了.但是P层连接V层的桥梁还没有搭建好,这个慢慢来,一个好的高楼大厦都是一步一步建造的.上面IPresenter接口我们没有让其他类继承,接下来就讲下这个.P层和V层相连接,V层的生命周期也要适配到P层,P层的每个功能都要适配生命周期,这里可以把生命周期的适配放在IPresenter接口中.P层持有V层对象,这里把它放到泛型中.代码如下.

public interface IPresenter {

/**

* 依附生命view

*

* @param view

*/

void attachView(T view);

/**

* 分离View

*/

void detachView();

/**

* 判断View是否已经销毁

*

* @return

*/

boolean isViewAttached();

}

这个IPresenter接口需要所有的P层实现类继承,对于生命周期这部分功能都是通用的,那么就可以抽出来一个抽象基类BasePresenter,去实现IPresenter的接口.

public abstract class BasePresenter implements IPresenter {

protected T mView;

@Override

public void attachView(T view) {

mView = view;

}

@Override

public void detachView() {

mView = null;

}

@Override

public boolean isViewAttached() {

return mView != null;

}

}

此时,SingleInterfacePresenter类的代码修改如下.泛型中的SingleInterfaceIView可以理解成对应的Activity,P层此时完成了对V层的通信.

public class SingleInterfacePresenter extends BasePresenter implements ISingleInterfacePresenter {

private final ISingleInterfaceModel singleInterfaceModel;

public SingleInterfacePresenter() {

this.singleInterfaceModel = new SingleInterfaceModel();

}

@Override

public void getData(int curPage) {

singleInterfaceModel.getData(curPage, new Callback() {

@Override

public void onSuccess(ArticleListBean loginResultBean) {

//如果Model层请求数据成功,则此处应执行通知View层的代码

//LP.w()是一个简单的log打印

LP.w(loginResultBean.toString());

if (isViewAttached()) {

mView.showArticleSuccess(loginResultBean);

}

}

@Override

public void onFail(String errorMsg) {

//如果Model层请求数据失败,则此处应执行通知View层的代码

LP.w(errorMsg);

if (isViewAttached()) {

mView.showArticleFail(errorMsg);

}

}

});

}

}

此时,P层和V层的连接桥梁已经搭建,但还未搭建完成,我们需要写个BaseMVPActvity让所有的Activity继承,统一处理Activity相同逻辑.在BaseMVPActvity中使用IPresenter的泛型,因为每个Activity中需要持有P层对象,这里把P层对象抽出来也放在BaseMVPActvity中.同时BaseMVPActvity中也需要继承IView,用于P层对V层的生命周期中.代码如下.

public abstract class BaseMVPActivity extends AppCompatActivity implements IView {

protected T mPresenter;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

initPresenter();

init();

}

protected void initPresenter() {

mPresenter = createPresenter();

//绑定生命周期

if (mPresenter != null) {

mPresenter.attachView(this);

}

}

@Override

protected void onDestroy() {

if (mPresenter != null) {

mPresenter.detachView();

}

super.onDestroy();

}

/**

* 创建一个Presenter

*

* @return

*/

protected abstract T createPresenter();

protected abstract void init();

}

接下来让SingleInterfaceActivity实现这个BaseMVPActivity.

public class SingleInterfaceActivity extends BaseMVPActivity implements SingleInterfaceIView {

private Button button;

private TextView textView;

@Override

protected void init() {

setContentView(R.layout.activity_single_interface);

button = findViewById(R.id.button);

textView = findViewById(R.id.textView);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

mPresenter.getData(0);

}

});

}

@Override

protected SingleInterfacePresenter createPresenter() {

return new SingleInterfacePresenter();

}

@Override

public void showArticleSuccess(ArticleListBean bean) {

textView.setText(bean.data.datas.get(0).title);

}

@Override

public void showArticleFail(String errorMsg) {

Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();

}

}

到此,MVP架构的整个简易流程完成.

代码写到这里,笔者先把这些代码提交到github(https://github.com/serge66/MVPDemo),github上会有一次提交记录,如果想看此时的代码,可以根据提交记录"第二次修改"克隆此时的代码.

4. 优化MVP架构

55f0aad0-6d46-4ba2-95e6-cba730f69bdc

上面是MVP的目录,从目录中我们可以看到一个功能点(网络请求)MVP三层各有两个文件需要写,相对于MVC来说写起来确实麻烦,这也是一些人不愿意写MVP,宁愿用MVC的原因.

这里我们可以对此优化一下.MVP架构中有个Contract的概念,Contract有统一管理接口的作用,目的是为了统一管理一个页面的View和Presenter接口,用Contract可以减少部分文件的创建,比如P层和V层的接口文件.

那我们就把P层的ISingleInterfacePresenter接口和V层的SingleInterfaceIView接口文件删除掉,放入SingleInterfaceContract文件中.代码如下.

public interface SingleInterfaceContract {

interface View extends IView {

void showArticleSuccess(ArticleListBean bean);

void showArticleFail(String errorMsg);

}

interface Presenter {

void getData(int curPage);

}

}

此时,SingleInterfacePresenter和SingleInterfaceActivity的代码修改如下.

public class SingleInterfacePresenter extends BasePresenter

implements SingleInterfaceContract.Presenter {

private final ISingleInterfaceModel singleInterfaceModel;

public SingleInterfacePresenter() {

this.singleInterfaceModel = new SingleInterfaceModel();

}

@Override

public void getData(int curPage) {

singleInterfaceModel.getData(curPage, new Callback() {

@Override

public void onSuccess(ArticleListBean loginResultBean) {

//如果Model层请求数据成功,则此处应执行通知View层的代码

//LP.w()是一个简单的log打印

LP.w(loginResultBean.toString());

if (isViewAttached()) {

mView.showArticleSuccess(loginResultBean);

}

}

@Override

public void onFail(String errorMsg) {

//如果Model层请求数据失败,则此处应执行通知View层的代码

LP.w(errorMsg);

if (isViewAttached()) {

mView.showArticleFail(errorMsg);

}

}

});

}

}

public class SingleInterfaceActivity extends BaseMVPActivity

implements SingleInterfaceContract.View {

private Button button;

private TextView textView;

@Override

protected void init() {

setContentView(R.layout.activity_single_interface);

button = findViewById(R.id.button);

textView = findViewById(R.id.textView);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

mPresenter.getData(0);

}

});

}

@Override

protected SingleInterfacePresenter createPresenter() {

return new SingleInterfacePresenter();

}

@Override

public void showArticleSuccess(ArticleListBean bean) {

textView.setText(bean.data.datas.get(0).title);

}

@Override

public void showArticleFail(String errorMsg) {

Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();

}

}

代码写到这里,笔者先把这些代码提交到github(https://github.com/serge66/MVPDemo),github上会有一次提交记录,如果想看此时的代码,可以根据提交记录"第三次修改"克隆此时的代码.

5. 单页面多网络请求

上面的MVP封装只适用于单页面一个网络请求的情况,当一个界面有两个网络请求时,此封装已不适合.为此,我们再次新建一个MultipleInterfaceActivity来进行说明.XML中布局是两个按钮两个Textview,点击则可以进行网络请求.

a378c0b54ed5dab6c3cd1546c9f7b225.gif

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_gravity="center"

android:gravity="center"

android:orientation="vertical"

tools:context=".view.MultipleInterfaceActivity">

android:id="@+id/button"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="点击" />

android:id="@+id/textView"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginTop="50px"

android:text="请点击上方按钮获取数据" />

android:id="@+id/btn"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginTop="100px"

android:text="点击" />

android:id="@+id/tv"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginTop="50px"

android:text="请点击上方按钮获取数据" />

MultipleInterfaceActivity类代码暂时如下.

public class MultipleInterfaceActivity extends BaseMVPActivity {

private Button button;

private TextView textView;

private Button btn;

private TextView tv;

@Override

protected void init() {

setContentView(R.layout.activity_multiple_interface);

button = findViewById(R.id.button);

textView = findViewById(R.id.textView);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

}

});

btn = findViewById(R.id.btn);

tv = findViewById(R.id.tv);

btn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

}

});

}

@Override

protected IPresenter createPresenter() {

return null;

}

}

此时我们可以想下,当一个页面中有多个网络请求时,Activity所继承的BaseMVPActivity的泛型中要写多个参数,那有没有上面代码的框架不变的情况下实现这个需求呢?答案必须有的.我们可以把多个网络请求的功能当做一个网络请求来看待,封装成一个MultiplePresenter,其继承至BasePresenter实现生命周期的适配.此MultiplePresenter类的作用就是容纳多个Presenter,连接同一个View.代码如下.

public class MultiplePresenter extends BasePresenter {

private T mView;

private List presenters = new ArrayList<>();

@SafeVarargs

public final > void addPresenter(K... addPresenter) {

for (K ap : addPresenter) {

ap.attachView(mView);

presenters.add(ap);

}

}

public MultiplePresenter(T mView) {

this.mView = mView;

}

@Override

public void detachView() {

for (IPresenter presenter : presenters) {

presenter.detachView();

}

}

}

因MultiplePresenter类中需要有多个网络请求,现在举例说明时,暂时用两个网络请求接口.MultipleInterfaceActivity类中代码改造如下.

public class MultipleInterfaceActivity extends BaseMVPActivity

implements SingleInterfaceContract.View, MultipleInterfaceContract.View {

private Button button;

private TextView textView;

private Button btn;

private TextView tv;

private SingleInterfacePresenter singleInterfacePresenter;

private MultipleInterfacePresenter multipleInterfacePresenter;

@Override

protected void init() {

setContentView(R.layout.activity_multiple_interface);

button = findViewById(R.id.button);

textView = findViewById(R.id.textView);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

singleInterfacePresenter.getData(0);

}

});

btn = findViewById(R.id.btn);

tv = findViewById(R.id.tv);

btn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

multipleInterfacePresenter.getBanner();

}

});

}

@Override

protected MultiplePresenter createPresenter() {

MultiplePresenter multiplePresenter = new MultiplePresenter(this);

singleInterfacePresenter = new SingleInterfacePresenter();

multipleInterfacePresenter = new MultipleInterfacePresenter();

multiplePresenter.addPresenter(singleInterfacePresenter);

multiplePresenter.addPresenter(multipleInterfacePresenter);

return multiplePresenter;

}

@Override

public void showArticleSuccess(ArticleListBean bean) {

textView.setText(bean.data.datas.get(0).title);

}

@Override

public void showArticleFail(String errorMsg) {

Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();

}

@Override

public void showMultipleSuccess(BannerBean bean) {

tv.setText(bean.data.get(0).title);

}

@Override

public void showMultipleFail(String errorMsg) {

Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();

}

}

写到这里,MVP框架基本算是完成.如果想再次优化,其实还是有可优化的地方,比如当View销毁时,现在只是让P层中的View对象置为null,并没有继续对M层通知.如果View销毁时,M层还在请求网络中呢,可以为此再加入一个取消网络请求的通用功能.这里只是举一个例子,每个人对MVP的理解不一样,而MVP架构也并不是一成不变,适合自己项目的才是最好的.

6. 完整项目地址

五. 参考资料

六. 后续

<> 敬请期待~~~

文章同步发布于:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

mvp关联activity生命周期_Android MVP架构从入门到精通-真枪实弹 的相关文章

  • C/C++ 程序自删除

    文章目录 前言 一 代码 二 部分代码解释 前言 一般病毒之内的可能都带有自删除功能 而目前可进行完美自删除的方法并不多 其中一种较好的解决方法就是利用批处理文件 批处理文件一个优点就是 即使自身在运行的情况下也可以删除自己 所以实现的逻辑
  • 【Opencv&Cpp】12 像素统计:最大/小值、平均值、标准差

    minMaxLoc 找到全局最小和最大值 meanStdDev 计算矩阵的均值和标准偏差 找到全局最小和最大值 minmaxloc minMaxLoc InputArray src double minVal double maxVal 0
  • 大家来讨论怎么写概要设计

    http blog csdn net sunwill chen article details 7864904 笔者声明 本文讲述笔者浅薄的观点 意在抛砖引玉 望网友一起发表观点共同切磋 目前网络上的概要设计格式繁多 质量也是参差不齐 许多
  • 单链表C语言代码实现

    一 代码 include
  • sqli-labs-master靶场搭建以及报错解决

    一 前提准备 1 下载 sqli labs master mirrors audi 1 sqli labs GitCode 2 安装PHP study Windows版phpstudy下载 小皮面板 phpstudy xp cn 二 搭建靶
  • 华为OD机试 - 最小传输时延(Java)

    题目描述 某通信网络中有N个网络结点 用1到N进行标识 网络通过一个有向无环图表示 其中图的边的值表示结点之间的消息传递时延 现给定相连节点之间的时延列表times i u v w 其中u表示源结点 v表示目的结点 w表示u和v之间的消息传
  • Pycharm中修改注释文本的颜色(详细设置步骤)

    下面是在Pycharm中设置注释文本颜色的详细步骤 下面是修改前后对比 修改前注释行的颜色 修改后注释行的颜色 以上就是Pycharm中修改注释文本颜色的详细步骤 希望能帮到你
  • 小程序真机调试连接本地服务器进行调试

    小程序连接本地服务器 开发小程序时经常会遇到需要连接本地服务器进行调试的时候 但是总是连接不上 这里就说一下本菜鸟连接本地服务器的方法 第一步 把下图红框的地方勾选住 好多方法都得选这一步 第二步 设置里面代理按图中勾选 第三步是连接的方法
  • JavaScript避免使用return跳出多重循环从而继续执行函数;使用break跳出多重for循环

    一 先来看一下使用break仅跳出一层for循环的用法 const foo function for let i 1 i lt 3 i for let j 1 j lt 3 j if i 2 break console log 输出j的值
  • Mac上使用GPU加速训练模型

    文章目录 前言 使用GPU 前言 上一篇文章中我介绍了使用pytorch的一个完整模型训练套路 其中没有使用gpu 如果要使用gpu的话 win上我们可以使用cuda mac上可以使用mps 而我自己是mac电脑 需要进行如下修改 使用GP
  • ubuntu下docker配置国内镜像源

    既然都看到这篇文章了 就不解释为什么需要配置国内镜像源了 直接上步骤 此文使用ubuntu环境为Ubuntu 18 04 4 LTS 使用 sudo vim etc docker daemon json 命令新建或编辑文件 输入以下内容 r
  • 大话设计模式C++实现-第23章-命令模式

    一 UML图 二 概念 命令模式 Command 将一个请求封装为一个对象 从而使你可用不同的请求对客户进行参数化 对请求进行排队或记录请求日志 以及支持可撤销的操作 三 说明 角色 1 Command类 用来声明执行操作的接口 2 Con
  • 漫谈 SLAM 技术(下)

    转自 https zhuanlan zhihu com p 135958593 3 视觉SLAM系统关键问题 结合上述介绍的SLAM系统 我们从以下几个方面分析视觉SLAM系统需要考虑的关键问题 1 图像信息使用 视觉SLAM方法根据使用图
  • 模块""可能与您正在运行的Windows版本不兼容。检查该模块是否与regsvr32.exe的x86或x64版

    本人最近在研究mencoder 转换视频格式 发现转换rmvb需要 1 把drv43260 dll拷贝到系统的system32文件夹下 2 开始 gt 运行 gt regsvr32 drv43260 dll 来自 http topic cs
  • C++容器之 vector map set查找元素

    前面两篇基本上讲解容器的增加删除 其实现实世界中对数据的查找才是最大的需求 下面主要围绕着容器的查找来讲解 首先 由于vector没有实现find 方法 只能使用algorithm提供的find 方法 所以 直接在vector查找节介绍al
  • 多线程编程

    Linux线程概述 内核线程和用户线程 线程是程序中完成独立任务的完整执行序列 即一个可调度的实体 根据运行环境和调度者身份 线程分为内核线程和用户线程 内核线程 在有的系统上也称为LWP 轻量级进程 运行在内核空间 由内核调度 用户线程
  • 概率在计算机学中的应用,概率统计在计算机中的应用

    概率统计在计算机中的应用 一 综述 研究自然界中随机现象统计规律的数学方法 叫做概率统计 又称数理统计方法 概率论 是根据大量同类随机现象的统计规律 对随机现象出现某一结果的可能性作出一种客观的科学判断 对这种出现的可能性大小做出数量上的描
  • 交换机ACL配置

    交换机ACL配置 实验要求 PC3能ping通R3和R4 PC2能ping通R3和R4 R3和R4全网互通 通过ACL使PC2不能ping通PC3 先给每个接口配置ip PC3 192 168 5 3 24 192 168 5 254 PC
  • ngx_http_ssl_module

    ngx http ssl module 模块 语法 ssl 在 从 默认值 ssl off 背景 http 服务器 支持HTTPS协议为给定的虚拟服务器 推荐使用 ssl 参数的 听 指令而不是 这个指令 语法 ssl buffer siz

随机推荐

  • 哈哈,太真实了!除了《颈椎康复指南》,还有这 9 本书

    点击上方 Java后端 选择 设为星标 优质文章 及时送达 作者 sivagao 链接 https github com sivagao 本文罗列的这些书籍封面其实是各种典型的反模式 不过它们真的是非常常见以至于大家都习以为常了 从 Sta
  • Go面试题专题(一):聊聊你理解的Golang defer关键字

    defer关键字是我们工作中经常用到的go语言特性 也是面试官比较青睐的一个知识点 今天通过这篇文章带各位道友彻底掌握它 面试题文档下链接点击这里免积分下载 go语言入门到精通点击这里免积分下载 文章目录 defer两大特性 defer与r
  • vue中element-ui实现表单根据不同下拉框进行动态表单校验

    vue中element ui实现表单根据不同下拉框进行动态表单校验 我们想实现的功能如下 请看效果 话不多说我们上代码 html部分
  • linux cd命令详解

    Linux cd 英文全拼 change directory 命令用于切换当前工作目录 使用方式 cd dirName 其中 dirName 为目录名称 可为绝对路径或相对路径 若目录名称省略 则切换至home 目录 也表示为 home 目
  • Android平台生成二维码(by google.zxing)

    查了大部分的资料 发现android平台下生成二维码的例子都是使用谷歌的zxing类 因此仿照某一个帖子编写了一个demo进行测试 仿照的帖子 https blog csdn net myname kk article details 77
  • 使用 Hexo 搭建静态个人博客与绑定个人域名

    1 安装Git 下载并安装Git 可以选择淘宝 Git for Windows 镜像 https npm taobao org mirrors git for windows 2 安装Node js 下载安装Node js Node js
  • SpringMVC关于Validform实时校验身份证的作为账户的问题

    此地址上有相关案例 http validform rjboy cn 看不懂别怪我 前端代码 例如 div class f fl item ifo item sfz div
  • C#知识结构

    对于一个工作多年的程序员而言 接口 反射 索引器 事件 委托这些耳熟能详的词汇 提起来别说多简单了 但是让老司机坐在那一个人拿起一支笔 把脑海中对C 知识结构进行梳理一下 大抵是写不了多内容的 原因是什么呢 是遗忘 当然不是 每天面对代码的
  • mkdir函数-linux

    mkdir函数 头文件库 include
  • Heron 编译错误:no such package ‘@org_apache_thrift_libthrift//jar’

    错误 ERROR heron heron metricsmgr src java BUILD 5 1 no such package org apache thrift libthrift jar Failed to fetch Maven
  • The 19th ZCPC -G. Easy Glide

    Grammy is playing a boring racing game named Easy Gliding The game s main content is to reach the destination as fast as
  • 安全工具杂烩

    20201103 本来想单独列出来一个文章来记录每个工具 但是发现并没有那么多精力 这里仅仅记录一下看到的一些不错的工具 sdnewhop grinder 据其描述 这个是一个通过shodan或者censys来获取主机信息的工具 是不是跟一
  • 阿里云服务器一直提示安全事件如何解决

    介绍 这几天一直收到阿里云官方的短信和邮箱提示阿里云安全事件提醒 阿里云的官方的客服也打电话询问过我需不需要帮助 由于我的阿里云服务器没有用于商业用途 只是学习的时候使用 所有也就决定自己解决了 影响 由于最近比较忙 就没有怎么注意阿里云短
  • 433MHz工业级无线数传通信模块

    433MHz工业级无线数传通信模块 无线RS232 RS485透明传输 距离1 3000米 DTD465系列工业无线数传模块采用最先进的电子和无线通信技术 能为众多的工业与应用提供高性能 中等距离和可靠数据传输的低成本解决方案 它的工业级电
  • 计算机date时间和‘千年虫事件’

    目录 一 千年虫事件 1 千年虫事件 名词解析 2 应对2000年计算机问题的解决方法 二 Unix Linux 2038问题 Linux系统的几种时间 1 时间戳 date 2 UTC时间和本地时间 timedatectl 3 避免因时间
  • 日精注塑机联网

    不改造程序的话 日精支持输出CSV和txt数据作为其他软件的接口 改造后可以支持63协议 在软件层面日精也有专用的软件 可以看到其实设备厂家提供的软件功能已经非常丰富了 但这类软件最大的缺点是只能自己家的机器使用 要想其他家也兼容进来 既要
  • 【星海随笔】计组数学小课堂

    计算机组成原理 https www bilibili com video BV1ps4y1d73V p 8 16的负一次方既为1 16 16 1 16进制转换为10进制 例如 5 8 5 16 1 8 16 1 十进制转N进制 则除以N 然
  • Transformers中文本生成方法model.generate()参数解释

    本博客仅作为记录 参考 LLM 大语言模型 解码时是怎么生成文本的 爱码网
  • python字符串中所有符合条件的索引

    使用re库中的finditer import re s 1111ah11111ah test re finditer ah s print i for i in test
  • mvp关联activity生命周期_Android MVP架构从入门到精通-真枪实弹

    Android MVP架构从入门到精通 真枪实弹 一 前言 你是否遇到过Activity Fragment中成百上千行代码 完全无法维护 看着头疼 你是否遇到过因后台接口还未写而你不能先写代码逻辑的情况 你是否遇到过用MVC架构写的项目进行