二次封裝MVP框架

在github淘到的mvp框架友浸,年前就一直想要研究下梢杭,但是年前比較忙,就在過年期間有空弄了一下栏账,今天寫下心得帖族,不出意料地話接下來就會用這個框架進(jìn)行項(xiàng)目開發(fā)了,只能先從別人那里取到經(jīng)挡爵,才能有經(jīng)驗(yàn)來玩自己的框架竖般。
?OK,先上github地址茶鹃,大家可以加群找那位大神探討探討涣雕,真·大神!
傳送門

配置部分

看下項(xiàng)目目錄:

項(xiàng)目目錄

在導(dǎo)入mvp的依賴后闭翩,要導(dǎo)入conf.gradle文件:


要導(dǎo)入的配置文件

可以看到里面是配置文件挣郭,要修改之類的可以在之類做修改:

ext {

    android = [
            compileSdkVersion: 23,
            buildToolsVersion: "23.0.2",

            minSdkVersion    : 15,
            targetSdkVersion : 23,

            versionCode      : 1,
            versionName      : '1.0.0',

            VSupportSdk      : '23.3.0',
            VRetrofitSdk     : "2.1.0",
            VOkhttp          : "3.4.2",
            VRxlifecycle     : "1.0"
    ]
    dependencies = [
            "appcompat-v7"               : "com.android.support:appcompat-v7:${android["VSupportSdk"]}",
            "support-v4"                 : "com.android.support:support-v4:${android["VSupportSdk"]}",
            "design"                     : "com.android.support:design:${android["VSupportSdk"]}",
            "annotations"                : "com.android.support:support-annotations:${android["VSupportSdk"]}",
            "recyclerview-v7"            : "com.android.support:recyclerview-v7:${android["VSupportSdk"]}",

            "butterknife"                : "com.jakewharton:butterknife:8.4.0",
            "butterknife-apt"            : "com.jakewharton:butterknife-compiler:8.4.0",
            "eventbus"                   : "org.greenrobot:eventbus:3.0.0",
            "glide"                      : "com.github.bumptech.glide:glide:3.7.0",
            "picasso"                    : "com.squareup.picasso:picasso:2.5.2",
            "xrecyclerview"              : "com.github.limedroid:ARecyclerView:v1.1.0",
            "avi-loading"                : "com.wang.avi:library:1.0.2",

            "gson"                       : "com.google.code.gson:gson:2.6.2",
            "rxandroid"                  : "io.reactivex:rxandroid:1.2.1",
            "rxjava"                     : "io.reactivex:rxjava:1.1.6",
            "retrofit"                   : "com.squareup.retrofit2:retrofit:${android["VRetrofitSdk"]}",
            "retrofit-converter-gson"    : "com.squareup.retrofit2:converter-gson:${android["VRetrofitSdk"]}",
            "retrofit-adapter-rxjava"    : "com.squareup.retrofit2:adapter-rxjava:${android["VRetrofitSdk"]}",
            "okhttp3-logging-interceptor": "com.squareup.okhttp3:logging-interceptor:${android["VOkhttp"]}",
            "okhttp3"                    : "com.squareup.okhttp3:okhttp:${android["VOkhttp"]}",
            "rxlifecycle"                : "com.trello:rxlifecycle:${android["VRxlifecycle"]}",
            "rxlifecycle-android"        : "com.trello:rxlifecycle-android:${android["VRxlifecycle"]}",
            "rxlifecycle-components"     : "com.trello:rxlifecycle-components:${android["VRxlifecycle"]}",
            "rxpermissions"              : "com.tbruyelle.rxpermissions:rxpermissions:0.9.1@aar",

            "canary-debug"               : "com.squareup.leakcanary:leakcanary-android:1.4-beta2",
            "canary-release"             : "com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2",
    ]

}

其中我們注意一個配置文件,像一些默認(rèn)圖啊疗韵,之類的東西就可以丟配置文件里面全局使用了:

public class XDroidConf {
    // #log
    public static final boolean LOG = true;
    public static final String LOG_TAG = "XDroid";

