一個小框架,基於rx_retrofit2_mvp

離職在即,也沒什麼事情作,就鼓搗了一下。任意搭建了一個小框架,看看之後能不能搞出本身的一個model,好了。不說別的,上代碼html

1,先上依賴庫java

    compile 'io.reactivex:rxandroid:1.2.1'
    compile 'com.squareup.okhttp3:okhttp:3.3.1'
    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'io.reactivex:rxjava:1.1.0'
    compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
    compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
    compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
    compile 'com.android.support:design:24.2.1'
    compile 'com.android.support:recyclerview-v7:24.2.1'
    compile 'com.android.support:cardview-v7:24.2.1'
    compile 'com.jakewharton:butterknife:7.0.1'
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'com.github.chrisbanes.photoview:library:1.2.3'

2。 依賴retrolambdareact

在app.build依賴android

    apply plugin: 'me.tatarka.retrolambda'
   compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

而後在項目的app.build依賴git

        classpath 'me.tatarka:gradle-retrolambda:3.2.5'
       OK,到這裏咱們的環境搭建就完畢了


3。搭建Http請求模塊github

  1. 搭建工具類RetrofitUtils
    package mvpmaster.lht.com.lht.utils;
    
    import com.squareup.okhttp.OkHttpClient;
    
    import java.util.concurrent.TimeUnit;
    
    import retrofit.GsonConverterFactory;
    import retrofit.Retrofit;
    import retrofit.RxJavaCallAdapterFactory;
    
    /**
     * Created by Ly on 2016/10/14.
     */
    
    public class RetrofitUtils {
        private static final int READ_TIMEOUT = 60;//讀取超時時間 單位 秒
        private static final int CONN_TIMEOUT = 60;//鏈接超時時間 單位 秒
        private static Retrofit retrofit;
    
        public RetrofitUtils() {
        }
    
        public static Retrofit getInstance(String url) {
            retrofit = null;
            // 初始化一個okhttpClicent的對象 否則ref會本身加入一個
            OkHttpClient client = new OkHttpClient();
    //        設置讀取時間爲1分鐘
            client.setReadTimeout(READ_TIMEOUT, TimeUnit.MINUTES);
    //        設置連接時間爲12s
            client.setConnectTimeout(CONN_TIMEOUT, TimeUnit.SECONDS);
            retrofit = new Retrofit.Builder()
                    .client(client)
                    .baseUrl(url)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .build();
            return retrofit;
        }
    }
    
    Ps,我發現這裏並不能作到session的保持,在某些公司的後臺並不能支持這種請求庫,我稍候會上傳一個更新後的版本號
  2. 這裏寫上個人一個interface(爲何我用了rx還要有callback???黑人問號臉)
    package mvpmaster.lht.com.lht.conf;
    
    /**
     * Created by Ly on 2016/10/13.
     */
    
    public interface OkHttpCallBack<T> {
        void onSuccess(T t);//成功的回調
    
        void onFaild(Throwable e);//失敗的回調
    
        void onFinish();
    }
    

  3. 而後是一些比較通用的api文件了,我就不寫凝視了
    package mvpmaster.lht.com.lht.conf;
    
    /**
     * Created by Ly on 2016/11/2.
     */
    
    public class HttpConf {
        private static final String ZHIHU_BASE_URL = "http://news-at.zhihu.com/api/4/";
        private static final String GANK_BASE_URL = "http://gank.io/api/";
        private static final String DAILY_BASE_URL = "http://app3.qdaily.com/app3/";
    
        public static String getZhihuBaseUrl() {
            return ZHIHU_BASE_URL;
        }
    
        public static String getGankBaseUrl() {
            return GANK_BASE_URL;
        }
    
        public static String getDailyBaseUrl() {
            return DAILY_BASE_URL;
        }
    }
    

    package mvpmaster.lht.com.lht.conf;
    
    /**
     * Created by Ly on 2016/11/2.
     */
    
    public class HttpStatusConf {
        private static final int SUCCESS = 200;
    
        public static int getSUCCESS() {
            return SUCCESS;
        }
    }
    

    package mvpmaster.lht.com.lht.utils;
    
    
    import mvpmaster.lht.com.lht.ui.beanIml.DailyBean;
    import mvpmaster.lht.com.lht.ui.beanIml.NewsDetailBean;
    import mvpmaster.lht.com.lht.ui.beanIml.NewsTimeLine;
    import retrofit.http.GET;
    import retrofit.http.Path;
    import rx.Observable;
    
    /**
     * Created by Ly on 2016/10/14.
     */
    
    public interface APIService {
    
    
        @GET("news/latest")
        Observable<NewsTimeLine> getZhiHuList();
    
        @GET("news/before/{time}")
        Observable<NewsTimeLine> getBeforetNews(@Path("time") String time);
    
        @GET("news/{id}")
        Observable<NewsDetailBean> getDetailNews(@Path("id") String id);
    
    
        //    for daily
        @GET("homes/index/{num}.json")
        Observable<DailyBean> getDailyTimeLine(@Path("num") String num);
    
    }

