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

一. 前言

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

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

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

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

二. MVC架构

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

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架构

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显示的内容.

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架构

上面是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,点击则可以进行网络请求.

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. 完整项目地址

五. 参考资料

六. 后续

<> 敬请期待~~~

文章同步发布于:

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

  1. mvp关联activity生命周期_极简SaaS创业手册一文读懂SaaS全生命周期阶段

    与520+全国社友,构建工业互联网认知框架 打通资讯→信息→知识→认知→见识的通路 工业互联网风向观察 今天李宽同学编译一篇文章,通过了解 SaaS 的主要阶段,你可以在每个阶段创建和实现更好的增长战 ...

  2. mvp关联activity生命周期_Rxjava+Retrofit统一异常处理与生命周期管理

    一.引入 9102年了,终于准备用mvp来重构一下了 之前写过Mvc模式下的Retrofit统一异常处理,这次用MVP重构过程中发现诸多不足之处,便重新进行修缮,使其在我看来更加优雅emmmmmm,文 ...

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

    Android MVP架构从入门到精通-真枪实弹 一. 前言 二. MVC架构 1. MVC架构优缺点 A. 缺点 B. 优点 三. MVP架构 1. MVP架构优缺点 A. 缺点 B. 优点 四. ...

  4. android activity生命周期_Android岗高频面试题合一集,看你能答出几题?

    前言 面试季黄金时期来袭,想必许多朋友在这时有找工作.换工作.跳槽涨薪等想法,但你们都有一个相同的过程那就是面试.大家肯定想知道面试时都问哪些面试相关问题,所以就去网上查找题去刷题,为了省去大家找题的 ...

  5. android代理生命周期,了解 Activity 生命周期

    当用户浏览.退出和返回到您的应用时,您应用中的 在生命周期回调方法中,您可以声明用户离开和再次进入 Activity 时 Activity 的行为方式.例如,如果您正构建流媒体视频播放器,当用户切换至 ...

  6. activity生命周期及数据保存

    为什么80%的码农都做不了架构师?>>>    public class ActivityLife extends Activity {// 在Activity生命周期开始时被调用@ ...

  7. Android apk动态加载机制的研究(二):资源加载和activity生命周期管理

    转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/23387079 (来自singwhatiwanna的csdn博客) 前言 为了 ...

  8. Fragment生命周期与宿主Activity生命周期

    当启动activity是,同时启动activity里面的fragment,各生命周期的起调流程 11-22 16:51:22.051 8358-8358/com.txjifei.mapclient.l ...

  9. 安卓学习笔记06:Activity生命周期与启动模式

    文章目录 零.学习目标 一.Activity生命周期 1.了解Activity生命周期 2.Activity生命周期简化图 (1)Activity存在与否 (2)Activity可见与否 (3)Act ...

最新文章

  1. 题解 UVA11354 【Bond】
  2. 网友脑洞大开 用 Emoji 开发“俏皮”编程语言
  3. java怎么统计字符串中各个字母的个数,人生转折!
  4. Java VisualVM插件地址
  5. SpringCloud个人笔记-01-Eureka初体验
  6. fedora 16 mysql远程连接
  7. Java引用类型有哪些
  8. Scala入门到精通——第二十二节 高级类型 (一)
  9. Android之解决卸载app后再次安装提示room数据库错误
  10. 打破冷漠僵局文章_保持冷静并打破僵局-最佳
  11. B - 数字三角形问题
  12. 关于ArcObjects图层编辑的读写编辑锁
  13. CF1151FSonya and Informatics
  14. 计算机硬件现状及发展,计算机硬件的发展历史以及计算机的现状和发展趋势
  15. python堆积图的画法_不再羡慕别人的漂亮图表,一文学会如何用Python绘制堆积图...
  16. 计算机中哪些服务是可以禁止的,win10哪些服务可以禁用?优化win10系统服务的方法...
  17. linux C获取本地IP地址
  18. 和计算机相关的英文名字女孩,最好听的英文名字女孩
  19. Tomcat启动失败 staring tomcat server at localhost has encoutered a problem
  20. Unity内置管线升级URP之色彩空间(伽马、sRGB、Gamma Space和Linear Space)

热门文章

  1. java 核心技术2_你必须掌握的 21 个 Java 核心技术
  2. mysql timestamp add_「timestampdiff」MySQL中TIMESTAMPDIFF和TIMESTAMPADD函数的用法 - seo实验室...
  3. linux内核时钟工作原理,linux内核的时钟机制
  4. python爬虫更改ip_Python小说爬虫,有目录,有文字极简处理,还有IP解锁方式
  5. Winform开发框架中工作流模块的动态处理
  6. Java快速开发平台——JEECG 3.7.8 版本发布!我们的目标是有鱼丸也有粗面
  7. c++-虚函数与多态
  8. JAVA-初步认识-第十四章-多线程(面试题)
  9. 微信小程序与Vue js数据渲染对比
  10. HTML5 云知梦自觉,记录知识 点