简书链接:学习googlesampledemo之安卓架构mvp的正确姿势以及个人分析源码心得
文章字数:1630,阅读全文大约需要6分钟
谈到mvp,我先说说我以前是如何封装刷新 翻页等等的吧.

另外我在没研究mvp之前写的刷新可控制是否加载更多是查询是否是更多 以及需要创建的适配器等等虽然不是接口,但是感觉也实现了一个刷新逻辑的共用
创建一个p,然后把操作接口传递进去传递进去 ,再activity的onCreate() new出这个p 然后activity实现接口方法 接口方法也包含了模型的查询,数据的查询也是在p层操作,但是具体请求地址是啥,地址成功之后的数据类型结果的返回还是通过申明的接口,
直接附上代码吧,各位说说这都叫什么模式,这都是我想出来的共用。
mvp把是完全的接口了,我这mvp又不像mvc也不像,传说中的4不像?但是我的确解决了不同类型无法改变的结类的 如activity fragment 共用一个逻辑,只需要复写这些重新定义即可,但是内部的核心还是那些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

public abstract class BaseJSONRefreshActivityN<ADAPTER extends RecyclerView.Adapter> extends BaseActionBarActivity implements BaseJSONRefreshLogicI<ADAPTER> {

public JSONRefreshRefreshWrap<ADAPTER> getRefreshWrap() {
return refreshWrap;
}

private JSONRefreshRefreshWrap<ADAPTER> refreshWrap;

@Override
protected final void init(Bundle savedInstanceState) {
refreshWrap = new JSONRefreshRefreshWrap<ADAPTER>() {
@Override
protected List parseJsonResult(String json) {
return BaseJSONRefreshActivityN.this.parseJsonResult(json);
}

@Override
public RecyclerView getRecyclerView() {

return BaseJSONRefreshActivityN.this.getRecyclerView();
}


public String getInterceptEmptyDataTip(){
return BaseJSONRefreshActivityN.this.getInterceptEmptyDataTip();
}
public boolean isInterceptEmptyData(){
return BaseJSONRefreshActivityN.this.isInterceptEmptyData();
}


@Override
public void onParseSucc(List list) {
BaseJSONRefreshActivityN.this.onParseSucc(list);
}


@Override
public SmartRefreshLayout getSwipyRefreshLayout() {
return (SmartRefreshLayout) BaseJSONRefreshActivityN.this.getSwipyRefreshLayout();
}

@Override
public void onInitStart() {
BaseJSONRefreshActivityN.this.onInitStart();
}

@Override
public void onInitFinish() {
BaseJSONRefreshActivityN.this.onInitFinish();
}

@Override
public RecyclerView.LayoutManager onCreateLayoutManager() {
return BaseJSONRefreshActivityN.this.onCreateLayoutManager();
}

@Override
public ADAPTER onCreateAdapter() {
return BaseJSONRefreshActivityN.this.onCreateAdapter();
}

@Override
public String getUrl(int page) {
return BaseJSONRefreshActivityN.this.getUrl(page);
}

@Override
public boolean autoLoad() {
return BaseJSONRefreshActivityN.this.autoLoad();
}

@Override
public boolean enableLoadMore() {
return BaseJSONRefreshActivityN.this.enableLoadMore();

}

@Override
protected boolean needEmptyView() {
return BaseJSONRefreshActivityN.this.needEmptyView();
}
};
refreshWrap.init();
}

}

我突然觉得,我这里应该直接 把activity 的BaseJSONRefreshLogicI传递过去,而不是通过复写匿名类的方法接控制外部类,不过坏处就是所有逻辑都要复写,我这里只是针对性的复写一些。

另外,我这叫啥子写法,是一套解决点赞 等+1 -1的封装