    // #cache
    public static final String CACHE_SP_NAME = "config";
    public static final String CACHE_DISK_DIR = "cache";

    // #router
    public static final int ROUTER_ANIM_ENTER = Router.RES_NONE;
    public static final int ROUTER_ANIM_EXIT = Router.RES_NONE;

    // #imageloader
    public static final int IL_LOADING_RES = ILoader.Options.RES_NONE;
    public static final int IL_ERROR_RES = ILoader.Options.RES_NONE;

    // #dev model
    public static final boolean DEV = true;
}

因?yàn)閷?dǎo)入的黃油刀是8版本的兑障,所以我們還是要修改一下我們的build.gradle之類的,

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'

android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion

    defaultConfig {
        applicationId 'lht.ly.com.lyframetest'
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode rootProject.ext.android.versionCode
        versionName rootProject.ext.android.versionName
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    apt rootProject.ext.dependencies["butterknife-apt"]
    compile rootProject.ext.dependencies["avi-loading"]
    compile project(":mvp")
}

根目錄build.gradle是:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from: "conf.gradle"
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}
allprojects {
    repositories {
        jcenter()
        maven { url "https://jitpack.io" }
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}

不出意外的話,項(xiàng)目環(huán)境就搭建完成了旺垒,我們就可以去擼代碼咯2士狻!神奇的代碼先蒋,給你寫詩一樣的感覺骇钦!

項(xiàng)目目錄

先帶著看下基類,然后我們再看具體實(shí)現(xiàn):

baseActivity

/**
 * Created by Ly on 2017/2/6.
 */

public abstract class BaseActivity<P extends IPresent> extends XActivity<P> {
    private static final String TAG = "Ly - . -";
    private Toast toast;
    private ProgressDialog progressDialog;
    /**
     * 顯示吐司
     *
     * @param msg
     */
    public void showTs(String msg) {
        if (toast != null) {
            toast.cancel();
            toast = null;
        }
        toast = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
        toast.show();
    }

    /**
     * 顯示菊花
     * 使用默認(rèn)提示
     */
    public void showDialog() {
        showDialog(getString(R.string.tips_loading));
    }

    /**
     * 顯示菊花
     *
     * @param msg
     */
    public void showDialog(String msg) {
        progressDialog = new ProgressDialog(this);
        progressDialog.setMessage(msg);
        progressDialog.show();
    }

    /**
     * 隱藏掉菊花
     */
    public void dissDialog() {
        if (progressDialog != null && progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }

    /**
     * 顯示log
     *
     * @param msg
     */
    public void showLog(String msg) {
        if (App.isBebug()) {
            Log.e(TAG, "showLog: " + msg);
        }
    }
}

baseFragment

/**
 * Created by Ly on 2017/2/5.
 */

public abstract class BaseFragment<P extends IPresent> extends XLazyFragment<P> {
    private static final String TAG = "Ly - . -";
    private Toast toast;
    private ProgressDialog progressDialog;


    /**
     * 顯示吐司
     *
     * @param msg
     */
    public void showTs(String msg) {
        if (toast != null) {
            toast.cancel();
            toast = null;
        }
        toast = Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT);
        toast.show();
    }

    /**
     * 顯示菊花
     * 使用默認(rèn)提示
     */
    public void showDialog() {
        showDialog(getString(R.string.tips_loading));
    }

    /**
     * 顯示菊花
     *
     * @param msg
     */
    public void showDialog(String msg) {
        progressDialog = new ProgressDialog(getActivity());
        progressDialog.setMessage(msg);
        progressDialog.show();
    }

    /**
     * 隱藏掉菊花
     */
    public void dissDialog() {
        if (progressDialog != null && progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }

    /**
     * 顯示log
     *
     * @param msg
     */
    public void showLog(String msg) {
        if (App.isBebug()) {
            Log.e(TAG, "showLog: " + msg);
        }
    }


    public static void launch(Activity activity) {
    }
}

哈哈哈哈竞漾,其實(shí)只是在原來的基礎(chǔ)上加了一下我自己常用的工具類而已眯搭。

看下我們的application:

/**
 * Created by Ly on 2017/2/5.
 */

public class App extends Application {

    private  Context context;

    // 全局控制(log顯示等)
    private static final boolean IS_BEBUG=true;
    @Override
    public void onCreate() {
        super.onCreate();
        context = this;

        XApi.registerProvider(new NetProvider() {
            @Override
            public Interceptor[] configInterceptors() {
                return new Interceptor[0];
            }

            @Override
            public void configHttps(OkHttpClient.Builder builder) {

            }

            @Override
            public CookieJar configCookie() {
                return null;
            }

            @Override
            public RequestHandler configHandler() {
                return null;
            }

            @Override
            public long configConnectTimeoutMills() {
                return 0;
            }

            @Override
            public long configReadTimeoutMills() {
                return 0;
            }

            @Override
            public boolean configLogEnable() {
                return true;
            }

            @Override
            public boolean handleError(NetError error) {
                return false;
            }
        });
    }

    public  Context getContext() {
        return context;
    }


    public static boolean isBebug() {
        return IS_BEBUG;
    }
}

實(shí)操部分

看下登錄界面:

看過我上一篇的朋友應(yīng)該知道,在那個項(xiàng)目里面业岁,類似登錄這個界面鳞仙,我們至少要寫4個類:
分別是M,V,P,Activity。

上一個mvp博客的項(xiàng)目目錄

但是笔时!看下我們現(xiàn)在的項(xiàng)目結(jié)構(gòu):

這一個框架的項(xiàng)目結(jié)構(gòu)

是的棍好!沒有接口! 無需寫Contract! 無需寫Present接口允耿! 無需寫View接口!

反正我說話說不清楚借笙,我們看代碼:

登錄界面的xml代碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="@dimen/activity_horizontal_margin">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0.5"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="Welcome"
            android:textColor="#333333"
            android:textSize="30sp" />

    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0.5"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:id="@+id/usernameWrapper"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <EditText
                android:id="@+id/username"
                style="@style/edit_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/text_input_username"
                android:inputType="textEmailAddress" />

        </android.support.design.widget.TextInputLayout>

        <android.support.design.widget.TextInputLayout
            android:id="@+id/passwordWrapper"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/usernameWrapper"
            android:layout_marginTop="4dp">

            <EditText
                android:id="@+id/password"
                style="@style/edit_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/text_input_password"
                android:inputType="textPassword" />

        </android.support.design.widget.TextInputLayout>


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/reg"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:layout_weight="1"
                android:text="@string/text_reg" />

            <Button
                android:id="@+id/btn"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:layout_weight="1"
                android:text="@string/login" />
        </LinearLayout>

    </LinearLayout>

</LinearLayout>
布局代碼沒什么好看的,我們看java代碼:
/**
 * Created by Ly on 2017/2/6.
 */

public class loginActivity extends BaseActivity<LoginP> {
    @BindView(R.id.username)
    EditText username;
    @BindView(R.id.usernameWrapper)
    TextInputLayout usernameWrapper;
    @BindView(R.id.password)
    EditText password;
    @BindView(R.id.passwordWrapper)
    TextInputLayout passwordWrapper;
    @BindView(R.id.reg)
    Button reg;
    @BindView(R.id.btn)
    Button btn;

    @OnClick({R.id.reg, R.id.btn})
    public void Onclick(View view) {
        switch (view.getId()) {
            case R.id.btn:
                getP().login(username.getText().toString(), password.getText().toString());
                break;
            case R.id.reg:

                break;
            default:
                break;
        }
    }

    @Override
    public void initData(Bundle savedInstanceState) {
        usernameWrapper.setHint(getString(R.string.text_input_username));
        passwordWrapper.setHint(getString(R.string.text_input_password));
    }

    @Override
    protected void dogetExtra() {

    }

    public void showUserNameErr(String err) {
        usernameWrapper.setHint(err);
    }

    public void showPasswordErr(String err) {
        passwordWrapper.setHint(err);
    }

    @Override
    public int getLayoutId() {
        return R.layout.activity_login;
    }

    @Override
    public LoginP newP() {
        return new LoginP();
    }

    public void toMain(){
        MainActivity.launch(loginActivity.this);
        finish();
    }
}

分段講析:

框架XActivity里面的3個Override:
  @Override
    public void initData(Bundle savedInstanceState) {
        usernameWrapper.setHint(getString(R.string.text_input_username));
        passwordWrapper.setHint(getString(R.string.text_input_password));
    }

其實(shí)這個可以看成oncreate()就可以了较锡,查看源碼我們可以看到:

源碼
    @Override
    public int getLayoutId() {
        return R.layout.activity_login;
    }

這個就可以看成setContentView(int layoutId);
其實(shí)也只是把它提取出來而已业稼。

    @Override
    public LoginP newP() {
        return new LoginP();
    }

而這個就是重頭戲了,P的獲取方法蚂蕴!因?yàn)樵谛枰臅r候我們可以通過getP()的方法獲取到P對象來進(jìn)行操作低散,所以這里我們要記得返回一個對象,當(dāng)然骡楼,如果你不需要用到getP(),你都可以直接忽略這個方法熔号,不對它做操作了。

黃油刀依賴注解并且注冊監(jiān)聽事件:
@BindView(R.id.username)
    EditText username;
    @BindView(R.id.usernameWrapper)
    TextInputLayout usernameWrapper;
    @BindView(R.id.password)
    EditText password;
    @BindView(R.id.passwordWrapper)
    TextInputLayout passwordWrapper;
    @BindView(R.id.reg)
    Button reg;
    @BindView(R.id.btn)
    Button btn;

    @OnClick({R.id.reg, R.id.btn})
    public void Onclick(View view) {
        switch (view.getId()) {
            case R.id.btn:
                getP().login(username.getText().toString(), password.getText().toString());
                break;
            case R.id.reg:

                break;
            default:
                break;
        }
    }

黃油刀的就不說了~

V的操作方法:

除開其他的君编,像其他幾個方法:

    public void showUserNameErr(String err) {
        usernameWrapper.setHint(err);
    }

    public void showPasswordErr(String err) {
        passwordWrapper.setHint(err);
    }

    public void toMain(){
        MainActivity.launch(loginActivity.this);
        finish();
    }

是我們自己定義在activity里面的method跨嘉,其實(shí)按我的理解來說,這個其實(shí)就是MVP里面的V里面寫的接口方法了吃嘿,應(yīng)該思路是一致的祠乃。

看下我們的P類:

/**
 * Created by Ly on 2017/2/6.
 */

public class LoginP extends XPresent<loginActivity> {


    public void login(String username, String password) {
        if (TextUtils.isEmpty(username)) {
            getV().showUserNameErr(getV().getString(R.string.tips_username_is_not_empty));
            return;
        }
        if (TextUtils.isEmpty(password)) {
            getV().showPasswordErr(getV().getString(R.string.tips_password_is_not_empty));
            return;
        }
        getV().showDialog();
        Api.getGankService().login(username, password, null, null, null).compose(XApi.<LoginBean>getApiTransformer())
                .compose(XApi.<LoginBean>getScheduler())
                .compose(getV().<LoginBean>bindToLifecycle())
                .subscribe(new ApiSubcriber<LoginBean>() {
                    @Override
                    protected void onFail(NetError error) {
                        Log.e("LHT", "onFail: " + error.getMessage());
                        getV().showTs(error.getMessage());
                    }

                    @Override
                    public void onNext(LoginBean loginBean) {
                        if (loginBean.getStatus() == 200) {
                            getV().toMain();
                        } else {
                            getV().showTs(loginBean.getMessage());
                        }
                    }

                    @Override
                    public void onCompleted() {
                        super.onCompleted();
                        getV().dissDialog();
                    }
                });
    }
}

這里給我的感覺就是M和P兩個方法的結(jié)合體:

附錄下: 這個p的調(diào)用路徑是:

調(diào)用路徑

思路是: 點(diǎn)擊按鈕 → getP()獲取P,調(diào)用login(String str,String str1)的方法 → 在login(String str,String str1)方法里面做操作(字符非法性判斷 → 不合法:getV()調(diào)用V的方法進(jìn)行提示兑燥;合法:調(diào)用網(wǎng)絡(luò)進(jìn)行登錄操作 → 結(jié)果通過getV()來進(jìn)行UI跳轉(zhuǎn)或者提示)

至此亮瓷!登錄的邏輯完成了,
整理一下思路:
1. 寫一個activity降瞳,繼承baseactivity
2. 寫一個P嘱支,繼承XPresent
3. 耗時操作通過getP()丟P里面做處理蚓胸,處理結(jié)果在P里面通過getV()調(diào)用View視圖去做顯示

主頁面(沒有P的頁面)

public class MainActivity extends BaseActivity {
    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.tabLayout)
    TabLayout tabLayout;
    @BindView(R.id.viewPager)
    ViewPager viewPager;

    List<Fragment> fragmentList = new ArrayList<>();

    XFragmentAdapter adapter;


    @Override
    public void initData(Bundle savedInstanceState) {
        setSupportActionBar(toolbar);
        fragmentList.clear();
        fragmentList.add(HomeFragment.newInstance());
        fragmentList.add(GankFragment.newInstance());
        fragmentList.add(GankFragment.newInstance());
        String[] titles = {getResources().getString(R.string.text_home),
                getResources().getString(R.string.text_gank),
                getResources().getString(R.string.text_meizhi)};
        if (adapter == null) {
            adapter = new XFragmentAdapter(getSupportFragmentManager(), fragmentList, titles);
        }

        viewPager.setAdapter(adapter);
        viewPager.setOffscreenPageLimit(3);
        tabLayout.setupWithViewPager(viewPager);
    }

    @Override
    protected void dogetExtra() {

    }

    @Override
    public int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    public Object newP() {
        return null;
    }

    /**
     * 供其他頁面調(diào)用的方法
     * 推薦唯一 進(jìn)入此頁面的方法
     *
     * @param activity
     */
    public static void launch(Activity activity) {
        Router.newIntent(activity)
                .to(MainActivity.class)
                .data(new Bundle())
                .launch();
    }
}

