【移动应用开发技术】android MVP示例代码分析_第1页
【移动应用开发技术】android MVP示例代码分析_第2页
【移动应用开发技术】android MVP示例代码分析_第3页
【移动应用开发技术】android MVP示例代码分析_第4页
【移动应用开发技术】android MVP示例代码分析_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】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. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论