1
2
3
4
5
6
public interface IOperaAction {
public int getAction();

public void setAction(int isfollow);//
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
   public static void addAction(Activity activity, final String function, String addUrl, String deleteUrl, final IOperaAction action) {

if (!AppContext.isLogin()) {
ActionEngine.toLoginActivity(activity);
return;
}

if (deleteUrl == null && action.getAction() == 1) {
ToastUtils.showToast("已经添加" + function + "了");
return;
}
final String cannel = action.getAction() == 1 ? "取消" : "";

HttpUtil.queryData(activity, action.getAction() == 0 ? addUrl : deleteUrl, true, new NetQuestTask.SimpleRequestDataListener() {
@Override
public void onSuccess(String str) {
ResultBean resultBean = JSON.parseObject(str, ResultBean.class);
if (resultBean.getResoures() == 1) {
action.setAction(action.getAction() == 1 ? 0 : 1);//
ToastUtils.showToast(cannel + function + "成功");
} else {
ToastUtils.showToast(cannel + function + "失败");
}
}

@Override
public void onFail(String str) {
ToastUtils.showToast(cannel + function + "失败 服务器错误 " + str);

}
});
}
//实际上服务器有时候不弄累加值得,这里贴的没有自动给+1 -1的,但是可以解决多个地方不同接口的点赞

image.png
image.png

好了,不说这个了

开始正文

打开项目是不是就只会一个一个zip包下载?多累啊,
其实仔细点开链接发现地址前缀是同一个,于是焕然大悟,难怪谷歌也说推荐git clone
首先要学习git的朋友看看我写的这篇文章
http://qssq666.cn/2016/08/26/%E5%AD%A6%E4%B9%A0git%E5%BF%83%E5%BE%97/

1
2
3
4
git clone https://github.com/googlesamples/android-architecture.git  qssq-lean-android-architecture
cd qssq-lean-android-architecture


刚进去只能看到几个文档md的,需要切换分支。
查看该项目所有分支

1
git branch -a

image.png

切换分支演示

checkout todo-mvp``` 当然我这里是切换到另外一个分支,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
```git branch```查看当前所处哪个分支,会发现当前分支颜色是不相同的  其实我是从master->todo-mvp->todo-mvp-rxjava的,上面的图已经暴露我的操作行踪了,尴尬不
![image.png](https://upload-images.jianshu.io/upload_images/2815884-731380f543c085eb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

再详细一点点
![image.png](https://upload-images.jianshu.io/upload_images/2815884-39a64f6339f9eac8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

```git branch -l``` 查看本地分支
```git branch -r``` 查看远程分支
![image.png](https://upload-images.jianshu.io/upload_images/2815884-44687f4ab017fb41.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

好,git就只需要掌握着几个就差不多了,开始学习了,下次见

2018-4-21 21:35:12


2018-4-22 00:33:21 继续
##### todo-mvp

随便找一个mvp的界面案例分析就够了,实际上这个demo是每一个activity就会搞一个mvp结构,我觉得吧,似乎没多大必要,除非是一个成功的产品,真的没必要各种 ,顶多来一套刷新机制的```mvp```

除非有很多共用性的东西,我觉得我的QQ机器人的添加数据界面是可以这么架构,我之前是用的```mvc```继承来做这件事情的

----

```TasksRepository```实现类实现了接口```TasksDataSource```数据源 称之为**M**

```StatisticsContract.View```扩展接口继承了接口```BaseView```接口则是这是是**V**,

在官方的项目中,所有activity界面都会包含一个对应逻辑的```android fragment```,所以由这个fragment实现
```StatisticsContract.View```进行view的显示隐藏等操作

```StatisticsPresenter```实现类实现了接口``` StatisticsContract.Presenter```接口则是这是是**P**,

下面Persenter的具体代码,可以看出来p掌控了v,也掌控了m,
先上个图再看代码
![image.png](https://upload-images.jianshu.io/upload_images/2815884-abaf3495d073b254.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/500)
从图上可以看出view箭头反过来指向了persenter,那么可以从``` mStatisticsView.setPresenter(this)```就可以证明的确它是要这么做了,不传递```Persenter```进去难道隔空传物不成?,对不,那么下面是Presenter的代码了

public class StatisticsPresenter implements StatisticsContract.Presenter {
private final TasksRepository mTasksRepository;
private final StatisticsContract.View mStatisticsView;
public StatisticsPresenter(@NonNull TasksRepository tasksRepository,
@NonNull StatisticsContract.View statisticsView) {
mTasksRepository = checkNotNull(tasksRepository, “tasksRepository cannot be null”);
mStatisticsView = checkNotNull(statisticsView, “StatisticsView cannot be null!”);
mStatisticsView.setPresenter(this);
}
@Override
public void start() {
loadStatistics();
}
private void loadStatistics() {
mStatisticsView.setProgressIndicator(true);
EspressoIdlingResource.increment(); // App is busy until further notice
mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {
@Override
public void onTasksLoaded(List tasks) {
int activeTasks = 0;
int completedTasks = 0;

            if (!EspressoIdlingResource.getIdlingResource().isIdleNow()) {
                EspressoIdlingResource.decrement(); // Set app as idle.
            }

            // We calculate number of active and completed tasks
            for (Task task : tasks) {
                if (task.isCompleted()) {
                    completedTasks += 1;
                } else {
                    activeTasks += 1;
                }
            }
            // The view may not be able to handle UI updates anymore
            if (!mStatisticsView.isActive()) {
                return;
            }
            mStatisticsView.setProgressIndicator(false);

            mStatisticsView.showStatistics(activeTasks, completedTasks);
        }

        @Override
        public void onDataNotAvailable() {
    
            if (!mStatisticsView.isActive()) {
                return;
            }
            mStatisticsView.showLoadingStatisticsError();
        }
    });
}

}

1
2

再看看实现v,通过这个代码就能理解为什么view箭头指向````Persenter```

public class StatisticsFragment extends Fragment implements StatisticsContract.View {

private TextView mStatisticsTV;

private StatisticsContract.Presenter mPresenter;

public static StatisticsFragment newInstance() {
    return new StatisticsFragment();
}

@Override
public void setPresenter(@NonNull StatisticsContract.Presenter presenter) {
    mPresenter = checkNotNull(presenter);
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.statistics_frag, container, false);
    mStatisticsTV = (TextView) root.findViewById(R.id.statistics);
    return root;
}

@Override
public void onResume() {
    super.onResume();
    mPresenter.start();
}

@Override
public void setProgressIndicator(boolean active) {
    if (active) {
        mStatisticsTV.setText(getString(R.string.loading));
    } else {
        mStatisticsTV.setText("");
    }
}

@Override
public void showStatistics(int numberOfIncompleteTasks, int numberOfCompletedTasks) {
    if (numberOfCompletedTasks == 0 && numberOfIncompleteTasks == 0) {
        mStatisticsTV.setText(getResources().getString(R.string.statistics_no_tasks));
    } else {
        String displayString = getResources().getString(R.string.statistics_active_tasks) + " "
                + numberOfIncompleteTasks + "\n" + getResources().getString(
                R.string.statistics_completed_tasks) + " " + numberOfCompletedTasks;
        mStatisticsTV.setText(displayString);
    }
}

@Override
public void showLoadingStatisticsError() {
    mStatisticsTV.setText(getResources().getString(R.string.statistics_error));
}

@Override
public boolean isActive() {
    return isAdded();
}

}

