版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】androidMVP示例代码分析
这篇文章主要讲解了“androidMVP示例代码分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着在下的思路慢慢深入,一起来研究和学习“androidMVP示例代码分析”吧!上项目结构图:从包名上很容易分辨出功能:addedittask是添加任务,data是数据管理,statistics是统计,taskdetail是任务详情,tasks是任务浏览之类的。事实上这个项目的关键也就是:
Tasks、TaskDetail、AddEditTask、Statistics。这四个关键的地方都有相同之处:定义了view和presenter的契约Activity负责fragment和presenter的创建Fragment实现了view接口presenter实现了presenter接口也就是说,几个功能每一个都是MVP的模式,只不过Model层是公用的。而且这个项目里View层都是Fragment,果然google推荐用Fragment自己的项目里也给我们做个示范……其实关于到底是不是要用Fragment,还是有些争议的,那么到底要不要用呢?我觉得对于个体而言,不管你喜不喜欢,都要用一用,试一试,因为人要成长,必须踩坑。对于正式项目而言,则需要综合考量,使用Fragment的利是否大于弊。扯远了,接下来看一下他代码仓库给的一张结构图:可以看出来左边是数据管理,典型的Model层。而右边呢,你可能认为Activity是Presenter,事实上并不是,Presenter在Activity内,Fragment是View无疑。到这,我觉得关于这个项目结构的简介已经足够了,接下来看代码。我觉得看一个Android项目的正确姿势应该是先把玩一下app,看一下功能。贴几张app的图:接着就该上入口的Activity看一下了,这个项目的入口Activity是TasksActivity,所在的包是tasks,看一下有哪些东西:***个是自定义View,第二个就是入口Activity了,第三个即上面所说的“契约”,里面包含了View接口和Presenter接口。TasksFilterType则是一个枚举,里面有三个过滤类型:所有,进行中的,完成的。TasksFragment就是MVP中的View了,TasksPresenter则是MVP中的Presenter了。看一下TasksActivity中的初始化代码:
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.tasks_act);
Log.e(getClass().getSimpleName(),"onCreate");
//
Set
up
the
toolbar.
Toolbar
toolbar
=
(Toolbar)
findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar
ab
=
getSupportActionBar();
ab.setHomeAsUpIndicator(R.drawable.ic_menu);
ab.setDisplayHomeAsUpEnabled(true);
/**
*
以下的DrawerLayout暂时不看了
*/
//
Set
up
the
navigation
drawer.
mDrawerLayout
=
(DrawerLayout)
findViewById(R.id.drawer_layout);
mDrawerLayout.setStatusBarBackground(R.color.colorPrimaryDark);
NavigationView
navigationView
=
(NavigationView)
findViewById(R.id.nav_view);
if
(navigationView
!=
null)
{
setupDrawerContent(navigationView);
}
//
获取fragment并将之添加到视图上
//
悬浮按钮在这个taksFragment里设置的点击事件
TasksFragment
tasksFragment
=
(TasksFragment)
getSupportFragmentManager().findFragmentById(R.id.contentFrame);
//
getSupportFragmentManager().findFragmentById()
if
(tasksFragment
==
null)
{
//
Create
the
fragment
tasksFragment
=
TasksFragment.newInstance();
//
提供方法帮助activity加载ui
//
这个方法其实就是拿到一个事务,然后把这个fragment
add到对应的id上了
ActivityUtils.addFragmentToActivity(
getSupportFragmentManager(),
tasksFragment,
R.id.contentFrame);
}
//
Create
the
presenter
mTasksPresenter
=
new
TasksPresenter(
IvideTasksRepository(getApplicationContext()),
tasksFragment);
//
Load
previously
saved
state,
if
available.
if
(savedInstanceState
!=
null)
{
TasksFilterType
currentFiltering
=
(TasksFilterType)
savedInstanceState.getSerializable(CURRENT_FILTERING_KEY);
mTasksPresenter.setFiltering(currentFiltering);
}
}首先是初始化toolbar和侧滑,这里不必深入细节,可以跳过这俩。之后初始化fragment和presenter,初始化Fragment先是尝试通过id寻找可能已经存在的Fragment对象,如果没有,则重新创建一个Fragment对象。下一步则是创建一个presenter,***则是让应用在横竖屏状态切换的情况下恢复数据。接下来看一下View和Presenter的“契约”:public
interface
TasksContract
{
interface
View
extends
BaseView<Presenter>
{
void
setLoadingIndicator(boolean
active);
void
showTasks(List<Task>
tasks);
void
showAddTask();
void
showTaskDetailsUi(String
taskId);
void
showTaskMarkedComplete();
void
showTaskMarkedActive();
void
showCompletedTasksCleared();
void
showLoadingTasksError();
void
showNoTasks();
void
showActiveFilterLabel();
void
showCompletedFilterLabel();
void
showAllFilterLabel();
void
showNoActiveTasks();
void
showNoCompletedTasks();
void
showSuccessfullySavedMessage();
boolean
isActive();
void
showFilteringPopUpMenu();
}
interface
Presenter
extends
BasePresenter
{
void
result(int
requestCode,
int
resultCode);
void
loadTasks(boolean
forceUpdate);
void
addNewTask();
void
openTaskDetails(@NonNull
Task
requestedTask);
void
completeTask(@NonNull
Task
completedTask);
void
activateTask(@NonNull
Task
activeTask);
void
clearCompletedTasks();
void
setFiltering(TasksFilterType
requestType);
TasksFilterType
getFiltering();
}
}这个接口里包含了View和Presenter,可以看到View和Presenter里的方法比较多,事实上这是应该的。因为在MVP架构里,View只负责根据Presenter的指示绘制UI,View将所有的用户交互交给Presenter处理。所以Presenter的很多方法可能就是对用户的输入的处理,而有输入必然有输出,View接口定义的各个方法便是给Presenter回调的。Presenter通过回调函数将对用户的输入的处理结果推到View中,View再根据这个结果对UI进行相应的更新。而在此项目中,Fragment就是View,在Fragment的各个点击事件中都调用了Presenter的对应方法,将业务逻辑交给Presenter处理。这看起来比传统的MVC强上很多,因为传统MVC中Activity既可以认为是Controller亦可以认为是View,职责难以分离,写到后面可能一个Activity就有上千行的代码,这会为后续的维护带来不少麻烦。而MVP则将业务逻辑抽取到了Presenter中,作为View的Fragment或者Activity职责更加单一,无疑为后续的开发维护带来了便利。接下来详细的看Presenter的初始化,Presenter的创建是在TasksActivity中完成的,查看其构造函数:public
TasksPresenter(@NonNull
TasksRepository
tasksRepository,
@NonNull
TasksContract.View
tasksView)
{
mTasksRepository
=
checkNotNull(tasksRepository,
"tasksRepository
cannot
be
null");
mTasksView
=
checkNotNull(tasksView,
"tasksView
cannot
be
null!");
mTasksView.setPresenter(this);
}前两个检查传入的参数是否为空,接着将其赋值给TasksPresenter内的引用,调用view的setPresenter方法,将自身传入,这样view中就可以使用presenter对象了,比直接从activity中拿看起来要优雅了不少。Presenter具体的逻辑就不看了,都是一些比较简单的代码,回顾一下打开这个app所发生的事件的流程:创建TasksActivity
->初始化Toolbar->初始化侧滑->创建TasksFragment对象->创建TaskPresenter对象
->给Fragment设置Presenter对象->
初始化Fragment布局,这样一套流程下来,整个流程就理清了,接下来只是等待用户的输入了。接下来要看的是从本文开始到现在都一直忽略了的Model:TasksRepository。不过在分析TasksRepository之前,安利一下这个项目里的实体类,写的比较优雅,我们平时写实体类时***也能按照他的套路来写。我为什么说他写的比较优雅呢?因为各个属性或者是带返回值的方法都打上了@Nullable或者@NoNull注解来说明是否可以为空,事实上空指针这个错可以算是平时经常遇到的错了……不过如果你有良好的设计和编码习惯,是可以避免的,带上这两个注解可以在编译期给你相关的提示。不仅如此,这个实体类还复写了equals()、hashCode()和toString()方法,而且实现的方式也符合规范,关于如何复写这三个方法,在《effective
java》上有很好的总结,各位可以去读一下。/*
*
Copyright
2016,
The
Android
Open
Source
Project
*
*
Licensed
under
the
Apache
License,
Version
2.0
(the
"License");
*
you
may
not
use
this
file
except
in
compliance
with
the
License.
*
You
may
obtain
a
copy
of
the
License
at
*
*
/licenses/LICENSE-2.0
*
*
Unless
required
by
applicable
law
or
agreed
to
in
writing,
software
*
distributed
under
the
License
is
distributed
on
an
"AS
IS"
BASIS,
*
WITHOUT
WARRANTIES
OR
CONDITIONS
OF
ANY
KIND,
either
express
or
implied.
*
See
the
License
for
the
specific
language
governing
permissions
and
*
limitations
under
the
License.
*/
package
com.example.android.architecture.blueprints.todoapp.data;
import
android.support.annotation.NonNull;
import
android.support.annotation.Nullable;
import
mon.base.Objects;
import
mon.base.Strings;
import
java.util.UUID;
/**
*
Immutable
model
class
for
a
Task.
*/
public
final
class
Task
{
@NonNull
private
final
String
mId;
@Nullable
private
final
String
mTitle;
@Nullable
private
final
String
mDescription;
private
final
boolean
mCompleted;
/**
*
Use
this
constructor
to
create
a
new
active
Task.
*
*
@param
title
title
of
the
task
*
@param
description
description
of
the
task
*/
public
Task(@Nullable
String
title,
@Nullable
String
description)
{
this(title,
description,
UUID.randomUUID().toString(),
false);
}
/**
*
Use
this
constructor
to
create
an
active
Task
if
the
Task
already
has
an
id
(copy
of
another
*
Task).
*
*
@param
title
title
of
the
task
*
@param
description
description
of
the
task
*
@param
id
id
of
the
task
*/
public
Task(@Nullable
String
title,
@Nullable
String
description,
@NonNull
String
id)
{
this(title,
description,
id,
false);
}
/**
*
Use
this
constructor
to
create
a
new
completed
Task.
*
*
@param
title
title
of
the
task
*
@param
description
description
of
the
task
*
@param
completed
true
if
the
task
is
completed,
false
if
it's
active
*/
public
Task(@Nullable
String
title,
@Nullable
String
description,
boolean
completed)
{
this(title,
description,
UUID.randomUUID().toString(),
completed);
}
/**
*
Use
this
constructor
to
specify
a
completed
Task
if
the
Task
already
has
an
id
(copy
of
*
another
Task).
*
*
@param
title
title
of
the
task
*
@param
description
description
of
the
task
*
@param
id
id
of
the
task
*
@param
completed
true
if
the
task
is
completed,
false
if
it's
active
*/
public
Task(@Nullable
String
title,
@Nullable
String
description,
@NonNull
String
id,
boolean
completed)
{
mId
=
id;
mTitle
=
title;
mDescription
=
description;
mCompleted
=
completed;
}
@NonNull
public
String
getId()
{
return
mId;
}
@Nullable
public
String
getTitle()
{
return
mTitle;
}
@Nullable
public
String
getTitleForList()
{
if
(!Strings.isNullOrEmpty(mTitle))
{
return
mTitle;
}
else
{
return
mDescription;
}
}
@Nullable
public
String
getDescription()
{
return
mDescription;
}
public
boolean
isCompleted()
{
return
mCompleted;
}
public
boolean
isActive()
{
return
!mCompleted;
}
public
boolean
isEmpty()
{
return
Strings.isNullOrEmpty(mTitle)
&&
Strings.isNullOrEmpty(mDescription);
}
@Override
public
boolean
equals(Object
o)
{
if
(this
==
o)
return
true;
if
(o
==
null
||
getClass()
!=
o.getClass())
return
false;
Task
task
=
(Task)
o;
return
Objects.equal(mId,
task.mId)
&&
Objects.equal(mTitle,
task.mTitle)
&&
Objects.equal(mDescription,
task.mDescription);
}
@Override
public
int
hashCode()
{
return
Objects.hashCode(mId,
mTitle,
mDescription);
}
@Override
public
String
toString()
{
return
"Task
with
title
"
+
mTitle;
}
}先看一下TasksRepository所在的包的结构:可以从包名上看出local是从本地读取数据,remote是远程读取,当然了,这里只是模拟远程读取。本地采用了数据库存取的方式。在TasksRepository(下文简称TR)内部有两个TasksDataSource的引用:private
final
TasksDataSource
mTasksRemoteDataSource;
private
final
TasksDataSource
mTasksLocalDataSource;TasksDataSource是data包内的一个接口,使用接口引用,无非是想解耦,就算以后需求变更,不想采用数据库的方式存储数据,只要实现了这个接口,TR内部的代码也无需变更。TR用了单例,实现方式并不是线程安全的:/**
*
Returns
the
single
instance
of
this
class,
creating
it
if
necessary.
*
*
@param
tasksRemoteDataSource
the
backend
data
source
*
@param
tasksLocalDataSource
the
device
storage
data
source
*
@return
the
{@link
TasksRepository}
instance
*/
public
static
TasksRepository
getInstance(TasksDataSource
tasksRemoteDataSource,
TasksDataSource
tasksLocalDataSource)
{
if
(INSTANCE
==
null)
{
INSTANCE
=
new
TasksRepository(tasksRemoteDataSource,
tasksLocalDataSource);
}
return
INSTANCE;
}说到底,他根本没有线程安全的必要,至少在这个app里,没有并发创建这个对象的场景,所以够用就行了。在TR内部使用了一个LinkedHashMap作为容器来保存Tasks,主要看一下两个方法,首先是存储:public
void
saveTask(@NonNull
Task
task)
{
checkNotNull(task);
mTasksRemoteDataSource.saveTask(task);
mTasksLocalDataSource.saveTask(task);
//
Do
in
memory
cache
update
to
keep
the
app
UI
up
to
date
if
(mCachedTasks
==
null)
{
mCachedTasks
=
new
LinkedHashMap<>();
}
mCachedTasks.put(task.getId(),
task);
}会将传入的task存储到远程数据源和本地数据源(本地数据库)中,然后将这个task传到mCachedTasks(LinkedHashMap)中。代码比较简单,不做更多的分析,接下来看一下读取Task:public
void
getTasks(@NonNull
final
LoadTasksCallback
callback)
{
checkNotNull(callback);
//
Respond
immediately
with
cache
if
available
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 核磁共振科护理工作上半年工作总结
- 生物磁疗环项目成效分析报告
- 废弃食物处理机项目成效分析报告
- 加工猪鬃用工具产品相关项目实施方案
- 纸制外卖食品盒项目成效分析报告
- 动物用牙刷项目成效分析报告
- 串联式混合动力汽车产品相关项目建议书
- 钓鱼用抄网产品相关项目实施方案
- 陆地车辆传动马达产品相关项目实施方案
- 《麒麟操作系统配置管理》课程标准、授课计划
- E-Prime实验设计技术-华南师范大学中国大学mooc课后章节答案期末考试题库2023年
- 测绘安全生产应急预案免费
- 2023年山东春季高考数学试题word版(含答案解析)
- 反恐C-TPAT程序文件整套(通用)
- 医疗器械分类目录(分类)
- 易制爆化学品防盗抢防恐袭应急预案
- 安全有道-决策层领导安全管理培训
- 《大学生劳动教育》全套课件
- 2023-2024学年广西壮族自治区梧州市小学数学五年级下册期末通关测试题
- 《农村电商发展问题研究11000字(论文)》
- Cad实习报告总结3000字范文.doc
评论
0/150
提交评论