在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。
但是!看下我们现在的项目结构:
是的!没有接口! 无需写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()里面做显示数据的操作:
其实就是拿到数据,往适配器里面的list填充数据,完事后通知适配器对象说:(。・∀・)ノ゙嗨,哥们,数据更新了,你给刷新下页面notifyDataSetChanged()一下呗!
Ps: 很多朋友喜欢把list放在适配器外面,虽然没问题!但是我还是比较推荐我们用内部的list,方便操作,通过adapter对外暴露数据接口,更安全,更不会出错! 个人建议。
对应的,如果数据失败了呢,显示emptyView或者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。