在github淘到的mvp框架友浸,年前就一直想要研究下梢杭,但是年前比較忙,就在過年期間有空弄了一下栏账,今天寫下心得帖族,不出意料地話接下來就會用這個框架進(jìn)行項(xiàng)目開發(fā)了,只能先從別人那里取到經(jīng)挡爵,才能有經(jīng)驗(yàn)來玩自己的框架竖般。
?OK,先上github地址茶鹃,大家可以加群找那位大神探討探討涣雕,真·大神!
傳送門
配置部分
看下項(xiàng)目目錄:
在導(dǎo)入mvp的依賴后闭翩,要導(dǎo)入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",
]
}
其中我們注意一個配置文件,像一些默認(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士狻!神奇的代碼先蒋,給你寫詩一樣的感覺骇钦!
先帶著看下基類,然后我們再看具體實(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。
但是笔时!看下我們現(xiàn)在的項(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ǎ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)去沛膳,查看源碼可以看到:
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í)上面的通配方法都可以不用看的锹安,recyclerview的設(shè)置,上下拉的設(shè)置倚舀,老油條和萌新們都是懂的叹哭,沒啥好說。
這里完成可以類比為: 在oncreate()里面請求數(shù)據(jù)痕貌,只不過這里的請求數(shù)據(jù)的方法是丟到了P方法里面去做而已风罩,
P方法里面的數(shù)據(jù)操作:
ApiSubcriber方法里面有2個回調(diào),我們在onnext()里面做顯示數(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):
配置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尝偎。