Google 官方Android MVP架构实践

一、Google 官方MVP介绍

近期,关于Android开发架构的讨论沸沸扬扬,各大技术平台随处可见关于Android架构的技术文章。MVC、MVP、MVVM等等,就目前的形式来看,MVP模式在Android开发领域界逐渐流行了起来。前段时间,Google也忍耐不住Android MVP架构的火热程度,给广大Android程序员带来了福利,推出了Google官方MVP架构例子。源码见: https://github.com/googlesamples/android-architecture

官方给出了四种MVP架构模式:

1、todo-mvp:MVP基础架构

2、todo-mvp-loaders:基于MVP基础架构,获取数据部分使用了Loaders架构

3、todo-mvp-databinding:基于MVP基础架构,使用了数据绑定组件

4、todo-mvp-clean:基于MVP基础架构,引入Clean架构概念

官方正在进行的MVP架构模式:

1、todo-mvp-contentproviders:基于MVP基础架构,使用了Content Provider

2、todo-mvp-dragger:基于MVP基础架构,使用dragger2依赖注入

3、todo-mvp-rxjava:基于MVP基础架构,使用RxJava解决数据并发

二、MVP架构模式介绍

MVP中的M代表Model,即数据层,V代表View,即界面层,P代表Presenter,负责关联Model和View,把Model层的数据显示到View。

三、MVP架构实践

这里,我从todo-mvp基础架构入手,模仿一下官方MVP源码,跟大家详细介绍一下Google官方MVP架构模式。官方的MVP示例源码展示的是一个简易便签,数据提供者是Sqlite数据库。我的项目里面数据提供者是网络,使用okhttp网络请求框架从服务器获取数据。一起来看看神奇的MVP架构模式是怎样把数据显示到手机界面的吧。

google官方todo-mvp模式架构图(结合todo-mvp源码可以好好感受一下mvp的魅力)

Presente基础接口

public interface BasePresenter {  
    void start();  
}  

View基础接口

public interface BaseView<T> {  
    void setPresenter(T presenter);  
}  

具体任务的Presener和View接口,Presenter接口里面封装的是数据获取的方法,需要数据提供者具体实现;View里面封装的方法是界面层(Fragment/Activity)要实现的方法。

public interface TaskDetailContract {  
  
    //http://www.diandidaxue.com:8080/apiServer.do?opcode=getBeauty&pageNum=1&numPerPage=5  
    interface Presenter extends BasePresenter{  
        void loadBeauty(int pageIndex, int pageSize);  
    }  
  
    interface View extends BaseView<Presenter>{  
        void showProgress();  
        void hideProgress();  
        void showBeauty(List<Beauty> beautyList);  
    }  
}  

数据提供者,基于Okhttp请求,我这里使用的鸿洋大神封装好的Okhttp请求库。提供一个接口,将数据共享出去,提供给具体Presenter。

Okhttp封装具体使用方法见:http://blog.csdn.net/lmj623565791/article/details/47911083

数据提供的服务器是我自己搭建的一个阿里云服务器,为了平时开发方便,写了一个数据接口,可以获取大量的美女图片和文字信息,get和post请求都支持,便于开发时候做数据测试。数据接口地址:http://www.diandidaxue.com:8080/apiServer.do?opcode=getBeauty&pageNum=1&numPerPage=50

public class ServerHelper {  
  
    private DataLoadListener listener;  
  
    public void getBeautyList(int pageIndex, int pageSize){  
        String url = "http://www.diandidaxue.com:8080/apiServer.do";  
        Map<String, String> params = new HashMap<String, String>();  
        params.put("opcode", "getBeauty");  
        params.put("pageNum", String.valueOf(pageIndex));  
        params.put("numPerPage", String.valueOf(pageSize));  
        OkHttpUtils.post().url(url).params(params).build().execute(new ListBeautyCallBack() {  
            @Override  
            public void onError(Call call, Exception e) {  
                listener.failure(e);  
            }  
  
            @Override  
            public void onResponse(List<Beauty> response) {  
                listener.success(response);  
            }  
        });  
    }  
  
    public interface DataLoadListener{  
        void failure(Exception e);  
        void success(List<Beauty> beautyList);  
    }  
  
    public void setListener(DataLoadListener listener) {  
        this.listener = listener;  
    }  
}  

具体Presenter,实现了TaskDetailContract.Presenter和ServerHelper.DataLoadListener接口,关联了TaskDetailContract.View接口,把网络请求到的数据提供给View。

public class TaskPresenter implements TaskDetailContract.Presenter,ServerHelper.DataLoadListener {  
  
    private TaskDetailContract.View view;  
    private ServerHelper serverHelper;  
  
    //数据提供者 这里应该封装网络数据请求  
  
    public TaskPresenter(TaskDetailContract.View view) {  
        this.view = view;  
        view.setPresenter(this);  
        serverHelper = new ServerHelper();  
        serverHelper.setListener(this);  
    }  
  