4,網絡請求搭建完了。重頭戲來了,咱們來搭建mvp的基本框架
  1. 咱們的baseActivity
    package mvpmaster.lht.com.lht.base;
    
    import android.content.Context;
    import android.os.Build;
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.design.widget.AppBarLayout;
    import android.support.v4.widget.SwipeRefreshLayout;
    import android.support.v7.app.ActionBar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.util.TypedValue;
    import android.view.MenuItem;
    
    import butterknife.ButterKnife;
    import mvpmaster.lht.com.lht.R;
    
    /**
     * Created by Ly on 2016/11/2.
     */
    
    public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity {
    
        protected T mPresenter;
        private AppBarLayout mAppBar;
        private Toolbar mToolbar;
        private SwipeRefreshLayout mRefreshLayout;
    
        public Context mContext;
        private boolean mIsRequestDataRefresh = false;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mContext = this;
            // 贊成爲空 不是所有的都要實現這個模式
            if (createPresenter() != null) {
                mPresenter = createPresenter();
                mPresenter.attachView((V) this);
            }
            setContentView(provideContentViewId());
            ButterKnife.bind(this);
    
            mAppBar = (AppBarLayout) findViewById(R.id.app_bar_layout);
            mToolbar = (Toolbar) findViewById(R.id.toolbar);
            if (mToolbar != null && mAppBar != null) {
                setSupportActionBar(mToolbar); //把Toolbar當作ActionBar給設置
    
                if (canBack()) {
                    ActionBar actionBar = getSupportActionBar();
                    if (null != actionBar) {
                        //設置ActionBar一個返回箭頭。主界面沒有,次級界面有
                        actionBar.setDisplayHomeAsUpEnabled(true);
                    }
                    if (Build.VERSION.SDK_INT >= 21) {
                        //Z軸浮動
                        mAppBar.setElevation(10.6F);
                    }
                }
            }
            if (isSetRefresh()) {
                setupSwipeRefresh();
            }
    
        }
    
        public static void toIntent(Context context, String... str) {
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // 此時android.R.id.home即爲返回箭頭
            if (item.getItemId() == android.R.id.home) {
                onBackPressed();
                finish();
                return true;
            } else {
                return super.onOptionsItemSelected(item);
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mPresenter != null) {
                mPresenter.detachView();
            }
        }
    
        /**
         * 生成下拉刷新
         */
        private void setupSwipeRefresh() {
            mRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
            if (null != mRefreshLayout) {
                mRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary);
                mRefreshLayout.setProgressViewOffset(true,
                        0,
                        (int) TypedValue.applyDimension
                                (TypedValue.COMPLEX_UNIT_DIP, 24, getResources()
                                        .getDisplayMetrics()));
            }
        }
    
        /**
         * 設置刷新
         *
         * @param requestDataRefresh
         */
        public void setRefresh(boolean requestDataRefresh) {
            if (mRefreshLayout == null) {
                return;
            }
            if (!requestDataRefresh) {
                mIsRequestDataRefresh = false;
                mRefreshLayout.postDelayed(() -> {
                    if (mRefreshLayout != null) {
                        mRefreshLayout.setRefreshing(false);
                    }
                }, 1000);
            } else {
                mRefreshLayout.setRefreshing(true);
            }
        }
    
        /**
         * 數據刷新
         */
        public void requestDataRefresh() {
            mIsRequestDataRefresh = true;
        }
    
        /**
         * 推斷當前 Activity 是否贊成返回
         * 主界面不一樣意返回,次級界面贊成返回
         *
         * @return false
         */
        public boolean canBack() {
            return false;
        }
    
        /**
         * 推斷子Activity是否需要刷新功能
         *
         * @return false
         */
        public Boolean isSetRefresh() {
            return false;
        }
    
        /**
         * 建立P
         *
         * @return T
         */
        protected abstract T createPresenter();
    
        /**
         * 用於引入佈局文件
         *
         * @return
         */
        abstract protected int provideContentViewId();
    }
    

    package mvpmaster.lht.com.lht.base;
    
    import android.content.Context;
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.widget.SwipeRefreshLayout;
    import android.util.TypedValue;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import butterknife.ButterKnife;
    import mvpmaster.lht.com.lht.R;
    
    /**
     * Created by Ly on 2016/11/2.
     */
    
    public abstract class BaseFragment<V, T extends BasePresenter<V>> extends Fragment {
        protected Context mContext;
        protected T mPresenter;
    
        private boolean mIsRequestDataRefresh = false;
        private SwipeRefreshLayout mRefreshLayout;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mContext = getActivity();
            mPresenter = createPresenter();
            mPresenter.attachView((V) this);
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View rootView = inflater.inflate(createViewLayoutId(), container, false);
            ButterKnife.bind(this, rootView);
            initView(rootView);
            if (isSetRefresh()) {
                setupSwipeRefresh(rootView);
            }
            return rootView;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            mPresenter.detachView();
        }
    
        private void setupSwipeRefresh(View view) {
            mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
            if (mRefreshLayout != null) {
                mRefreshLayout.setColorSchemeResources(R.color.refresh_progress_1,
                        R.color.refresh_progress_2, R.color.refresh_progress_3);
                mRefreshLayout.setProgressViewOffset(true, 0, (int) TypedValue
                        .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
                mRefreshLayout.setOnRefreshListener(this::requestDataRefresh);
            }
        }
    
        public void requestDataRefresh() {
            mIsRequestDataRefresh = true;
        }
    
    
        public void setRefresh(boolean requestDataRefresh) {
            if (mRefreshLayout == null) {
                return;
            }
            if (!requestDataRefresh) {
                mIsRequestDataRefresh = false;
                mRefreshLayout.postDelayed(() -> {
                    if (mRefreshLayout != null) {
                        mRefreshLayout.setRefreshing(false);
                    }
                }, 1000);
            } else {
                mRefreshLayout.setRefreshing(true);
            }
        }
    
        protected abstract T createPresenter();
    
        protected abstract int createViewLayoutId();
    
        protected void initView(View rootView) {
        }
    
        public Boolean isSetRefresh() {
            return true;
        }
    }
    
    還有其它的一些base,我就不一一上傳了。

  2. 說一下我理解的mvp。在個人見解中。mvp,m是要作一些耗時操做的,像讀取網絡數據,數據庫數據。sp數據啊...這些髒活苦活全部都丟給它去作,咱們在contract中給它定義好了interface,model類在本身自己去實現它。而後按着上層的要求去作那些苦活累活;而View呢?我認爲一般是指咱們的activity或者fragment巴,他們就負責一些比較輕鬆的東西了。像顯示個toast啊,show一下dialog啊,拿一下editext的數據啊。最苦力也就是設置個適配器啊。監聽一下滑動啊之類的,反正最輕鬆的那個就是它了;而後就是presenter了,這個類我認爲挺難弄的,相似於紅娘吧,它也要實現Contract的interface,而且要持有model和view的引用。在interface的回調裏面去操控model類去作耗時操做,而後在對應的callback(怎麼又是callback?)去操控view去實現各類交互。(不要噴我說得那麼模糊,但是這樣的東西寫不出來,用了就會有這樣的想法,而且。用了一次mvp,你就不會再想去用mvc了)
  3. 咱們舉一個樣例,首頁那裏拿取了知乎的信息,用了一個recyclerview去顯示拿到的數據。咱們就用它來說,我先上傳一波項目文件夾,否則太懵逼了。
  4. 咱們先看咱們的fragment 
    package mvpmaster.lht.com.lht.ui.fragment.zhuhu;
    
    import android.os.Bundle;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.widget.Toast;
    
    import butterknife.Bind;
    import mvpmaster.lht.com.lht.R;
    import mvpmaster.lht.com.lht.base.BaseFragment;
    import mvpmaster.lht.com.lht.ui.adapter.ZhiHuAdapter;
    import mvpmaster.lht.com.lht.ui.beanIml.NewsTimeLine;
    
    /**
     * Created by Ly on 2016/11/2.
     */
    
    public class ZhiHuFragment extends BaseFragment<ZhiHuContract.ZhiHuView, ZhiHuPresenter> implements ZhiHuContract.ZhiHuView {
        @Bind(R.id.content_list)
        RecyclerView mRlvZhiHu;
        
        private LinearLayoutManager mLayoutManager;
        private ZhiHuAdapter zhiHuAdapter;
    
        // 最後一個可見的視圖
        private int lastVisibleItem;
        // 是否載入過不少其它
        private boolean isLoadMore = false;
        // 知乎日報需要的下一個參數
        private String time;
    
    
    
        /**
         * 初始化配置
         */
        private void initConf() {
    //        適配器
            zhiHuAdapter = new ZhiHuAdapter(getActivity());
    //        manager
            mLayoutManager = new LinearLayoutManager(getActivity());
            mRlvZhiHu.setLayoutManager(mLayoutManager);
            mRlvZhiHu.setAdapter(zhiHuAdapter);
    //        啓動本身主動刷新配置
            setDataRefresh(true);
    //        獲取第一次的數據
            mPresenter.getDataList();
    //        檢測recView的滑動狀態
            scrollRecycleView();
        }
    
        /**
         * recyclerView Scroll listener , maybe in here is wrong ?
         */
        public void scrollRecycleView() {
            mRlvZhiHu.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                        lastVisibleItem = mLayoutManager
                                .findLastVisibleItemPosition();
                        if (mLayoutManager.getItemCount() == 1) {
                            zhiHuAdapter.updateLoadStatus(zhiHuAdapter.getLOAD_MORE());
                            return;
                        }
                        if (lastVisibleItem + 1 == mLayoutManager
                                .getItemCount()) {
                            zhiHuAdapter.updateLoadStatus(zhiHuAdapter.getLOAD_PULL_TO());
                            isLoadMore = true;
                            zhiHuAdapter.updateLoadStatus(zhiHuAdapter.getLOAD_MORE());
                            mPresenter.getBeforeDateList(time);
                        }
                    }
                }
    
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();
                }
            });
        }
    
        @Override
        public void requestDataRefresh() {
            super.requestDataRefresh();
            setDataRefresh(true);
            mPresenter.getDataList();
        }
    
        @Override
        protected ZhiHuPresenter createPresenter() {
            return new ZhiHuPresenter(this);
        }
    
        @Override
        protected int createViewLayoutId() {
            return R.layout.fragment_zhihu;
        }
    
        @Override
        public void setDataRefresh(boolean refresh) {
            setRefresh(refresh);
        }
    
        public static ZhiHuFragment newInstance() {
            Bundle args = new Bundle();
            ZhiHuFragment fragment = new ZhiHuFragment();
            fragment.setArguments(args);
            return fragment;
        }
    
    
        @Override
        public void loadFinishAndReset(NewsTimeLine newsTimeLine, String time) {
            zhiHuAdapter.resetData(newsTimeLine);
            setDataRefresh(false);
            this.time = time;
        }
    
        @Override
        public void loadFinishAndAdd(NewsTimeLine newsTimeLine, String time) {
            zhiHuAdapter.addData(newsTimeLine);
            this.time = time;
        }
    
        @Override
        public void loadFailure() {
            setDataRefresh(false);
            zhiHuAdapter.updateLoadStatus(zhiHuAdapter.getLOAD_FAILURE());
        }
    
        @Override
        protected void initView(View rootView) {
            super.initView(rootView);
            initConf();
        }
    
        @Override
        public void TsShow(String msg) {
            Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
        }
    }
    
    咱們在這個看到一個mPresenter。這個是什麼鬼?是從哪裏來的?事實上這個就是Presenter的對象了。也是BaseActivity的泛型裏面的那個咱們點取查看它的引用,發現在這個fragment裏面一共被引用了3次。第一次是剛進去頁面的時候,第一次讀取數據。第二個是下拉載入的時候,讀取了下一波的數據。再有一次是上拉刷新的時候。咱們又一次刷新了一次頁面拿取了最新的一波數據,但是?拿數據的在哪裏呢?網絡請求呢?在哪裏??
  5. 咱們看Presenter裏面作的操做吧:
    package mvpmaster.lht.com.lht.ui.fragment.zhuhu;
    
    import mvpmaster.lht.com.lht.base.BasePresenter;
    import mvpmaster.lht.com.lht.conf.OkHttpCallBack;
    import mvpmaster.lht.com.lht.ui.beanIml.NewsTimeLine;
    
    /**
     * Created by Ly on 2016/11/2.
     */
    
    public class ZhiHuPresenter extends BasePresenter<ZhiHuContract.ZhiHuView> implements ZhiHuContract.ZhiHuPresenter {
        private ZhiHuContract.ZhiHuView zhiHuView;
        private ZhiHuContract.ZhiHuModel zhiHuModel;
    
        public ZhiHuPresenter(ZhiHuContract.ZhiHuView zhiHuView) {
            this.zhiHuView = zhiHuView;
            zhiHuModel = new ZhiHuModel();
        }
    
    
        @Override
        public void getDataList() {
            zhiHuModel.getDataList(new OkHttpCallBack<NewsTimeLine>() {
                @Override
                public void onSuccess(NewsTimeLine newsTimeLine) {
                    zhiHuView.loadFinishAndReset(newsTimeLine, newsTimeLine.getDate());
                }
    
                @Override
                public void onFaild(Throwable e) {
                    loadError(e);
                    zhiHuView.loadFailure();
                }
    
                @Override
                public void onFinish() {
    
                }
            });
        }
    
        @Override
        public void getBeforeDateList(String time) {
            zhiHuModel.getBeforeDateList(time, new OkHttpCallBack<NewsTimeLine>() {
                @Override
                public void onSuccess(NewsTimeLine newsTimeLine) {
                    zhiHuView.loadFinishAndAdd(newsTimeLine, newsTimeLine.getDate());
                }
    
                @Override
                public void onFaild(Throwable e) {
                    loadError(e);
                    zhiHuView.loadFailure();
                }
    
                @Override
                public void onFinish() {
    
                }
            });
        }
    
        private void loadError(Throwable throwable) {
            throwable.printStackTrace();
            zhiHuView.TsShow(throwable.getMessage());
        }
    }
    
    咱們看到有三個ovver的方法。各自是刷新(第一次讀取),載入。讀取失敗 三種狀況:咱們可以看到,這個類持有了View和Model兩個模塊,在方法體裏面,咱們調用了model的方法去作耗時,在結果方法體裏面咱們調用了view的方法去改動UI,同一時候presenter這個模塊又被view持有了,view可以在聲明週期裏面去調用特定的方法,view和presenter相互溝通。view和model全然隔離,presenter調控model,presenter溝通全局。
補上個人git地址:https://github.com/LinHuanTanLy/mvpMaster
補上另一個請求框架:https://github.com/LinHuanTanLy/OkHttpWithSession
相關文章
相關標籤/搜索