二次封装MVP框架

在github淘到的mvp框架,年前就一直想要研究下,但是年前比较忙,就在过年期间有空弄了一下,今天写下心得,不出意料地话接下来就会用这个框架进行项目开发了,只能先从别人那里取到经,才能有经验来玩自己的框架。
 OK,先上github地址,大家可以加群找那位大神探讨探讨,真·大神!
传送门

配置部分

看下项目目录:

项目目录

在导入mvp的依赖后,要导入conf.gradle文件:


要导入的配置文件

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

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

因为导入的黄油刀是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
}

不出意外的话,项目环境就搭建完成了,我们就可以去撸代码咯!!神奇的代码,给你写诗一样的感觉!

项目目录

先带着看下基类,然后我们再看具体实现:

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) {
    }
}

哈哈哈哈,其实只是在原来的基础上加了一下我自己常用的工具类而已。

看下我们的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;
    }
}

实操部分

看下登录界面:

看过我上一篇的朋友应该知道,在那个项目里面,类似登录这个界面,我们至少要写4个类:
分别是M,V,P,Activity。

上一个mvp博客的项目目录

但是!看下我们现在的项目结构:

这一个框架的项目结构

是的!没有接口! 无需写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对象来进行操作,所以这里我们要记得返回一个对象,当然,如果你不需要用到getP(),你都可以直接忽略这个方法,不对它做操作了。

黄油刀依赖注解并且注册监听事件:
@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里面写的接口方法了,应该思路是一致的。

看下我们的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两个方法的结合体:

附录下: 这个p的调用路径是:

调用路径

思路是: 点击按钮 → getP()获取P,调用login(String str,String str1)的方法 → 在login(String str,String str1)方法里面做操作(字符非法性判断 → 不合法:getV()调用V的方法进行提示;合法:调用网络进行登录操作 → 结果通过getV()来进行UI跳转或者提示)

至此!登录的逻辑完成了,
整理一下思路:
1. 写一个activity,继承baseactivity
2. 写一个P,继承XPresent
3. 耗时操作通过getP()丢P里面做处理,处理结果在P里面通过getV()调用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;
    }

    /**
     * 供其他页面调用的方法
     * 推荐唯一 进入此页面的方法
     *
     * @param activity
     */
    public static void launch(Activity activity) {
        Router.newIntent(activity)
                .to(MainActivity.class)
                .data(new Bundle())
                .launch();
    }
}

其中跳转页面建议使用:

/**
   * 供其他页面调用的方法
   * 推荐唯一 进入此页面的方法
   *
   * @param activity
   */
  public static void launch(Activity activity) {
      Router.newIntent(activity)
              .to(MainActivity.class)
              .data(new Bundle())
              .launch();
  }

要传递的参数可以直接put进去。

而获取传参的数据可以用:


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

因为我比较喜欢用这种方法去获取参数,所以我写了一个方法进去,查看源码可以看到:


获取参数的方法

MainActiviy说完了,我们来说下加载的fragment,相对应的操作会更多

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

  /**
   * 请求的参数
   */
  private String loadFlags = "all";
  /**
   * 适配器
   */
  private HomeAdapter homeAdapter;

  /**
   * 发生错误的时候加载的视图
   */
  private StateView errorView;

  /**
   * newInstance 获取当前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);
      }
      //  设置错误视图
      contentLayout.errorView(errorView);
      //  设置为竖直方向的recyclerview
      contentLayout.getRecyclerView().verticalLayoutManager(context);
      homeAdapter = new HomeAdapter(getActivity());
      contentLayout.getRecyclerView().setAdapter(homeAdapter);
      // 设置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());
          }
      });
      // 上下拉的方法实现
      contentLayout.getRecyclerView().setOnRefreshAndLoadMoreListener(new XRecyclerView.OnRefreshAndLoadMoreListener() {
          @Override
          public void onRefresh() {
              getP().loadData(loadFlags, 1);
          }

          @Override
          public void onLoadMore(int page) {
              getP().loadData(loadFlags, page);
          }
      });
      // 请求网络
      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();
      }
  }

  /**
   *  网络请求获取数据后 调用的方法
   * @param page
   * @param gankResults
   */
  public void showData(int page, GankResults gankResults) {
      // 设置分页加载的页码
      contentLayout.getRecyclerView().setPage(page, 10);
      if (page == 1) {
          // 更新数据
          homeAdapter.setData(gankResults.getResults());
      } else {
          // 新增数据
          homeAdapter.addData(gankResults.getResults());
      }
  }
}

我们看下P方法里面对他做了什么:

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

public class PBasePager extends XPresent<HomeFragment> {
    // 默认每页加载10张  可以把这个丢在参数列表里面进行动态修改
    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);
                    }
                });
    }
}

分段解析:

初始化设置并且请求数据

初始化设置并且请求数据

其实上面的通配方法都可以不用看的,recyclerview的设置,上下拉的设置,老油条和萌新们都是懂的,没啥好说。

这里完成可以类比为: 在oncreate()里面请求数据,只不过这里的请求数据的方法是丢到了P方法里面去做而已,

P方法里面的数据操作:

数据请求

ApiSubcriber方法里面有2个回调,我们在onnext()里面做显示数据的操作:

往adapter添加数据
通知适配器去刷新
其实就是拿到数据,往适配器里面的list填充数据,完事后通知适配器对象说:(。・∀・)ノ゙嗨,哥们,数据更新了,你给刷新下页面notifyDataSetChanged()一下呗!
Ps: 很多朋友喜欢把list放在适配器外面,虽然没问题!但是我还是比较推荐我们用内部的list,方便操作,通过adapter对外暴露数据接口,更安全,更不会出错! 个人建议。

对应的,如果数据失败了呢,显示emptyView或者errView! 这个是开发里面常常需要的,
我们可以通过这个方法去实现:

实现errView

配置header部分:

因为后台改革,所以我们前端需要自己配置header来进行请求,玩过retrofit的人都知道,要改东西....好吧,加个拦截器。

单一修改对应的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,然后里面放一些必要的参数,比如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) {  // 请求数据时候在把保存的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个回调方法: onBeforeRequest onAfterRequest

我们在onBeforeRequest中返回返回一个Request,我们可以通过它来进行header的添加:

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

同时我们在onAfterRequest方法中return 返回的Response,表示我们使用这个。

修改对应的cookie

有时候后台不返回前端token,而在服务器保存了,这个时候就需要我们前端保持了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) {  // 请求数据时候在把保存的cookie携带过去
                        List<Cookie> cookies = cookieStore.get(mUrl);
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                };
            }

其中我们保存了一个url返回的cookie。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,899评论 25 707
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,221评论 11 349
  • 今天我们常说,一方水土一方人。人们每到一个地方,都要问一问有什么特产,如果有,一定要尝一尝,这对于那些旅游的人来说...
    五味先生阅读 285评论 3 3
  • 朋友问,怎么突然想到去上海,我回,除了想去老胡的餐厅坐一坐,这一次,我还想住进一间当地的民宿。 最近一段时间Air...
    野性丽丽阅读 1,363评论 0 15