大家好,我又來了,今天抽點時間來把上次沒分享完的分享完一下,也是在把基礎框架運用到實際項目的第一週,順便把我上次想分享而又來不及的另一種基礎封裝中的獲取數據方式分享一下
沒上看一篇的同學可以先去了解了解哈
一、話不多說,先上更改的獲取服務器數據的代碼
修改獲取服務器數據方式後的代碼片段 BaseGetDataActivity
public abstract class BaseGetDataActivity<T> extends BaseHeaderActivity {
public T t;
@Override
public void initData() {
intoHttp();
}
public void intoHttp() {
HttpClient.getInstance().doWithToken(this, getObservable(), new CallbackObserver<T>(this) {
@Override
public void onSuccess(BaseResponse<T> httpResult) {
t = httpResult.result;
initRefreshView();
}
});
}
public abstract void initRefreshView();
public abstract Observable<BaseResponse<T>> getObservable();
}
doWithToken方法,我是放在一個統一管理類中的
public <T> void doWithToken(Context context, Observable<BaseResponse<T>> observable, CallbackObserver<T> callback) {
observable.compose(SchedulerProvider.getInstance().observableIO2Main(context)).subscribe(callback);
}
區別我想大家應該都看明白了,就是在於把Observable<BaseResponse>放到實現類去實現,避免了泛型擦除的問題,從而使得直接可以拿到數據結果,並且可以在BaseGetDataActivity中把所有錯誤情況預先默認處理一遍,特殊的在重寫父類方法進行處理,也不需要實現url方法和map方法了,可以直接在ApiService類中寫明接口訪問方式和參數。
接口實現方式前後對比
@GET
Observable<ResponseBody> executeGet(@Url String url,@QueryMap Map<String, String> mapsP);
@GET("v1/xxxxx/xxxxxx")
Observable<BaseBean> updateItemStatu(@Query("itemId") String itemId);
個人比較喜歡後者,也更優一些,但是前者的也不失爲節省代碼量,減少bug的好方法哦,至於
Headers大家可以在okhttp中addInterceptor攔截添加,處理。
訪問網絡錯誤的處理邏輯我統一封裝在了CallbackObserver中,繼承DisposableObserver<BaseResponse>實現,由於不同的業務有着不同的處理,這裏就不貼出來了,大致就是onStart中是否彈出加載框,onComplete中非空隱藏加載框,onError中對常見錯誤的Toast,onNext中根據服務器返回的數據基礎模型code做出相應的處理,例如Token失效重新登陸等等的統一處理。
當然如果項目需要,比如處理一些網絡異常的通用界面展示,無網絡界面展示等等,那也可以重寫CallbackObserver中的onError方法在BaseGetDataActivity中統一處理操作,這樣做的好處就是節省代碼量,節省時間,避免每次碰到這種界面都需要捋一遍邏輯代碼在寫,一次成功,永無bug
根據BaseGetDataActivity同理可得單純的提交數據類的寫法
二、單純的提交數據類的寫法 BaseUpDataActivity
public abstract class BaseUpDataActivity<T> extends BaseHeaderActivity {
public T t;
public void intoHttp() {
HttpClient.getInstance().doWithToken(this, getObservable(), new CallbackObserver<T>(this) {
@Override
public void onSuccess(BaseResponse<T> httpResult) {
t = httpResult.result;
initRefreshView();
}
});
}
/**
* 默認提交數據完成的操作
*/
public void initRefreshView() {
toFinish();
}
public abstract Observable<BaseResponse<T>> getObservable();
}
在上面已經解釋了BaseGetDataActivity的操作了,那這裏就沒什麼解釋的必要了,直接來上篇省略了的既要拉取數據,又要提交數據的Base封裝吧,國際慣例,先上代碼
三、既要拉取數據,又要提交數據的Base封 BaseDataActivity
BaseDataActivity
public abstract class BaseDataActivity<T, A> extends BaseHeaderActivity {
public T t;//拉取數據的bean
public A a;//提交數據服務器返回的bean,一半都返回BaseResponse就夠了,
@Override
public void initData() {
doGetData(this);
}
public void doGetData() {
HttpClient.getInstance().doWithToken(this, getObservable(), new CallbackObserver<T>() {
@Override
public void onSuccess(BaseResponse<T> httpResult) {
t = httpResult.result;
initRefreshView();
}
});
}
public void doGetData(Context context) {
HttpClient.getInstance().doWithToken(this, getObservable(), new CallbackObserver<T>(context) {
@Override
public void onSuccess(BaseResponse<T> httpResult) {
t = httpResult.result;
initRefreshView();
}
});
}
public void doUpData() {
HttpClient.getInstance().doWithToken(this, upObservable(), new CallbackObserver<A>() {
@Override
public void onSuccess(BaseResponse<A> httpResult) {
a = httpResult.result;
if (a != null)
initUpDataRefresh();
}
});
}
public void doUpData(Context context) {
HttpClient.getInstance().doWithToken(this, upObservable(), new CallbackObserver<A>(context) {
@Override
public void onSuccess(BaseResponse<A> httpResult) {
a = httpResult.result;
if (a != null)
initUpDataRefresh();
}
});
}
public void doUpDataGetData() {
upObservable().compose(SchedulerProvider.getInstance().observableIO2IO(this)).flatMap((Function<BaseResponse<A>, ObservableSource<BaseResponse<T>>>) base -> {
if (base != null) {
if (base.getCode() == Constant.ACCESS_SUCCESSFUL) {
return getObservable();
}
BaseResponse<T> baseResponse=new BaseResponse<>();
baseResponse.code=base.code;
baseResponse.message=base.message;
baseResponse.result=null;
return Observable.just(baseResponse) ;
}
return null;
}).observeOn(AndroidSchedulers.mainThread()).subscribe(new CallbackObserver<T>(){
@Override
public void onSuccess(BaseResponse<T> httpResult) {
hideLoading();
if(httpResult==null)return ;
t = httpResult.result;
if (t != null)
initRefreshView();
}
});
}
public abstract void initRefreshView();
public abstract void initUpDataRefresh();
public abstract Observable<BaseResponse<T>> getObservable();
public abstract Observable<BaseResponse<A>> upObservable();
}
一個個來分析,doGetData,doUpData兩個方法爲什麼要重載呢,一開始我也沒有寫重載的,而是每次訪問都會加載loading,一半情況呢,也是夠用的,但是有些業務,需要在你提交數據之後再次刷新界面改變狀態,讀取一些數據的時候,那麼loading就衝突了,於是就有了重載,控制loading的顯示與不顯示,帶context就代表需要loading,
但是我又一想,既然提交之後又要拉取,那我何不利用rxjava的操作符合並一下接口,flatMap,在提交接口訪問完成並且成功的時候調用刷新接口,兩者合併,也就是方法doUpDataGetData,實現類這邊就不展示了,因爲沒有寫test類,直接寫的實際業務功能,不便展示,接下來就是列表界面代碼修改片段了
三、列表類的訪問服務器修改
修改了訪問服務器的,還修改了空界面,錯誤界面,無網絡界面處理,添加了getHeaderUiView,addFooterUiView,非列表中的頭和尾,更具有包容性
BaseDataActivity
public abstract class BaseItemActivity<T> extends BaseHeaderActivity implements AdapterCoverHelper<T> {
@BindView(R.id.mRecyclerView)
public RecyclerView mRecyclerView;
@BindView(R.id.mNestedScrollView)
public NestedScrollView mNestedScrollView;
@BindView(R.id.refreshLayout)
public SmartRefreshLayout refreshLayout;
@BindView(R.id.img_empty)
ImageView imgEmpty;
@BindView(R.id.view_empty_msg)
TextView viewEmptyMsg;
@BindView(R.id.view_empty_layout)
LinearLayout viewEmptyLayout;
@BindView(R.id.view_error_msg)
TextView viewErrorMsg;
@BindView(R.id.view_error_layout)
LinearLayout viewErrorLayout;
@BindView(R.id.view_no_connected_layout)
FrameLayout viewNoConnectedLayout;
@BindView(R.id.layout_header_view)
public FrameLayout layoutHeaderView;
@BindView(R.id.layout_footer_view)
public FrameLayout layoutFooterView;
public List<T> items = new ArrayList<>();
public BaseItemAdapter<T> baseItemAdapter;
public int pageNo = 1, pageSize = 20;
public boolean isLoadMore = true;
@Override
public int getContentLayoutId() {
return R.layout.activity_baseitem;
}
@Override
protected void initView() {
super.initView();
initRecyclerView();
initAdapterView();
}
public void initRecyclerView() {
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
initDividerItemDecoration();
mRecyclerView.setFocusable(false);
}
public void initAdapterView() {
baseItemAdapter = new BaseItemAdapter(mRecyclerView, getItemLayoutId(), this);
baseItemAdapter.setData(items);
//baseItemAdapter.notifyDataSetChanged();
if (getHeaderView() != null) baseItemAdapter.addHeaderView(getHeaderView());
if (addFooterView() != null) baseItemAdapter.addFooterView(addFooterView());
if (getHeaderView() == null && addFooterView() == null)
mRecyclerView.setAdapter(baseItemAdapter);
else
mRecyclerView.setAdapter(baseItemAdapter.getHeaderAndFooterAdapter());
if (getHeaderUiView() != null) layoutHeaderView.addView(getHeaderUiView());
if (addFooterUiView() != null) layoutFooterView.addView(addFooterUiView());
}
@Override
public void initListener() {
refreshLayout.setRefreshHeader(new ClassicsHeader(this));
refreshLayout.setRefreshFooter(new ClassicsFooter(this));
refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshlayout) {
if (isLoadMore)
isOpenLoadMore(true);
pageNo = 1;
items.clear();
initHttpData();
}
});
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(RefreshLayout refreshlayout) {
initHttpData();
}
});
refreshLayout.autoRefresh();
}
public void initHttpData() {
if (isIntoHttp())
doGetWithToken();
}
public abstract int getItemLayoutId();
public abstract Observable<BaseResponse<List<T>>> getObservable();
public boolean isDividerItemDecoration() {
return true;
}
public void initDividerItemDecoration() {
if (isDividerItemDecoration()) {
DividerItemDecoration divider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
divider.setDrawable(ContextCompat.getDrawable(this, R.drawable.recommend_item_divider));
mRecyclerView.addItemDecoration(divider);
}else {
mRecyclerView.addItemDecoration(new DividerItemVerticalDefault());
}
}
/**
* 是否進來就加載
*
* @return
*/
public boolean isIntoHttp() {
return true;
}
public View getHeaderView() {
return null;
}
public View addFooterView() {
return null;
}
public View getHeaderUiView() {
return null;
}
public View addFooterUiView() {
return null;
}
public void initSuccess(BaseResponse<List<T>> response) {
if (isSize(response.result)) {
initSetState(Constant.STATE_SUCCESS);
items.addAll(response.result);
baseItemAdapter.setData(items);
} else {
initSetState(Constant.STATE_EMPTY);
isOpenLoadMore(false);
}
}
public void initSuccess(List<T> response) {
if (isSize(response)) {
initSetState(Constant.STATE_SUCCESS);
items.addAll(response);
baseItemAdapter.setData(items);
} else {
initSetState(Constant.STATE_EMPTY);
isOpenLoadMore(false);
}
}
public void initError(Throwable response) {
ToastUtil.showToastError(response.getMessage());
initErrorFinishRefreshAndLoadMore();
}
public void initSetState(int state) {
switch (state) {
case Constant.STATE_SUCCESS:
initSuccessFinishRefreshAndLoadMore();
viewEmptyLayout.setVisibility(View.GONE);
viewErrorLayout.setVisibility(View.GONE);
viewNoConnectedLayout.setVisibility(View.GONE);
break;
case Constant.STATE_EMPTY:
initErrorFinishRefreshAndLoadMore();
if (baseItemAdapter.getData().size() == 0) {
viewEmptyLayout.setVisibility(View.VISIBLE);
viewErrorLayout.setVisibility(View.GONE);
viewNoConnectedLayout.setVisibility(View.GONE);
}
break;
case Constant.STATE_ERROR:
initErrorFinishRefreshAndLoadMore();
if (baseItemAdapter.getData().size() == 0) {
viewEmptyLayout.setVisibility(View.GONE);
viewErrorLayout.setVisibility(View.VISIBLE);
viewNoConnectedLayout.setVisibility(View.GONE);
}
break;
case Constant.STATE_CONNECTED:
initErrorFinishRefreshAndLoadMore();
if (baseItemAdapter.getData().size() == 0) {
viewEmptyLayout.setVisibility(View.GONE);
viewErrorLayout.setVisibility(View.GONE);
viewNoConnectedLayout.setVisibility(View.VISIBLE);
}
break;
}
}
/**
* 成功拉取服務器數據,刷新拉取時間
*
* @param isRefresh true 下拉刷新結束 false 上拉加載結束
*/
private void initSuccessFinishRefreshAndLoadMore(boolean isRefresh) {
if (refreshLayout == null) return;
if (isRefresh) {
refreshLayout.finishRefresh(true);
} else {
refreshLayout.finishLoadMore(true);
}
}
/**
* 訪問服務器失敗,不刷新拉取時間
*
* @param isRefresh true 下拉刷新結束 false 上拉加載結束
*/
private void initErrorFinishRefreshAndLoadMore(boolean isRefresh) {
if (refreshLayout == null) return;
if (isRefresh) {
refreshLayout.finishRefresh(false);
} else {
refreshLayout.finishLoadMore(false);
}
}
/**
* 是否開啓上拉加載,未滿一屏則默認不可下拉加載
*
* @param isOpen
*/
public void isOpenLoadMore(boolean isOpen) {
isLoadMore = isOpen;
if (refreshLayout == null) return;
refreshLayout.setEnableLoadMore(isOpen);
}
private void initErrorFinishRefreshAndLoadMore() {
if (refreshLayout == null) return;
switch (refreshLayout.getState()) {
case Refreshing:
refreshLayout.finishRefresh(false);
break;
case Loading:
refreshLayout.finishLoadMore(false);
break;
case None:
break;
default:
refreshLayout.setNoMoreData(false);
}
}
private void initSuccessFinishRefreshAndLoadMore() {
if (refreshLayout == null) return;
switch (refreshLayout.getState()) {
case Refreshing:
refreshLayout.finishRefresh(true);
break;
case Loading:
refreshLayout.finishLoadMore(true);
break;
case None:
break;
default:
refreshLayout.setNoMoreData(true);
}
}
public void doGetWithToken() {
getObservable().compose(SchedulerProvider.getInstance().observableIO2Main(this)).subscribe(new CallbackObserver<List<T>>() {
@Override
public void onSuccess(BaseResponse<List<T>> httpResult) {
initSuccess(httpResult);
}
@Override
public void onError(Throwable e) {
super.onError(e);
if (e instanceof SocketTimeoutException || e instanceof ConnectException || e instanceof UnknownHostException) {
initSetState(Constant.STATE_CONNECTED);
}
}
@Override
protected void onFailed(BaseResponse<List<T>> tHttpResult) {
super.onFailed(tHttpResult);
initSetState(Constant.STATE_ERROR);
}
});
}
}
xml類
<com.scwang.smartrefresh.layout.SmartRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:zhy="http://schemas.android.com/apk/res-auto"
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/layout_header_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.widget.NestedScrollView
android:id="@+id/mNestedScrollView"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/mRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:nestedScrollingEnabled="false" />
</android.support.v4.widget.NestedScrollView>
<include layout="@layout/view_empty_layout" />
<include layout="@layout/view_error_layout" />
<include layout="@layout/view_no_connected_layout" />
<FrameLayout
android:id="@+id/layout_footer_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</FrameLayout>
</com.scwang.smartrefresh.layout.SmartRefreshLayout>
可以看到,除了更換了網絡訪問方式,還修改了空界面,錯誤界面,無網絡界面的處理方式,不在是用自定義控件實現,這種更加便於修改和理解。
再者就是加了getHeaderUiView,addFooterUiView,兩個方法,使得可以兼容更多的界面情況,這裏就不用多分析了,改分析的都在上面分析完了,各種界面的顯示判斷相信大家應該都知道
下面介紹下多列表封裝的使用
多列表base封裝
多列表,也就是複雜的,多種類型的列表界面,可以選擇單個單個fragment寫,當然也可以使用多列表了,唉,算了,看了下以前封裝的多列表基類,發現還是用的不是最新的rxjava2+retrofit2+okhttp3+rxlifecycle2之類,那就等我五一的時候換一下並且測試一下在給大家分享吧,省的誤人子弟啊哈哈,抱歉抱歉,五一一定補上。
這次就介紹到這裏了,更新多列表的分享我會直接在接在本篇文章之後
本篇文章旨在於讓大家如何高效快速的編寫代碼,並且減少因爲漏寫,疏忽而導致的bug
偷懶一時爽,一直偷懶一直爽,哈哈。大神請輕噴,也歡迎指點小弟一二