其中跳轉(zhuǎn)頁面建議使用:

/**
   * 供其他頁面調(diào)用的方法
   * 推薦唯一 進(jìn)入此頁面的方法
   *
   * @param activity
   */
  public static void launch(Activity activity) {
      Router.newIntent(activity)
              .to(MainActivity.class)
              .data(new Bundle())
              .launch();
  }

要傳遞的參數(shù)可以直接put進(jìn)去。

而獲取傳參的數(shù)據(jù)可以用:


  @Override
  protected void dogetExtra() {
      Intent intent = getIntent();
      if (null != intent) {
          url = intent.getStringExtra("url");
          desc = intent.getStringExtra("desc");
          initWebView();
          initToolbar();
      }
  }

因?yàn)槲冶容^喜歡用這種方法去獲取參數(shù)除师,所以我寫了一個方法進(jìn)去沛膳,查看源碼可以看到:


獲取參數(shù)的方法

MainActiviy說完了,我們來說下加載的fragment汛聚,相對應(yīng)的操作會更多

public class HomeFragment extends BaseFragment<PBasePager> {
  /**
   * 上下拉的recyclerview
   */
  @BindView(R.id.contentLayout)
  XRecyclerContentLayout contentLayout;

  /**
   * 請求的參數(shù)
   */
  private String loadFlags = "all";
  /**
   * 適配器
   */
  private HomeAdapter homeAdapter;