    /** 
     * 请求网络数据 
     * @param pageIndex 页码 
     * @param pageSize 一页显示的数量 
     */  
    @Override  
    public void loadBeauty(final int pageIndex, int pageSize) {  
        serverHelper.getBeautyList(pageIndex,pageSize);  
    }  
  
    @Override  
    public void start() {  
  
    }  
  
    @Override  
    public void failure(Exception e) {  
        view.hideProgress();  
    }  
  
    @Override  
    public void success(List<Beauty> beautyList) {  
        view.hideProgress();  
        view.showBeauty(beautyList);  
    }  
}  

具体的View,这里的BeautyFragment实现了TaskDetailContract.View,使用TaskDetailContract.Presenter进行数据请求,View如果接受到数据,则对数据进行界面展示操作。这样保证了界面层只负责数据的显示,减少了界面层的数据逻辑处理,使得界面更加流畅。

public class BeautyFragment extends Fragment implements TaskDetailContract.View, SwipeRefreshLayout.OnRefreshListener {  
  
    private TaskDetailContract.Presenter presenter;  
    private SwipeRefreshLayout refreshLayout;  
    private RecyclerView recyclerView;  
    private BeautyAdapter adapter;  
    private List<Beauty> beautyList;  
    private int pageIndex = 1;  
    private int pageSize = 8;  
    private boolean isLastPage = false;  
    private int lastVisibleItem = 0;  
    private LinearLayoutManager linearLayoutManager;  
  
    public static BeautyFragment newInstance(){  
        return new BeautyFragment();  
    }  
  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        presenter = new TaskPresenter(this);  
        beautyList = new ArrayList<>();  
    }  
  
    @Override  
    public void onActivityCreated(Bundle savedInstanceState) {  
        super.onActivityCreated(savedInstanceState);  
    }  
  
    @Nullable  
    @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
        View view = inflater.inflate(R.layout.fragment_beauty,container,false);  
        recyclerView = (RecyclerView) view.findViewById(R.id.tabRecyler);  
        refreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.tabSwipeRefresh);  
  
        linearLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL,false);  
  
        //设置下拉刷新  
        refreshLayout.setColorSchemeColors(R.color.colorAccent);  
        refreshLayout.setOnRefreshListener(this);  
        //第一次进入界面时候加载进度条  
        refreshLayout.setProgressViewOffset(false, 0, (int) TypedValue  
                .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources()  
                        .getDisplayMetrics()));  
        recyclerView.setLayoutManager(linearLayoutManager);  
        adapter = new BeautyAdapter(beautyList,getActivity());  
        recyclerView.setAdapter(adapter);  
  
        presenter.loadBeauty(1,8);  
  
  
        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {  
            @Override  
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {  
                super.onScrolled(recyclerView, dx, dy);  
                lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();  
            }  
  
            @Override  
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {  
                super.onScrollStateChanged(recyclerView, newState);  
                if (newState == RecyclerView.SCROLL_STATE_IDLE  
                        && lastVisibleItem + 1 == adapter.getItemCount()) {  
                    Log.i("pageIndex",pageIndex+"");  
                    //根据类目网络请求数据  
                    if(!isLastPage){  
                        presenter.loadBeauty(pageIndex,pageSize);  
                    }  
                }  
            }  
        });  
  
        return view;  
    }  
  
    @Override  
    public void showProgress() {  
        refreshLayout.setRefreshing(true);  
    }  
  
    @Override  
    public void hideProgress() {  
        refreshLayout.setRefreshing(false);  
    }  
  
    @Override  
    public void showBeauty(List<Beauty> list) {  
        ActivityUtils.checkNotNull(list);  
        if(list != null){  
            if(pageIndex == 1){  
                beautyList.clear();  
                beautyList.addAll(list);  
                isLastPage = false;  
                pageIndex ++;  
            }else if(list                                                                                                                                                                                                                   .size() == pageSize){  
                beautyList.addAll(list);  
                isLastPage = false;  
                pageIndex ++;  
            }else {  
                beautyList.addAll(list);  
                isLastPage = true;  
            }  
        }  
        adapter.notifyDataSetChanged();  
  
  
    }  
  
  
    @Override  
    public void setPresenter(TaskDetailContract.Presenter presenter) {  
        this.presenter = presenter;  
    }  
  
    @Override  
    public void onRefresh() {  
        pageIndex = 1;  
        presenter.loadBeauty(pageIndex,pageSize);  
    }  
}  
public class BeautyActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_beauty);  
  
        BeautyFragment fragment = new BeautyFragment();  
        if(fragment == null){  
            fragment = BeautyFragment.newInstance();  
        }  
        ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),fragment,R.id.container);  
  
    }  

好了,Google 官方MVP架构实践介绍完毕,希望对大家有收获。上述代码是实现MVP架构模式的关键代码,中间还涉及到的ActivityUtils等类都在源码中哦,欢迎大家下载。

源码下载地址:https://github.com/xiaomanzijia/mvp