1
2
3
4
5
6
7
8
9
10


从上面代码体现了2个信息
非fragment本身的```@Override``` 实际上是p控制v的过程
而fragment也调用了```mPresenter.start()``` 实际上是反过来控制```Persenter```
```start()```实际上是间接让```Persenter```去控制```M``` 进行加载数据
所以 图片看懂了。。。


### M的逻辑

public class TasksRepository implements TasksDataSource {

private static TasksRepository INSTANCE = null;

private final TasksDataSource mTasksRemoteDataSource;

private final TasksDataSource mTasksLocalDataSource;


boolean mCacheIsDirty = false;
private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
                        @NonNull TasksDataSource tasksLocalDataSource) {
    mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
    mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}


public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,
                                          TasksDataSource tasksLocalDataSource) {
    if (INSTANCE == null) {
        INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource);
    }
    return INSTANCE;
}

public static void destroyInstance() {
    INSTANCE = null;
}


@Override
public void getTasks(@NonNull final LoadTasksCallback callback) {
    checkNotNull(callback);

    // Respond immediately with cache if available and not dirty
    if (mCachedTasks != null && !mCacheIsDirty) {
        callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
        return;
    }

    if (mCacheIsDirty) {
        // If the cache is dirty we need to fetch new data from the network.
        getTasksFromRemoteDataSource(callback);
    } else {
        // Query the local storage if available. If not, query the network.
        mTasksLocalDataSource.getTasks(new LoadTasksCallback() {
            @Override
            public void onTasksLoaded(List<Task> tasks) {
                refreshCache(tasks);
                callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
            }

            @Override
            public void onDataNotAvailable() {
                getTasksFromRemoteDataSource(callback);
            }
        });
    }
}

@Override
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);
}

@Override
public void completeTask(@NonNull Task task) {
    checkNotNull(task);
    mTasksRemoteDataSource.completeTask(task);
    mTasksLocalDataSource.completeTask(task);

    Task completedTask = new Task(task.getTitle(), task.getDescription(), task.getId(), true);

    // Do in memory cache update to keep the app UI up to date
    if (mCachedTasks == null) {
        mCachedTasks = new LinkedHashMap<>();
    }
    mCachedTasks.put(task.getId(), completedTask);
}

@Override
public void completeTask(@NonNull String taskId) {
    checkNotNull(taskId);
    completeTask(getTaskWithId(taskId));
}

