MVP 谷歌demo小解(三)
来源:互联网 发布:同花顺软件k线图 编辑:程序博客网 时间:2024/05/29 11:01
官方项目地址
https://github.com/googlesamples/android-architecture
项目结构图
Project视图下
- src/main-项目路径
- src/androidTest-UI层测试
- src/androidTestMock-UI层测试mock数据支持
- src/test-业务层单元测试
- src/mock-业务层单元测试mock数据支持
项目包是按功能来划分的,每个包内又有XActivity、XContract、XFragment、XPresenter。其中XContract有View实体和Presenter实体所用到的接口;XFragment是View的实体;XPresenter是Presenter的实体;XActivity做些关联及其他基础操作。
app的build.gradle
因为当前没有对测试做深入探究,所以测试相关,要等后面作深入研究后再做解析
dependencies { // App's dependencies, including test compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion" compile "com.android.support:cardview-v7:$rootProject.supportLibraryVersion" compile "com.android.support:design:$rootProject.supportLibraryVersion" compile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion" compile "com.android.support:support-v4:$rootProject.supportLibraryVersion" compile "com.android.support.test.espresso:espresso-idling-resource:$rootProject.espressoVersion" compile "com.google.guava:guava:$rootProject.guavaVersion" // Dependencies for local unit tests testCompile "junit:junit:$rootProject.ext.junitVersion" testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion" testCompile "org.hamcrest:hamcrest-all:$rootProject.ext.hamcrestVersion" // Android Testing Support Library's runner and rules androidTestCompile "com.android.support.test:runner:$rootProject.ext.runnerVersion" androidTestCompile "com.android.support.test:rules:$rootProject.ext.runnerVersion" // Dependencies for Android unit tests androidTestCompile "junit:junit:$rootProject.ext.junitVersion" androidTestCompile "org.mockito:mockito-core:$rootProject.ext.mockitoVersion" androidTestCompile 'com.google.dexmaker:dexmaker:1.2' androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2' // Espresso UI Testing androidTestCompile "com.android.support.test.espresso:espresso-core:$rootProject.espressoVersion" androidTestCompile ("com.android.support.test.espresso:espresso-contrib:$rootProject.espressoVersion") androidTestCompile "com.android.support.test.espresso:espresso-intents:$rootProject.espressoVersion" // Resolve conflicts between main and test APK: androidTestCompile "com.android.support:support-annotations:$rootProject.supportLibraryVersion" androidTestCompile "com.android.support:support-v4:$rootProject.supportLibraryVersion" androidTestCompile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion"}
工程文件
BaseView.java
public interface BaseView<T> { // view关联presenter void setPresenter(T presenter);}
BasePresenter.java
public interface BasePresenter { //第一次加载数据 void start();}
TasksActivity.java
启动的第一个activity,其实跟MVP相关的就下面代码,其他都是配置菜单栏之类的。当前项目的实现都是,activity中使用FrameLayout控件来放一个fragment作为展示的界面;activity创建presenter并将其与fragment关联。
TasksFragment tasksFragment =(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);if (tasksFragment == null) { // 实例化tasksFragment tasksFragment = TasksFragment.newInstance(); // 将tasksFragment添加到FrameLayout ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), tasksFragment, R.id.contentFrame);}// 实例化presentermTasksPresenter = new TasksPresenter(Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
我们看到先new的TasksView然后是new TasksPresenter,会不会出现TasksView执行onResume时,TasksPresenter还没有赋值呢?做了个测试,验证了下是赋过值的。相关方法执行顺序
TasksContract.java(View层与Presenter层接口)
当前功能的view层和presenter层的接口,TasksFragment.java和TasksPresenter.java都要实现相关的方法。罗列出部分
public interface TasksContract { interface View extends BaseView<Presenter> { //setPresenter()在BaseView中 //当前界面显示tasks void showTasks(List<Task> tasks); //跳转到添加task页 void showAddTask(); } interface Presenter extends BasePresenter { //start()在BasePresenter中 //Fragment的onActivityResult调用 void result(int requestCode, int resultCode); //加载tasks void loadTasks(boolean forceUpdate); //item的点击会调用用的三个方法 void openTaskDetails(@NonNull Task requestedTask); void completeTask(@NonNull Task completedTask); void activateTask(@NonNull Task activeTask); }}
FakeTasksRemoteDataSource.java(model层)
TasksActivity中
//TasksPresenter第一个参数,就是model层的实例mTasksPresenter = new TasksPresenter(Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
TasksRepository单例方式实例化对象
public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,TasksDataSource tasksLocalDataSource) { if (INSTANCE == null) { INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource); } return INSTANCE;}
接口对象中,会用到的几个方法
public interface TasksDataSource { interface LoadTasksCallback { void onTasksLoaded(List<Task> tasks); void onDataNotAvailable(); } void getTasks(@NonNull LoadTasksCallback callback);}
流程
应用启动后,TaskActivity初始化做的事情
环节有所简化(显示/隐藏加载进度框;Model层的强制刷新等),不然流程图占屏幕太大了
如何抽取接口方法?
Model层
与数据库、网络请求有关的操作,如getTasks
void getTasks(@NonNull LoadTasksCallback callback);
Presenter层
界面的初始化界面数据、响应View层的事件
//onActivityResult的事件调用void result(int requestCode, int resultCode);//初始化void loadTasks(boolean forceUpdate);//点击添加新taskvoid addNewTask();//点击task查看详情void openTaskDetails(@NonNull Task requestedTask);
View层
当前项目中,几个比较典型的
这层抽取的细节比较多,所以有些难取舍。
1. 让Presenter能够关联到View,也是基类中的方法
public void setPresenter(@NonNull TasksContract.Presenter presenter);
2. 初始化数据成功后,刷新显示界面;初始化数据成功后,但是tasks长度为0则showNoTasks
public void showTasks(List<Task> tasks);public void showNoTasks();
3. 显示/隐藏正在加载对话框
public void setLoadingIndicator(boolean active);
4. 点击跳转到其他界面,在View调用提供的方法
mNoTaskAddView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showAddTask(); }});@Overridepublic void showAddTask() { Intent intent = new Intent(getContext(), AddEditTaskActivity.class); startActivityForResult(intent, AddEditTaskActivity.REQUEST_ADD_TASK);}
点击跳转到其他界面,在Presenter调用View提供的方法
//TasksPresenter代码@Overridepublic void openTaskDetails(@NonNull Task requestedTask) { checkNotNull(requestedTask, "requestedTask cannot be null!"); mTasksView.showTaskDetailsUi(requestedTask.getId());}//TasksFragment代码TaskItemListener mItemListener = new TaskItemListener() { @Override public void onTaskClick(Task clickedTask) { mPresenter.openTaskDetails(clickedTask); }};@Overridepublic void showTaskDetailsUi(String taskId) { // in it's own Activity, since it makes more sense that way and it gives us the flexibility // to show some Intent stubbing. Intent intent = new Intent(getContext(), TaskDetailActivity.class); intent.putExtra(TaskDetailActivity.EXTRA_TASK_ID, taskId); startActivity(intent);}
两种方式均可,但个人更偏向于第一种。点击事件当前View处理即可
5. onActivityResult方法
@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) { mPresenter.result(requestCode, resultCode);}
6. Toast提示信息,也要抽取成方法。方便View界面调用
private void showMessage(String message);
7. ListView或Adapter中如果需要使用Presenter层实例方法时,以接口回调形式实现
/** * Listener for clicks on tasks in the ListView. */TaskItemListener mItemListener = new TaskItemListener() { @Override public void onTaskClick(Task clickedTask) { mPresenter.openTaskDetails(clickedTask); } @Override public void onCompleteTaskClick(Task completedTask) { mPresenter.completeTask(completedTask); } @Override public void onActivateTaskClick(Task activatedTask) { mPresenter.activateTask(activatedTask); }};private static class TasksAdapter extends BaseAdapter { private List<Task> mTasks; private TaskItemListener mItemListener; public TasksAdapter(List<Task> tasks, TaskItemListener itemListener) { setList(tasks); mItemListener = itemListener; }}
其他的都类比着来就可以了。
其他项目中我抽取的
1. 设置title栏的值;设置title栏按钮的文字
@Overridepublic void setUiTitle(int titleId){ mTvTitle.setText(titleId);}@Overridepublic void setRightBtText(int rightBtTextId){ mTvRight.setText(rightBtTextId);}
2. 添加点击事件当Presenter实例化的时候调用;处理点击事件
//添加点击事件的接口public void initOnClickListener();//View中处理点击事件public void onClick(View v) { switch (v.getId()) { case R.id.tv_right://点击添加分类 mGoodsKindPresenter.addKind(); break; }}
3. 添加文本监听,当Presenter实例化的时候调用initTextChangedListener,添加文本监听事件
/** * 添加文本改变的监听 */@Overridepublic void initTextChangedListener();
4. 获取activity的context参数
public void Context getContext();
5. 界面弹出框及内容的点击事件监听
/** * 加载类别弹出框ui * @param parentList * @param childrenList */void showDialogSelectedKinds(List<KindDomain> parentList, List<KindDomain> childrenList);/** * 选取父分类 * @param domain */void dialogOnItemClick(KindDomain domain);
我就罗列这么多吧,其他的自己琢磨吧!
View和Model之间的耦合度降低,使其更关注自身业务逻辑,结构清晰,维护方便。这样也便于单元测试,代码框架更适用于快速迭代开发。但也有缺点就是类很多
项目效果图
- MVP 谷歌demo小解(三)
- ARC小解(三)
- protocol小解(三)之代理设计
- 三值逻辑小解
- Android MVP架构学习(附demo)
- otter(三)--同步过程小解
- MVP简单demo
- [Asp.Net]MVP Demo
- android mvp demo
- Android MVP Demo
- MVP框架实战Demo
- MVP模式Demo
- 初识MVP,简单Demo
- MVP入门Demo
- DEMO:通讯录(三)
- Android开发--MVP demo+Jsoup在线小说阅读器(一)
- iOS架构模式MVC、MVP、MVVM(内附demo)
- 使用MVP+Retrofit+RxJava实现的的Android Demo (上)使用Nuclues库实现MVP
- jquery源码解析---理解观察者模式
- 一些iOS开发中会用到的英文术语(一)
- Android 如何检索Android设备的唯一ID
- 《CSS揭秘》案例
- thinkphp实现定位功能
- MVP 谷歌demo小解(三)
- 在windows风格下设置JButton的背景色
- JavaSE学习_13_list_set_map
- centos 安装AMQP扩展的方法和步骤
- 抽象类和接口理解
- SpringMVC 开发接口
- 特约商户, 超多福利
- Android-LayoutInflater分析
- Printer