学习googlesample demo之安卓架构mvp的正确姿势以及个人分析源码心得

谈到mvp,我先说说我以前是如何封装刷新 翻页等等的吧.

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


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的封装

public interface IOperaAction {
    public int getAction();

    public void setAction(int isfollow);//
}

   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/

git clone https://github.com/googlesamples/android-architecture.git  qssq-lean-android-architecture
cd qssq-lean-android-architecture


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

git branch -a
image.png

切换分支演示
git checkout todo-mvp 当然我这里是切换到另外一个分支,
git branch查看当前所处哪个分支,会发现当前分支颜色是不相同的 其实我是从master->todo-mvp->todo-mvp-rxjava的,上面的图已经暴露我的操作行踪了,尴尬不

image.png

再详细一点点


image.png

git branch -l 查看本地分支
git branch -r 查看远程分支

image.png

好,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

从图上可以看出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<Task> 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();
            }
        });
    }
}

再看看实现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();
    }
}

从上面代码体现了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很多 有你好看

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容