  /**
   * 發(fā)生錯誤的時候加載的視圖
   */
  private StateView errorView;

  /**
   * newInstance 獲取當(dāng)前fragment
   *
   * @return
   */
  public static HomeFragment newInstance() {
      Bundle args = new Bundle();
      HomeFragment fragment = new HomeFragment();
      fragment.setArguments(args);
      return fragment;
  }


  @Override
  public void initData(Bundle savedInstanceState) {
      if (errorView == null) {
          //  初始化錯誤視圖
          errorView = new StateView(context);
      }
      //  設(shè)置錯誤視圖
      contentLayout.errorView(errorView);
      //  設(shè)置為豎直方向的recyclerview
      contentLayout.getRecyclerView().verticalLayoutManager(context);
      homeAdapter = new HomeAdapter(getActivity());
      contentLayout.getRecyclerView().setAdapter(homeAdapter);
      // 設(shè)置recyclerview的loadmore UI
      contentLayout.loadingView(View.inflate(getContext(), R.layout.view_loading, null));
      contentLayout.getRecyclerView().useDefLoadMoreView();
      // item的點(diǎn)擊事件
      homeAdapter.setRecItemClick(new RecyclerItemCallback<GankResults.Item, HomeAdapter.ViewHolder>() {
          @Override
          public void onItemClick(int position, GankResults.Item model, int tag, HomeAdapter.ViewHolder holder) {
              super.onItemClick(position, model, tag, holder);
              showLog(model.getUrl() + "-----");
              homeDetailActivity.launch(getActivity(), model.getUrl(), model.getDesc());
          }
      });
      // 上下拉的方法實(shí)現(xiàn)
      contentLayout.getRecyclerView().setOnRefreshAndLoadMoreListener(new XRecyclerView.OnRefreshAndLoadMoreListener() {
          @Override
          public void onRefresh() {
              getP().loadData(loadFlags, 1);
          }

          @Override
          public void onLoadMore(int page) {
              getP().loadData(loadFlags, page);
          }
      });
      // 請求網(wǎng)絡(luò)
      getP().loadData(loadFlags, 1);
  }

