什么是android mvp模式
关于android程序的构架, 当前最流行的模式即为mvp模式, google官方提供了sample代码来展示这种模式的用法。
repo地址: android-architecture.
本文为阅读官方sample代码的阅读笔记和分析。
官方android architecture blueprints [beta]:
android在如何组织和构架一个app方面提供了很大的灵活性, 但是同时这种自由也可能会导致app在测试, 维护, 扩展方面变得困难。
android architecture blueprints展示了可能的解决方案。 在这个项目里, 我们用各种不同的构架概念和工具实现了同一个应用(to do app)。 主要的关注点在于代码结构, 构架, 测试和维护性。
但是请记住, 用这些模式构架app的方式有很多种, 要根据你的需要, 不要把这些当做绝对的典范。
mvp模式 概念
之前有一个mvc模式: model-view-controller.
mvc模式 有两个主要的缺点: 首先, view持有controller和model的引用; 第二, 它没有把对ui逻辑的操作限制在单一的类里, 这个职能被controller和view或者model共享。
所以后来提出了mvp模式来克服这些缺点。
mvp(model-view-presenter)模式:
model: 数据层。 负责与网络层和数据库层的逻辑交互。
view: ui层。 显示数据, 并向presenter报告用户行为。
presenter: 从model拿数据, 应用到ui层, 管理ui的状态, 决定要显示什么, 响应用户的行为。
mvp模式的最主要优势就是耦合降低, presenter变为纯java的代码逻辑, 不再与android framework中的类如activity, fragment等关联, 便于写单元测试。
todo-mvp 基本的model-view-presenter架构
app中有四个功能:
tasks
taskdetail
addedittask
statistics
每个功能都有:
一个定义view和presenter接口的contract接口;
一个activity用来管理fragment和presenter的创建;
一个实现了view接口的fragment;
一个实现了presenter接口的presenter.
mvp
基类
presenter基类:
public interface basepresenter {
void start();
}
例子中这个start()方法都在fragment的onresume()中调用。
view基类:
public interface baseview《t》 {
void setpresenter(t presenter);
}
view实现
fragment作为每一个view接口的实现, 主要负责数据显示和在用户交互时调用presenter, 但是例子代码中也是有一些直接操作的部分, 比如点击开启另一个activity, 点击弹出菜单(菜单项的点击仍然是调用presenter的方法)。
view接口中定义的方法多为showxxx()方法。
fragment作为view实现, 接口中定义了方法:
@override
public boolean isactive() {
return isadded();
}
在presenter中数据回调的方法中, 先检查view.isactive()是否为true, 来保证对fragment的操作安全。
presenter实现
presenter的start()方法在onresume()的时候调用, 这时候取初始数据; 其他方法均对应于用户在ui上的交互操作。
new presenter的操作是在每一个activity的oncreate()里做的: 先添加了fragment(view), 然后把它作为参数传给了presenter. 这里并没有存presenter的引用。
presenter的构造函数有两个参数, 一个是model(model类一般叫xxxrepository), 一个是view. 构造中先用guava的checknotnull()
检查两个参数是否为null, 然后赋值到字段; 之后再调用view的setpresenter()方法把presenter传回view中引用。
model实现细节
model只有一个类, 即tasksrepository. 它还是一个单例。 因为在这个应用的例子中, 我们操作的数据就这一份。
它由手动实现的注入类injection类提供:
public class injection {
public static tasksrepository providetasksrepository(@nonnull context context) {
checknotnull(context);
return tasksrepository.getinstance(faketasksremotedatasource.getinstance(),
taskslocaldatasource.getinstance(context));
}
}
构造如下:
private tasksrepository(@nonnull tasksdatasource tasksremotedatasource,
@nonnull tasksdatasource taskslocaldatasource) {
mtasksremotedatasource = checknotnull(tasksremotedatasource);
mtaskslocaldatasource = checknotnull(taskslocaldatasource);
}
数据分为local和remote两大部分。 local部分负责数据库的操作, remote部分负责网络。 model类中还有一个内存缓存。
tasksdatasource是一个接口。 接口中定义了presenter查询数据的回调接口, 还有一些增删改查的方法。
单元测试
mvp模式的主要优势就是便于为业务逻辑加上单元测试。
本例子中的单元测试是给tasksrepository和四个feature的presenter加的。
presenter的单元测试, mock了view和model, 测试调用逻辑, 如:
public class addedittaskpresentertest {
@mock
private tasksrepository mtasksrepository;
@mock
private addedittaskcontract.view maddedittaskview;
private addedittaskpresenter maddedittaskpresenter;
@before
public void setupmocksandview() {
mockitoannotations.initmocks(this);
when(maddedittaskview.isactive()).thenreturn(true);
}
@test
public void savenewtasktorepository_showssuccessmessageui() {
maddedittaskpresenter = new addedittaskpresenter(“1”, mtasksrepository, maddedittaskview);
maddedittaskpresenter.savetask(“new task title”, “some task description”);
verify(mtasksrepository).savetask(any(task.class)); // saved to the model
verify(maddedittaskview).showtaskslist(); // shown in the ui
}
。。。
}
todo-mvp-loaders 用loader取数据的mvp
基于上一个例子todo-mvp, 只不过这里改为用loader来从repository得到数据。
todo-mvp-loaders
使用loader的优势:
去掉了回调, 自动实现数据的异步加载;
当内容改变时回调出新数据;
当应用因为configuration变化而重建loader时, 自动重连到上一个loader.
diff with todo-mvp
既然是基于todo-mvp, 那么之前说过的那些就不再重复, 我们来看一下都有什么改动:
git difftool -d todo-mvp
添加了两个类:
taskloader和tasksloader.
在activity中new loader类, 然后传入presenter的构造方法。
contract中view接口删掉了isactive()方法, presenter删掉了populatetask()方法。
数据获取
添加的两个新类是taskloader和tasksloader, 都继承于asynctaskloader, 只不过数据的类型一个是单数, 一个是复数。
asynctaskloader是基于modernasynctask, 类似于asynctask,
把load数据的操作放在loadinbackground()里即可, deliverresult()方法会将结果返回到主线程, 我们在listener的onloadfinished()里面就可以接到返回的数据了, (在这个例子中是几个presenter实现了这个接口)。
tasksdatasource接口的这两个方法:
list《task》 gettasks();
task gettask(@nonnull string taskid);
都变成了同步方法, 因为它们是在loadinbackground()方法里被调用。
presenter中保存了loader和loadermanager, 在start()方法里initloader, 然后oncreateloader返回构造传入的那个loader.
onloadfinished()里面调用view的方法。 此时presenter实现loadermanager.loadercallbacks.
数据改变监听
tasksrepository类中定义了observer的接口, 保存了一个listener的list:
private list《tasksrepositoryobserver》 mobservers = new arraylist《tasksrepositoryobserver》();
public interface tasksrepositoryobserver {
void ontaskschanged();
}
每次有数据改动需要刷新ui时就调用:
private void notifycontentobserver() {
for (tasksrepositoryobserver observer : mobservers) {
observer.ontaskschanged();
}
}
在两个loader里注册和注销自己为tasksrepository的listener: 在onstartloading()里add, onreset()里面remove方法。
这样每次tasksrepository有数据变化, 作为listener的两个loader都会收到通知, 然后force load:
@override
public void ontaskschanged() {
if (isstarted()) {
forceload();
}
}
这样onloadfinished()方法就会被调用。
todo-databinding
基于todo-mvp, 使用data binding library来显示数据, 把ui和动作绑定起来。
说到viewmodel, 还有一种模式叫mvvm(model-view-viewmodel)模式。
这个例子并没有严格地遵循model-view-viewmodel模式或者model-view-presenter模式, 因为它既用了viewmodel又用了presenter.
mvp-databinding
data binding library让ui元素和数据模型绑定:
layout文件用来绑定数据和ui元素;
事件和action handler绑定;
数据变为可观察的, 需要的时候可以自动更新。
diff with todo-mvp
添加了几个类:
statisticsviewmodel;
swiperefreshlayoutdatabinding;
tasksitemactionhandler;
tasksviewmodel;
从几个view的接口可以看出方法数减少了, 原来需要多个showxxx()方法, 现在只需要一两个方法就可以了。
数据绑定
以tasksdetailfragment为例:
以前在todo-mvp里需要这样:
public void oncreateview(。。。) {
。。。
mdetaildescription = (textview)
root.findviewbyid(r.id.task_detail_description);
}
@override
public void showdescription(string description) {
mdetaildescription.setvisibility(view.visible);
mdetaildescription.settext(description);
}
现在只需要这样:
public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) {
view view = inflater.inflate(r.layout.taskdetail_frag, container, false);
mviewdatabinding = taskdetailfragbinding.bind(view);
。。。
}
@override
public void showtask(task task) {
mviewdatabinding.settask(task);
}
因为所有数据绑定的操作都写在了xml里:
《textview
android:id=“@+id/task_detail_description”
。。。
android:text=“@{task.description}” /》
事件绑定
数据绑定省去了findviewbyid()和settext(), 事件绑定则是省去了setonclicklistener()。
比如taskdetail_frag.xml中的
《checkbox
android:id=“@+id/task_detail_complete”
。。。
android:checked=“@{task.completed}”
android:oncheckedchanged=“@{(cb, ischecked) -》
presenter.completechanged(task, ischecked)}” /》
其中presenter是这时候传入的:
@override
public void onactivitycreated(bundle savedinstancestate) {
super.onactivitycreated(savedinstancestate);
mviewdatabinding.setpresenter(mpresenter);
}
数据监听
在显示list数据的界面tasksfragment, 仅需要知道数据是否为空, 所以它使用了tasksviewmodel来给layout提供信息, 当尺寸设定的时候, 只有一些相关的属性被通知, 和这些属性绑定的ui元素被更新。
public void settasklistsize(int tasklistsize) {
mtasklistsize = tasklistsize;
notifypropertychanged(br.notaskiconres);
notifypropertychanged(br.notaskslabel);
notifypropertychanged(br.currentfilteringlabel);
notifypropertychanged(br.notempty);
notifypropertychanged(br.tasksaddviewvisible);
}
其他实现细节
adapter中的data binding, 见tasksfragment中的tasksadapter.
@override
public view getview(int i, view view, viewgroup viewgroup) {
task task = getitem(i);
taskitembinding binding;
if (view == null) {
// inflate
layoutinflater inflater = layoutinflater.from(viewgroup.getcontext());
// create the binding
binding = taskitembinding.inflate(inflater, viewgroup, false);
} else {
binding = databindingutil.getbinding(view);
}
// we might be recycling the binding for another task, so update it.
// create the action handler for the view
tasksitemactionhandler itemactionhandler =
new tasksitemactionhandler(museractionslistener);
binding.setactionhandler(itemactionhandler);
binding.settask(task);
binding.executependingbindings();
return binding.getroot();
}
presenter可能会被包在actionhandler中, 比如tasksitemactionhandler.
viewmodel也可以作为view接口的实现, 比如statisticsviewmodel.
swiperefreshlayoutdatabinding类定义的onrefresh()动作绑定。
todo-mvp-clean
这个例子是基于clean architecture的原则:
the clean architecture.
关于clean architecture, 还可以看这个sample app: android-cleanarchitecture.
这个例子在todo-mvp的基础上, 加了一层domain层, 把应用分为了三层:
mvp-clean.png
domain: 盛放了业务逻辑, domain层包含use cases或者interactors, 被应用的presenters使用。 这些use cases代表了所有从presentation层可能进行的行为。
关键概念
和基本的mvp sample最大的不同就是domain层和use cases. 从presenters中抽离出来的domain层有助于避免presenter中的代码重复。
use cases定义了app需要的操作, 这样增加了代码的可读性, 因为类名反映了目的。
use cases对于操作的复用来说也很好。 比如completetask在两个presenter中都用到了。
use cases的执行是在后台线程, 使用command pattern. 这样domain层对于android sdk和其他第三方库来说都是完全解耦的。
diff with todo-mvp
每一个feature的包下都新增了domain层, 里面包含了子目录model和usecase等。
usecase是一个抽象类, 定义了domain层的基础接口点。
usecasehandler用于执行use cases, 是一个单例, 实现了command pattern.
usecasethreadpoolscheduler实现了usecasescheduler接口, 定义了use cases执行的线程池, 在后台线程异步执行, 最后把结果返回给主线程。
usecasescheduler通过构造传给usecasehandler.
测试中用了usecasescheduler的另一个实现testusecasescheduler, 所有的执行变为同步的。
injection类中提供了多个use cases的依赖注入, 还有usecasehandler用来执行use cases.
presenter的实现中, 多个use cases和ussecasehandler都由构造传入, 执行动作, 比如更新一个task:
private void updatetask(string title, string description) {
if (mtaskid == null) {
throw new runtimeexception(“updatetask() was called but task is new.”);
}
task newtask = new task(title, description, mtaskid);
musecasehandler.execute(msavetask, new savetask.requestvalues(newtask),
new usecase.usecasecallback《savetask.responsevalue》() {
@override
public void onsuccess(savetask.responsevalue response) {
// after an edit, go back to the list.
maddtaskview.showtaskslist();
}
@override
public void onerror() {
showsaveerror();
}
});
}
todo-mvp-dagger
关键概念:
dagger2 是一个静态的编译期依赖注入框架。
这个例子中改用dagger2实现依赖注入。 这样做的主要好处就是在测试的时候我们可以用替代的modules. 这在编译期间通过flavors就可以完成, 或者在运行期间使用一些调试面板来设置。
diff with todo-mvp
injection类被删除了。
添加了5个component, 四个feature各有一个, 另外数据对应一个: tasksrepositorycomponent, 这个component被保存在application里。
数据的module: tasksrepositorymodule在mock和prod目录下各有一个。
对于每一个feature的presenter的注入是这样实现的:
首先, 把presenter的构造函数标记为@inject, 然后在activity中构造component并注入到字段:
@inject addedittaskpresenter maddedittaskspresenter;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.addtask_act);
。。。。。
// create the presenter
daggeraddedittaskcomponent.builder()
.addedittaskpresentermodule(
new addedittaskpresentermodule(addedittaskfragment, taskid))
.tasksrepositorycomponent(
((todoapplication) getapplication()).gettasksrepositorycomponent()).build()
.inject(this);
}
这个module里provide了view和taskid:
@module
public class addedittaskpresentermodule {
private final addedittaskcontract.view mview;
private string mtaskid;
public addedittaskpresentermodule(addedittaskcontract.view view, @nullable string taskid) {
mview = view;
mtaskid = taskid;
}
@provides
addedittaskcontract.view provideaddedittaskcontractview() {
return mview;
}
@provides
@nullable
string providetaskid() {
return mtaskid;
}
}
注意原来构造方法里调用的setpresenter方法改为用方法注入实现:
/**
* method injection is used here to safely reference {@code this} after the object is created.
* for more information, see java concurrency in practice.
*/
@inject
void setuplisteners() {
maddtaskview.setpresenter(this);
}
todo-mvp-contentproviders
这个例子是基于todo-mvp-loaders的, 用content provider来获取repository中的数据。
mvp-contentproviders
使用content provider的优势是:
管理了结构化数据的访问;
content provider是跨进程访问数据的标准接口。
diff with todo-mvp-loaders
注意这个例子是唯一一个不基于最基本的todo-mvp, 而是基于todo-mvp-loaders. (但是我觉得也可以认为是直接从todo-mvp转化的。)
看diff: git difftool -d todo-mvp-loaders.
去掉了taskloader和tasksloader. (回归到了基本的todo-mvp)。
tasksrepository中的方法不是同步方法, 而是异步加callback的形式。 (回归到了基本的todo-mvp)。
taskslocaldatasource中的读方法都变成了空实现, 因为presenter现在可以自动收到数据更新。
新增loaderprovider用来创建cursor loaders, 有两个方法:
// 返回特定fiter下或全部的数据
public loader《cursor》 createfilteredtasksloader(taskfilter taskfilter)
// 返回特定id的数据
public loader《cursor》 createtaskloader(string taskid)
其中第一个方法的参数taskfilter, 用来指定过滤的selection条件, 也是新增类。
loadermanager和loaderprovider都是由构造传入presenter, 在回调ontaskloaded()和ontasksloaded()中init loader.
在taskspresenter中还做了判断, 是init loader还是restart loader:
@override
public void ontasksloaded(list《task》 tasks) {
// we don‘t care about the result since the cursorloader will load the data for us
if (mloadermanager.getloader(tasks_loader) == null) {
mloadermanager.initloader(tasks_loader, mcurrentfiltering.getfilterextras(), this);
} else {
mloadermanager.restartloader(tasks_loader, mcurrentfiltering.getfilterextras(), this);
}
}
其中initloader()和restartloader()时传入的第二个参数是一个bundle, 用来指明过滤类型, 即是带selection条件的数据库查询。
同样是在onloadfinshed()的时候做view处理, 以taskdetailpresenter为例:
@override
public void onloadfinished(loader《cursor》 loader, cursor data) {
if (data != null) {
if (data.movetolast()) {
ondataloaded(data);
} else {
ondataempty();
}
} else {
ondatanotavailable();
}
}
数据类task中新增了静态方法从cursor转为task, 这个方法在presenter的onloadfinished()和测试中都用到了。
public static task from(cursor cursor) {
string entryid = cursor.getstring(cursor.getcolumnindexorthrow(
taskspersistencecontract.taskentry.column_name_entry_id));
string title = cursor.getstring(cursor.getcolumnindexorthrow(
taskspersistencecontract.taskentry.column_name_title));
string description = cursor.getstring(cursor.getcolumnindexorthrow(
taskspersistencecontract.taskentry.column_name_description));
boolean completed = cursor.getint(cursor.getcolumnindexorthrow(
taskspersistencecontract.taskentry.column_name_completed)) == 1;
return new task(title, description, entryid, completed);
}
另外一些细节:
数据库中的内存cache被删了。
adapter改为继承于cursoradapter.
单元测试
新增了mockcursorprovider类, 用于在单元测试中提供数据。
其内部类taskmockcursor mock了cursor数据。
presenter的测试中仍然mock了所有构造传入的参数, 然后准备了mock数据, 测试的逻辑主要还是拿到数据后的view操作, 比如:
@test
public void loadalltasksfromrepositoryandloadintoview() {
// when the loader finishes with tasks and filter is set to all
when(mbundle.getserializable(taskfilter.key_task_filter)).thenreturn(tasksfiltertype.all_tasks);
taskfilter taskfilter = new taskfilter(mbundle);
mtaskspresenter.setfiltering(taskfilter);
mtaskspresenter.onloadfinished(mock(loader.class), malltaskscursor);
// then progress indicator is hidden and all tasks are shown in ui
verify(mtasksview).setloadingindicator(false);
verify(mtasksview).showtasks(mshowtasksargumentcaptor.capture());
}
todo-mvp-rxjava
关于这个例子, 之前看过作者的文章: android architecture patterns part 2:
model-view-presenter,
这个文章上过android weekly issue #226.
这个例子也是基于todo-mvp, 使用rxjava处理了presenter和数据层之间的通信。
mvp基本接口改变
basepresenter接口改为:
public interface basepresenter {
void subscribe();
void unsubscribe();
}
view在onresume()的时候调用presenter的subscribe(); 在onpause()的时候调用presenter的unsubscribe()。
如果view接口的实现不是fragment或activity, 而是android的自定义view, 那么在android view的onattachedtowindow()和ondetachedfromwindow()方法里分别调用这两个方法。
presenter中保存了:
private compositesubscription msubscriptions;
在subscribe()的时候, msubscriptions.add(subscription);;
在unsubscribe()的时候, msubscriptions.clear(); 。
diff with todo-mvp
数据层暴露了rxjava的observable流作为获取数据的方式, tasksdatasource接口中的方法变成了这样:
observable《list《task》》 gettasks();
observable《task》 gettask(@nonnull string taskid);
callback接口被删了, 因为不需要了。
taskslocaldatasource中的实现用了sqlbrite, 从数据库中查询出来的结果很容易地变成了流:
@override
public observable《list《task》》 gettasks() {
。。。
return mdatabasehelper.createquery(taskentry.table_name, sql)
.maptolist(mtaskmapperfunction);
}
tasksrepository中整合了local和remote的data, 最后把observable返回给消费者(presenters和unit tests)。 这里用了.concat()和.first()操作符。
presenter订阅tasksrepository的observable, 然后决定view的操作, 而且presenter也负责线程的调度。
简单的比如addedittaskpresenter中:
@override
public void populatetask() {
if (mtaskid == null) {
throw new runtimeexception(“populatetask() was called but task is new.”);
}
subscription subscription = mtasksrepository
.gettask(mtaskid)
.subscribeon(mschedulerprovider.computation())
.observeon(mschedulerprovider.ui())
.subscribe(new observer《task》() {
@override
public void oncompleted() {
}
@override
public void onerror(throwable e) {
if (maddtaskview.isactive()) {
maddtaskview.showemptytaskerror();
}
}
@override
public void onnext(task task) {
if (maddtaskview.isactive()) {
maddtaskview.settitle(task.gettitle());
maddtaskview.setdescription(task.getdescription());
}
}
});
msubscriptions.add(subscription);
}
statisticspresenter负责统计数据的显示, taskspresenter负责过滤显示所有数据, 里面的rxjava操作符运用比较多, 可以看到链式操作的特点。
关于线程调度, 定义了baseschedulerprovider接口, 通过构造函数传给presenter, 然后实现用schedulerprovider, 测试用immediateschedulerprovider. 这样方便测试。
android mvp模式有什么弊端
才开始学习使用mvp时,看到大家说了很多mvp的优点,代码复用,条理清晰等等。不过我改下来发现,mvp在我看来,最大的优点还是代码解耦,逻辑清晰,至于代码复用,暂时没有感觉很好用,除非是界面和逻辑基本一样的,不然想要复用,其实不太现实。
mvp的优点很明显,缺点其实也很明显,明显项目会多出许多类,增加了项目的复杂程度,而且像某些逻辑及其简单,事件较少的界面,使用mvp实际上反而是累赘,明明用mvc也就几十行代码的事,改成mvp多了好多个类,反而感觉不划算,改需求时又要翻阅好多个类。因此,我建议大家,如果你的某个界面极其简单,其实就不要用mvp了,mvp是逻辑越复杂,优势越明显,逻辑简单时,反而不如mvc好用,希望大家不要为了用mvp而用mvp。
下面来谈谈文章主题,mvp的优化问题,最开始采用网上大家的写法,发现代码的复用性不好,有些逻辑类似的代码,基本上每个presenter 和model都要重新写,于是想到使用base类的方法,把某些共有的方法抽离以达到代码的复用性,类似于baseactivity。
举个例子比如网络请求,在mvc中通常是把网络请求封装在baseactivity中,不过既然是mvp,网络请求自然应该封装在model里面啦
public abstract class baseactivitymodel implements ipublicmodel {
//网络连接模式,当一个页面含有多个网络请求时,通过传入不同的模式,选择相应的加载参数
public static final int mode_one=1;
public static final int mode_two=2;
public static final int mode_three=3;
//网络连接工具接口类
protected internetconnect mconnect;
/**
* @param mode 请求模式
* @param intent 上个页面传递过来的intent
* @param i 请求回调
* @param parameter 请求的一些参数
*/
@override
public void requestdata (int mode, intent intent, jsoni i, string.。。 parameter) {
hashmap《string, string》 map = new hashmap《》();
jsonbean.payload payload=new jsonbean.payload();
mconnect.loadparameter(intent,mode,payload,map,parameter);//加载参数,由子类实现
map.put(“payload”, volleyconnect.getgson().tojson(payload));
volleyconnect.getinvolleyconnect().getservicemsg( map,i);//封装volley,传入参数以及回调接口
}
/**
* 设置网络请求
*/
@override
public void setmconnect (internetconnect mconnect) {
this.mconnect=mconnect;
}
}
同样的共有的方法和字段抽象出presenter的基类
public abstract class baseactivitypresenter《t extends ipublicview, e extends ipublicmodel》 implements ipublicpresenter {
protected t view;
protected e model;
protected requestresult mrequestresult;
protected handler mhandler;
public baseactivitypresenter (t view) {
this.view = view;
type type = getclass().getgenericsuperclass();//使用反射实例化model
type truetype = ((parameterizedtype) type).getactualtypearguments()[1];
try {
this.model = ((class《e》) truetype).newinstance();
} catch (instantiationexception e) {
e.printstacktrace();
} catch (illegalaccessexception e) {
e.printstacktrace();
}
new timecount(200, 50, new itimecount() {
@override
public void isruning (long millisuntilfinished) {
}
@override
public void isfinish () {
init();//加载子类方法,延时200毫秒加载
}
}).start();
}
/*
设置网络请求回调
*/
public void setrequestresult (requestresult requestresult) {
mrequestresult = requestresult;
}
/*
获取view的handler,需要传入一个回调接口
*/
public void sethandler (ihandler handleri) {
mhandler = view.exposehandler(handleri);
}
@override
public void requestdata (final int mode, string.。。 parameter) {
view.setloading(true);
model.requestdata(mode, view.exposeintent(), new jsoni() {
@override
public void notice (jsonbean bean) {
// if (bean.getstatus().equals(“0”)) {
// mrequestresult.requestdatasuccess(mode,bean);
// }else{
// mrequestresult.requestdatafail(mode,bean);
// }
view.setloading(false);
}
@override
public void notice (int error) {
view.showerror(error);
}
}, parameter);
}
}
这样我们就可以更加简单方便的使用mvp模式了,下面是使用示例
public class loginpresenter extends baseactivitypresenter《iloginview,loginmodel》 implements iloginpresenter, requestresult {
public loginpresenter (iloginview view) {
super(view);
}
@override
public void init () {
setrequestresult(this);
}
@override
public void requestdatasuccess (int mode, jsonbean bean) {
}
@override
public void requestdatafail (int mode, jsonbean bean) {
}
}
可以看到,loginpresenter不再需要去写model字段和网络请求逻辑,通过泛型,可以自动创建model,而网络请求,仅仅需要设置对应的回调就可以哒。
总结,这样做进一步降低了代码耦合,方便以后代码维护,而且整个mvp感觉更加简单。
机械硬盘坏了能修吗
遥控电路图(照明灯多路红外遥控电路/红外遥控开关电路/航模遥控开关电路)
TL084各引脚功能及电压
重磅报告:物联网时代,哪些行业将被颠覆?
Galaxy M02规格和功能
什么是android mvp模式,android mvp模式有什么弊端
华为荣耀升级版T8950京东首发_华为t8950怎么样
气动角座阀的维修方法
零电流零电压开关交错并联双管正激变换器的研究
高效RF功率放大器面临的大功耗的问题
EDGE/GSM四种手机发送电路的架构详解
发三款智能新品 联想NBD新板凳平台发布
嵌入式WinXP系统技术支持最长可到2019年
“中国制造业数字化转型的全景图”主题演讲
微软重构Win10任务栏:不激活系统的用户将面临更多功能受限
新5G为旧互联网数据赋能
润和软件HopeStage与上海瑞美云LIS系统管理软件完成产品兼容性互认证
关于FPGA它的整体结构是怎样的
音乐集成电路在使用中应注意事项
华为Mate 8使用了那些海思芯片?