1. 程式人生 > >Google 官方Android MVP架構實踐

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 GankContract {

    interface Presenter extends BasePresenter{
        void loadGank(int pageIndex, int pageSize);
    }

    interface View extends BaseView<Presenter>{
        void showProgress();
        void hideProgress();
        void showGank(List<Gank> gankList);
    }
}


資料提供者,基於Okhttp請求,我這裡使用的鴻洋大神封裝好的Okhttp請求庫。提供一個介面,將資料共享出去,提供給具體Presenter。

資料提供的伺服器是我自己搭建的一個阿里雲伺服器,為了平時開發方便,寫了一個數據介面,可以獲取大量的美女圖片和文字資訊,get和post請求都支援,便於開發時候做資料測試。資料介面地址: http://gank.io/api/data/%E7%A6%8F%E5%88%A9/10/3  感謝乾貨提供。

public class ServerHelper {

    private DataLoadListener listener;

    public void getGankList(int pageSize, int pageIndex){
        String url = "http://gank.io/api/data/%E7%A6%8F%E5%88%A9/" + pageSize + "/" + pageIndex;
        Log.i("test", url);
        OkHttpUtils.get().url(url).build().execute(new GankResponseCallBack() {
            @Override
            public void onError(Call call, Exception e) {
                listener.failure(e);
                Log.i("test", "errorerror");
            }

            @Override
            public void onResponse(List<Gank> response) {
                Log.i("test", "資料獲取成功 "  + new Gson().toJson(response, List.class));
                listener.success(response);
            }
        });
    }

    public interface DataLoadListener{
        void failure(Exception e);
        void success(List<Gank> gankList);
    }

    public void setListener(DataLoadListener listener) {
        this.listener = listener;
    }
}


具體Presenter,實現了GankContract.Presenter和ServerHelper.DataLoadListener介面,關聯了GankContract.View介面,把網路請求到的資料提供給View。

public class GankPresenter implements GankContract.Presenter,ServerHelper.DataLoadListener {

    private GankContract.View view;
    private ServerHelper serverHelper;

    //資料提供者 這裡應該封裝網路資料請求

    public GankPresenter(GankContract.View view) {
        this.view = view;
        view.setPresenter(this);
        serverHelper = new ServerHelper();
        serverHelper.setListener(this);
    }


    @Override
    public void start() {

    }

    @Override
    public void failure(Exception e) {
        view.hideProgress();
    }

    @Override
    public void success(List<Gank> gankList) {
        view.hideProgress();
        view.showGank(gankList);
    }


    @Override
    public void loadGank(int pageIndex, int pageSize) {
        serverHelper.getGankList(pageIndex, pageSize);
    }
}



具體的View,這裡的BeautyFragment實現了GankContract.View,使用GankContract.Presenter進行資料請求,View如果接受到資料,則對資料進行介面展示操作。這樣保證了介面層只負責資料的顯示,減少了介面層的資料邏輯處理,使得介面更加流暢。

public class GankFragment extends Fragment implements GankContract.View, SwipeRefreshLayout.OnRefreshListener {

    private GankContract.Presenter presenter;
    private SwipeRefreshLayout refreshLayout;
    private RecyclerView recyclerView;
    private GankAdapter adapter;
    private List<Gank> gankList;
    private int pageIndex = 1;
    private int pageSize = 10;
    private boolean isLastPage = false;
    private int lastVisibleItem = 0;
    private LinearLayoutManager linearLayoutManager;

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

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        gankList = 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 GankAdapter(gankList,getActivity());
        recyclerView.setAdapter(adapter);

        Log.i("test", "11111");

        presenter.loadGank(pageSize,pageIndex);


        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+"");
                    //根據類目網路請求資料
                    Log.e("test", "最後一頁" + isLastPage);
                    if(!isLastPage){
                        presenter.loadGank(pageSize,pageIndex);
                    }
                }
            }
        });

        return view;
    }

    @Override
    public void showProgress() {
        refreshLayout.setRefreshing(true);
    }

    @Override
    public void hideProgress() {
        refreshLayout.setRefreshing(false);
    }

    @Override
    public void showGank(List<Gank> list) {
        if (list != null) {
            if (pageIndex == 1) {
                gankList.clear();
                gankList.addAll(list);
                isLastPage = false;
                pageIndex++;
            } else if (list.size() == pageSize) {
                gankList.addAll(list);
                isLastPage = false;
                pageIndex++;
            } else {
                gankList.addAll(list);
                isLastPage = true;
            }
        }
        adapter.notifyDataSetChanged();
    }



    @Override
    public void setPresenter(GankContract.Presenter presenter) {
        this.presenter = presenter;
    }

    @Override
    public void onRefresh() {
        pageIndex = 1;
        presenter.loadGank(pageSize,pageIndex);
    }
}

public class GankActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_beauty);

        GankFragment fragment  = GankFragment.newInstance();

        ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),fragment,R.id.container);

        //設定presenter
        new GankPresenter(fragment);
    }
}


好了,Google 官方MVP架構實踐介紹完畢,希望對大家有收穫。上述程式碼是實現MVP架構模式的關鍵程式碼,中間還涉及到的ActivityUtils等類都在原始碼中哦,歡迎大家下載。