  @Override
  public int getLayoutId() {
      return R.layout.fragment_home;
  }

  @Override
  public PBasePager newP() {
      return new PBasePager();
  }


  /**
   * 按照錯誤類別進(jìn)行加載錯誤視圖
   *
   * @param error
   */
  public void showError(NetError error) {
      if (error != null) {
          showLog(error.getMessage());
          switch (error.getType()) {
              case NetError.ParseError:
                  errorView.setMsg(getString(R.string.err_model));
                  break;

              case NetError.AuthError:
                  errorView.setMsg(getString(R.string.err_certification));
                  break;

              case NetError.BusinessError:
                  errorView.setMsg(getString(R.string.err_business));
                  break;

              case NetError.NoConnectError:
                  errorView.setMsg(getString(R.string.err_net));
                  break;

              case NetError.NoDataError:
                  errorView.setMsg(getString(R.string.err_empty));
                  break;

              case NetError.OtherError:
                  errorView.setMsg(getString(R.string.err_unknow));
                  break;
          }
          contentLayout.showError();
      }
  }

  /**
   *  網(wǎng)絡(luò)請求獲取數(shù)據(jù)后 調(diào)用的方法
   * @param page
   * @param gankResults
   */
  public void showData(int page, GankResults gankResults) {
      // 設(shè)置分頁加載的頁碼
      contentLayout.getRecyclerView().setPage(page, 10);
      if (page == 1) {
          // 更新數(shù)據(jù)
          homeAdapter.setData(gankResults.getResults());
      } else {
          // 新增數(shù)據(jù)
          homeAdapter.addData(gankResults.getResults());
      }
  }
}

