二次封裝MVP框架

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

配置部分

看下項目目錄:

項目目錄

在導(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",
    ]

}

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

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;
}

因為導(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
}

不出意外的話,項目環(huán)境就搭建完成了,我們就可以去擼代碼咯??!神奇的代碼,給你寫詩一樣的感覺!

項目目錄

先帶著看下基類,然后我們再看具體實現(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();
    }

    /**
     * 顯示菊花
     * 使用默認提示
     */
    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();
    }

    /**
     * 顯示菊花
     * 使用默認提示
     */
    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) {
    }
}

哈哈哈哈,其實只是在原來的基礎(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;
    }
}

實操部分

看下登錄界面:

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

上一個mvp博客的項目目錄

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

這一個框架的項目結(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));
    }

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

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

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

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

而這個就是重頭戲了,P的獲取方法!因為在需要的時候我們可以通過getP()的方法獲取到P對象來進行操作,所以這里我們要記得返回一個對象,當(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,其實按我的理解來說,這個其實就是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)用路徑

思路是: 點擊按鈕 → getP()獲取P,調(diào)用login(String str,String str1)的方法 → 在login(String str,String str1)方法里面做操作(字符非法性判斷 → 不合法:getV()調(diào)用V的方法進行提示;合法:調(diào)用網(wǎng)絡(luò)進行登錄操作 → 結(jié)果通過getV()來進行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)用的方法
     * 推薦唯一 進入此頁面的方法
     *
     * @param activity
     */
    public static void launch(Activity activity) {
        Router.newIntent(activity)
                .to(MainActivity.class)
                .data(new Bundle())
                .launch();
    }
}

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

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

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

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


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

因為我比較喜歡用這種方法去獲取參數(shù),所以我寫了一個方法進去,查看源碼可以看到:


獲取參數(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的點擊事件
      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());
          }
      });
      // 上下拉的方法實現(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();
  }


  /**
   * 按照錯誤類別進行加載錯誤視圖
   *
   * @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> {
    // 默認每頁加載10張  可以把這個丟在參數(shù)列表里面進行動態(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ù)

其實上面的通配方法都可以不用看的,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ù)據(jù),往適配器里面的list填充數(shù)據(jù),完事后通知適配器對象說:(????)??嗨,哥們,數(shù)據(jù)更新了,你給刷新下頁面notifyDataSetChanged()一下唄!
Ps: 很多朋友喜歡把list放在適配器外面,雖然沒問題!但是我還是比較推薦我們用內(nèi)部的list,方便操作,通過adapter對外暴露數(shù)據(jù)接口,更安全,更不會出錯! 個人建議。

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

實現(xiàn)errView

配置header部分:

因為后臺改革,所以我們前端需要自己配置header來進行請求,玩過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);

如果我們要對一個接口進行單獨的header修改,那么我們可以使用@Header的方法來進行修改。

全局修改所有的header:

如果每個項目都需要有一個共同的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,我們可以通過它來進行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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,769評論 25 709
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,757評論 11 349
  • 今天我們常說,一方水土一方人。人們每到一個地方,都要問一問有什么特產(chǎn),如果有,一定要嘗一嘗,這對于那些旅游的人來說...
    五味先生閱讀 329評論 3 3
  • 朋友問,怎么突然想到去上海,我回,除了想去老胡的餐廳坐一坐,這一次,我還想住進一間當(dāng)?shù)氐拿袼蕖?最近一段時間Air...
    野性麗麗閱讀 1,478評論 0 15

友情鏈接更多精彩內(nèi)容