@Override
public void activateTask(@NonNull Task task) {
    checkNotNull(task);
    mTasksRemoteDataSource.activateTask(task);
    mTasksLocalDataSource.activateTask(task);

    Task activeTask = new Task(task.getTitle(), task.getDescription(), task.getId());

    // Do in memory cache update to keep the app UI up to date
    if (mCachedTasks == null) {
        mCachedTasks = new LinkedHashMap<>();
    }
    mCachedTasks.put(task.getId(), activeTask);
}

@Override
public void activateTask(@NonNull String taskId) {
    checkNotNull(taskId);
    activateTask(getTaskWithId(taskId));
}

@Override
public void clearCompletedTasks() {
    mTasksRemoteDataSource.clearCompletedTasks();
    mTasksLocalDataSource.clearCompletedTasks();

    // Do in memory cache update to keep the app UI up to date
    if (mCachedTasks == null) {
        mCachedTasks = new LinkedHashMap<>();
    }
    Iterator<Map.Entry<String, Task>> it = mCachedTasks.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<String, Task> entry = it.next();
        if (entry.getValue().isCompleted()) {
            it.remove();
        }
    }
}


@Override
public void getTask(@NonNull final String taskId, @NonNull final GetTaskCallback callback) {
    checkNotNull(taskId);
    checkNotNull(callback);
    Task cachedTask = getTaskWithId(taskId);

    if (cachedTask != null) {
        callback.onTaskLoaded(cachedTask);
        return;
    }
    mTasksLocalDataSource.getTask(taskId, new GetTaskCallback() {
        @Override
        public void onTaskLoaded(Task 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);
            callback.onTaskLoaded(task);
        }

        @Override
        public void onDataNotAvailable() {
            mTasksRemoteDataSource.getTask(taskId, new GetTaskCallback() {
                @Override
                public void onTaskLoaded(Task 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);
                    callback.onTaskLoaded(task);
                }

                @Override
                public void onDataNotAvailable() {
                    callback.onDataNotAvailable();
                }
            });
        }
    });
}

@Override
public void refreshTasks() {
    mCacheIsDirty = true;
}

@Override
public void deleteAllTasks() {
    mTasksRemoteDataSource.deleteAllTasks();
    mTasksLocalDataSource.deleteAllTasks();

    if (mCachedTasks == null) {
        mCachedTasks = new LinkedHashMap<>();
    }
    mCachedTasks.clear();
}

@Override
public void deleteTask(@NonNull String taskId) {
    mTasksRemoteDataSource.deleteTask(checkNotNull(taskId));
    mTasksLocalDataSource.deleteTask(checkNotNull(taskId));

    mCachedTasks.remove(taskId);
}

private void getTasksFromRemoteDataSource(@NonNull final LoadTasksCallback callback) {
    mTasksRemoteDataSource.getTasks(new LoadTasksCallback() {
        @Override
        public void onTasksLoaded(List<Task> tasks) {
            refreshCache(tasks);
            refreshLocalDataSource(tasks);
            callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
        }

        @Override
        public void onDataNotAvailable() {
            callback.onDataNotAvailable();
        }
    });
}

private void refreshCache(List<Task> tasks) {
    if (mCachedTasks == null) {
        mCachedTasks = new LinkedHashMap<>();
    }
    mCachedTasks.clear();
    for (Task task : tasks) {
        mCachedTasks.put(task.getId(), task);
    }
    mCacheIsDirty = false;
}

private void refreshLocalDataSource(List<Task> tasks) {
    mTasksLocalDataSource.deleteAllTasks();
    for (Task task : tasks) {
        mTasksLocalDataSource.saveTask(task);
    }
}

@Nullable
private Task getTaskWithId(@NonNull String id) {
    checkNotNull(id);
    if (mCachedTasks == null || mCachedTasks.isEmpty()) {
        return null;
    } else {
        return mCachedTasks.get(id);
    }
}

}

看完了吗?  可以说所有``` @Override```方法都是被```Persenter```操控的它压根只负责处理数据,也不会控制```view,```更不会反过来控制```Persenter```
mvp实际上看完这个```google demo```感觉也还是很简单的,给我的感觉就是全接口了,应该是很累的,像我这种小公司追求编码速度效率的话,那些不重用的东西我还是用mvc写,甚至再掺杂一些```mvvm```的东西进行绑定.
我现在还没咨询过完mvp的大佬,我是感觉没来几个共用我不会这么搞, 有特性的东西,如刷新几乎每一个页面都需要,这个时候一个base 做一套mvp 控制刷新就ok了。

个人愚见,更多参考官方demo,官方demo很多 有你好看