我們看下P方法里面對他做了什么:

/**
 * Created by Ly on 2017/2/5.
 */

public class PBasePager extends XPresent<HomeFragment> {
    // 默認(rèn)每頁加載10張  可以把這個丟在參數(shù)列表里面進(jìn)行動態(tài)修改
    protected static final int PAGE_SIZE = 10;

    public void loadData(String type, final int page) {
        OtherApi.getGankService().getGankData(type, PAGE_SIZE, page)
                .compose(XApi.<GankResults>getApiTransformer())
                .compose(XApi.<GankResults>getScheduler())
                .compose(getV().<GankResults>bindToLifecycle())
                .subscribe(new ApiSubcriber<GankResults>() {
                    @Override
                    protected void onFail(NetError error) {
                        getV().showError(error);
                    }

                    @Override
                    public void onNext(GankResults gankResults) {
                        getV().showData(page, gankResults);
                    }
                });
    }
}

分段解析:

初始化設(shè)置并且請求數(shù)據(jù)

初始化設(shè)置并且請求數(shù)據(jù)

其實(shí)上面的通配方法都可以不用看的锹安,recyclerview的設(shè)置,上下拉的設(shè)置倚舀,老油條和萌新們都是懂的叹哭,沒啥好說。

這里完成可以類比為: 在oncreate()里面請求數(shù)據(jù)痕貌,只不過這里的請求數(shù)據(jù)的方法是丟到了P方法里面去做而已风罩,

P方法里面的數(shù)據(jù)操作:

數(shù)據(jù)請求

ApiSubcriber方法里面有2個回調(diào),我們在onnext()里面做顯示數(shù)據(jù)的操作:

往adapter添加數(shù)據(jù)
通知適配器去刷新
其實(shí)就是拿到數(shù)據(jù)舵稠,往適配器里面的list填充數(shù)據(jù)超升,完事后通知適配器對象說:(????)??嗨,哥們哺徊,數(shù)據(jù)更新了廓俭,你給刷新下頁面notifyDataSetChanged()一下唄!
Ps: 很多朋友喜歡把list放在適配器外面唉工,雖然沒問題!但是我還是比較推薦我們用內(nèi)部的list汹忠,方便操作淋硝,通過adapter對外暴露數(shù)據(jù)接口,更安全宽菜,更不會出錯谣膳! 個人建議。

對應(yīng)的铅乡,如果數(shù)據(jù)失敗了呢继谚,顯示emptyView或者errView! 這個是開發(fā)里面常常需要的,
我們可以通過這個方法去實(shí)現(xiàn):

實(shí)現(xiàn)errView

配置header部分:

因?yàn)楹笈_改革阵幸,所以我們前端需要自己配置header來進(jìn)行請求花履,玩過retrofit的人都知道,要改東西....好吧挚赊,加個攔截器诡壁。

單一修改對應(yīng)的header:

 /**
     * 登錄接口
     *
     * @param username
     * @param password
     * @return
     */
    @Headers({
            "Content-Type:application/json;charset=ISO-8859-1"
    })
    @FormUrlEncoded
    @POST("api/login")
    Flowable<LoginBean> doLogin(@Field("username") String username,
                                @Field("password") String password);

如果我們要對一個接口進(jìn)行單獨(dú)的header修改,那么我們可以使用@Header的方法來進(jìn)行修改荠割。

全局修改所有的header:

如果每個項(xiàng)目都需要有一個共同的header妹卿,然后里面放一些必要的參數(shù)旺矾,比如token之類的,那如果手動一個個地添加夺克,那我肯定選擇死亡的箕宙。
我們可以在applicaion里面添加一個攔截器,從而做到全局修改的目的:

 private void initMvpConf() {
        XApi.registerProvider(new NetProvider() {
            @Override
            public Interceptor[] configInterceptors() {
                Interceptor mTokenInterceptor = new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request originalRequest = chain.request();
                        originalRequest.newBuilder().addHeader("Content-type", "application/json")
                                .addHeader("Accept", "application/json");
                        return chain.proceed(originalRequest);
                    }
                };
                Interceptor arr[] = new Interceptor[]{mTokenInterceptor};
                return arr;
            }

            @Override
            public void configHttps(OkHttpClient.Builder builder) {
            }

            @Override
            public CookieJar configCookie() {
                return new CookieJar() {
                    private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
                    private HttpUrl mUrl;

                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {  // 登錄的時候保存cookie
                        if (TextUtils.equals(url.toString(), HttpRequest.API_BASE_URL + "memberAppAction!login.do")) {
                            mUrl = url;
                            cookieStore.put(url, cookies);
                        }
                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {  // 請求數(shù)據(jù)時候在把保存的cookie攜帶過去
                        List<Cookie> cookies = cookieStore.get(mUrl);
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                };
            }

            @Override
            public RequestHandler configHandler() {
                return new RequestHandler() {
                    @Override
                    public Request onBeforeRequest(Request request, Interceptor.Chain chain) {
                        return request.newBuilder()
                                .addHeader("Accept", "*/*")
                                .addHeader("Content-Type", "application/json;charset=ISO-8859-1")
                                .build();
                    }

                    @Override
                    public Response onAfterRequest(Response response, String result, Interceptor.Chain chain) {
                        return response;
                    }
                };
            }

            @Override
            public long configConnectTimeoutMills() {
                return 0;
            }

            @Override
            public long configReadTimeoutMills() {
                return 0;
            }

            @Override
            public boolean configLogEnable() {
                return true;
            }

            @Override
            public boolean handleError(NetError error) {
                return false;
            }
        });
    }

其中我們注意 configHandler 這個方法铺纽,我們可以看到 有2個回調(diào)方法: onBeforeRequest onAfterRequest

我們在onBeforeRequest中返回返回一個Request柬帕,我們可以通過它來進(jìn)行header的添加:

 return request.newBuilder()
      .addHeader("Accept", "*/*")
      .addHeader("Content-Type", "application/json;charset=ISO-8859-1")
      .build();

同時我們在onAfterRequest方法中return 返回的Response,表示我們使用這個室囊。

修改對應(yīng)的cookie

有時候后臺不返回前端token雕崩,而在服務(wù)器保存了,這個時候就需要我們前端保持了cookie融撞,那我們要怎么保存呢盼铁?
同樣是在application里面的一個方法,我們需要保存了cookie:

 @Override
            public CookieJar configCookie() {
                return new CookieJar() {
                    private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
                    private HttpUrl mUrl;

                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {  // 登錄的時候保存cookie
                        if (TextUtils.equals(url.toString(), HttpRequest.API_BASE_URL + "memberAppAction!login.do")) {
                            mUrl = url;
                            cookieStore.put(url, cookies);
                        }
                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {  // 請求數(shù)據(jù)時候在把保存的cookie攜帶過去
                        List<Cookie> cookies = cookieStore.get(mUrl);
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                };
            }

其中我們保存了一個url返回的cookie尝偎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饶火,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子致扯,更是在濱河造成了極大的恐慌肤寝,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抖僵,死亡現(xiàn)場離奇詭異鲤看,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)耍群,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門义桂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蹈垢,你說我怎么就攤上這事慷吊。” “怎么了曹抬?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵溉瓶,是天一觀的道長。 經(jīng)常有香客問我谤民,道長堰酿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任赖临,我火速辦了婚禮胞锰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兢榨。我一直安慰自己嗅榕,他們只是感情好顺饮,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凌那,像睡著了一般兼雄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帽蝶,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天赦肋,我揣著相機(jī)與錄音,去河邊找鬼励稳。 笑死佃乘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驹尼。 我是一名探鬼主播趣避,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼新翎!你這毒婦竟也來了程帕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤地啰,失蹤者是張志新(化名)和其女友劉穎愁拭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亏吝,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岭埠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔚鸥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枫攀。...
    茶點(diǎn)故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖株茶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情图焰,我是刑警寧澤启盛,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站技羔,受9級特大地震影響僵闯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜藤滥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一鳖粟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拙绊,春花似錦向图、人聲如沸泳秀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗜傅。三九已至,卻和暖如春檩赢,著一層夾襖步出監(jiān)牢的瞬間吕嘀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工贞瞒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留偶房,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓军浆,卻偏偏與公主長得像棕洋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瘾敢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理拍冠,服務(wù)發(fā)現(xiàn),斷路器簇抵,智...
    卡卡羅2017閱讀 134,638評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,858評論 25 707
  • 從三月份找實(shí)習(xí)到現(xiàn)在庆杜,面了一些公司,掛了不少碟摆,但最終還是拿到小米晃财、百度、阿里典蜕、京東断盛、新浪、CVTE愉舔、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,218評論 11 349
  • 今天我們常說钢猛,一方水土一方人。人們每到一個地方轩缤,都要問一問有什么特產(chǎn)命迈,如果有,一定要嘗一嘗火的,這對于那些旅游的人來說...
    五味先生閱讀 285評論 3 3
  • 朋友問,怎么突然想到去上海湃累,我回勃救,除了想去老胡的餐廳坐一坐碍讨,這一次,我還想住進(jìn)一間當(dāng)?shù)氐拿袼蕖?最近一段時間Air...
    野性麗麗閱讀 1,363評論 0 15