最近正在做一个项目,架构呢就是使用的第一篇博客所描述的架构。在业务模块中,ui框架使用了不严谨的mvc架构,摒弃了m层,把m层与c层合并到了一起,也就是vc模式的架构。一个View与一个Controller一一对应,那么操作逻辑和网络请求在controller中执行,然后view中根据数据做出响应。
该框架的封装要感谢我的俩同事, @朱小蛟 和 @马小飞,代码大部分都是他们搞的。
1.涉及框架
一,Retrofit。一个RESTful 的HTTP网络请求框架(基于OKHttp),通过注解配置网络请求参数,支持同步,异步网络请求,支持多种数据的解析,并提供对RxJava的支持。优点很多,拓展性好,简洁易用,高解耦等等吧。
compile "com.squareup.retrofit2:adapter-rxjava:$rootProject.adapterRxjavaVersion"
compile "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
compile "com.squareup.retrofit2:converter-gson:$rootProject.converterGsonVersion"
二,RxAndroid。rxandroid是rxjava在android上的一个扩展,这个库用来处理事件,异步任务。该库,怎么说,就俩字,简洁。当业务十分繁琐复杂时这一点就显出优势了,依然可以保持简洁。
compile "io.reactivex:rxandroid:$rootProject.rxandroidVersion"
三,LifeCycle。这是Google官方在I/O大会中引出的新架构内容之一,它实现的一个重要目的,时实现Android的与Activity和Fragment生命周期相关的逻辑控制进一步的解耦。以前因为要写与activity 和 fragment 生命周期相关的控制逻辑的时候,不得不在activity或fragment的onCreate,onStart,onResume里面塞进自己的代码,这样就会让页面中的代码越来越多。而lifecycle专注于activity或者fragment生命周期的维护管理。
compile "android.arch.lifecycle:extensions:$rootProject.lifecycleExtensionsVersion"
annotationProcessor "android.arch.lifecycle:compiler:$rootProject.lifecycleCompilerVersion"
2.代码走起
1.因为是vc模式,v中会调用一些c中的逻辑方法,c中会有很多地方调用网络请求,或者调用操作逻辑,所以先创建一个BaseController,以统一controller对一些逻辑的编写。
ViewModel是lifecycle中的一个抽象类,目的是将数据从ui中分离出来,并且当activity或者fragment重构的时候,viewmodel会自动保留之前的数据并给新的activity或fragment使用。
public abstract class BaseController extends ViewModel {
@Override
protected void onCleared() {
}
}
然后因为需要通过响应式对数据做传递,则需要订阅者集合,并创建网络请求和逻辑操作的公有方法。
public abstract class BaseController extends ViewModel {
/**
* RxJava 的订阅者集合
*/
protected CompositeSubscription mSubscriptions = new CompositeSubscription();
/**
* 封装网络请求
*
* @param observable 被观察者,url
* @param observer 回调
* @param <T> 解析后的实体
*/
protected <T> void getHttpData(Observable<T> observable, Observer<T> observer) {
Subscription subscription = observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(observer);
mSubscriptions.add(subscription);
}
/**
* 无网络请求的操作请求
* @param t 承载数据的数据体
* @param observer 回调
* @param <T> 解析后的实体
*/
protected <T> void getOperateData(final T t, Observer<T> observer){
Observable<T> observable = Observable.create(new Observable.OnSubscribe<T>() {
@Override
public void call(Subscriber<? super T> subscriber) {
subscriber.onNext(t);
subscriber.onCompleted();
}
});
mSubscriptions.add(observable.subscribe(observer));
}
@Override
protected void onCleared() {
// 取消订阅
mSubscriptions.clear();
}
}
ok,此时basecontroller就基本好了。现在就看看retrofit如何进行一个封装吧。创建一个HttpUtil。直接上代码,看代码秒懂。
public class HttpUtil {
private static OkHttpClient okHttpClient;
private static Converter.Factory gsonConverterFactory = GsonConverterFactory.create();
private static CallAdapter.Factory rxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create();
private static Retrofit retrofit;
public static <T> T getRetrofit(final Class<T> service) {
if (okHttpClient == null) {
OkHttpClient.Builder client = new OkHttpClient.Builder();
// 添加http请求json数据打印
client.addInterceptor(new LoggingInterceptor.Builder()
.loggable(BuildConfig.DEBUG)
.setLevel(Level.BASIC)
.log(Platform.INFO)
.request("Request")
.response("Response")
.build()).connectTimeout(5, TimeUnit.SECONDS);
// TODO: 2018/2/1 添加 StringCallBackInter 拦截器ø
okHttpClient = client.build();
}
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(ENV.getHttpHost())
.addConverterFactory(gsonConverterFactory)
.addCallAdapterFactory(rxJavaCallAdapterFactory)
.build();
}
return retrofit.create(service);
}
}
这两个写好之后,下面写一个例子,看如何使用。有一个可上拉加载下拉刷新的页面,来看看数据如何调用,如何获取。先创建一个controller继承basecontroller。并创建下拉刷新和上拉加载的方法。
public class TestHttpController extends BaseController {
/**
* 刷新
*/
private MutableLiveData<ResultBean> mObservableRefresh;
/**
* 加载更多
*/
private MutableLiveData<ResultBean> mObservableLoadMore;
/**
* 角色权限集合
*/
private MutableLiveData<PermissionRoleListBean> permissionRoleListData;
private int mPage;
public MutableLiveData<ResultBean> getRefreshData() {
mObservableRefresh = new MutableLiveData<>();
getCategoryItems(true);
return mObservableRefresh;
}
public MutableLiveData<ResultBean> getLoadMoreData() {
mObservableLoadMore = new MutableLiveData<>();
getCategoryItems(false);
return mObservableLoadMore;
}
private void getCategoryItems(final boolean isRefresh) {
if (isRefresh) {
mPage = 1;
}
getHttpData(HttpUtil.getRetrofit(ManagerUrls.class).getCategoryData("Android", 20, mPage),
new Observer<ResultBean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
if (isRefresh) {
mObservableRefresh.setValue(null);
} else {
mObservableLoadMore.setValue(null);
}
}
@Override
public void onNext(ResultBean resultBean) {
if (isRefresh) {
mObservableRefresh.setValue(resultBean);
} else {
mObservableLoadMore.setValue(resultBean);
}
mPage += 1;
}
});
}
}
看页面中如何使用吧。
@Route(path = "/testa/testhttp")
public class TestHttpActivity extends BaseActivity implements OnRefreshLoadmoreListener {
@BindView(R2.id.recyclerview)
RecyclerView recyclerview;
@BindView(R2.id.refresh)
SmartRefreshLayout refresh;
private TestHttpController testHttpController;
private TestHttpAdapter testHttpAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_http);
ButterKnife.bind(this);
initView();
// 获取 ViewModel
testHttpController = ViewModelProviders.of(this).get(TestHttpController.class);
getRefreshData();
}
private void initView() {
refresh.setOnRefreshLoadmoreListener(this);
testHttpAdapter = new TestHttpAdapter(null);
recyclerview.setLayoutManager(new LinearLayoutManager(this));
recyclerview.setAdapter(testHttpAdapter);
}
@Override
public void onRefresh(RefreshLayout refreshlayout) {
getRefreshData();
}
@Override
public void onLoadmore(RefreshLayout refreshlayout) {
getLoadMoreData();
}
private void getRefreshData() {
testHttpController.getRefreshData().observe(this, new Observer<ResultBean>() {
@Override
public void onChanged(@Nullable ResultBean resultBean) {
refresh.finishRefresh();
if (resultBean == null || resultBean.error) {
Toast.makeText(TestHttpActivity.this, "刷新失败", Toast.LENGTH_SHORT).show();
return;
}
testHttpAdapter.setNewData(resultBean.results);
}
});
}
private void getLoadMoreData() {
testHttpController.getLoadMoreData().observe(this, new Observer<ResultBean>() {
@Override
public void onChanged(@Nullable ResultBean resultBean) {
refresh.finishLoadmore();
if (resultBean == null || resultBean.error) {
return;
}
testHttpAdapter.addData(resultBean.results);
}
});
}
@Override
public void permissionSuccess(int requestCode, List<String> perms) {
}
@Override
public void permissionFaild(int requestCode, List<String> perms) {
}
}
这样就ok了。但是当有网络请求的时候,我们需要调用一些loading弹框等等ui上的操作,就需要经常在页面方法里去编写一些show方法,dissmiss方法等等,虽然没几行代码,但是有时候很容易忘掉,一旦忘掉,那么逻辑操作就会很不友好,还得返回去加上。那么我们就对Observer做一个封装,把一些必须的逻辑放在baseobserver中,那就可以专注逻辑代码了。
public class BaseObserver<T> implements Observer<T> {
private Context context;
private BaseObserverListener mlistener;
private QMUITipDialog qmDialog = null;
private Handler mCancelHandler;
public BaseObserver() {
this(null, null);
}
public BaseObserver(Context context, BaseObserverListener listener) {
try {
this.context = context;
mlistener = listener;
mCancelHandler = new Handler();
if (null != context) {
// android.view.WindowLeaked: Activity com.sound.wisdomcloud.ui.activity.SplashActivity has leaked window DecorView@5300feb[] that was originally added here
// 写在静态类里会报这个异常,所以只能维护在这里边了。
if (null == qmDialog) {
qmDialog = new QMUITipDialog.Builder(context)
.setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING)
.setTipWord("正在加载")
.create();
}
Log.e("窗体泄漏", "构造 qmDialog.show();");
qmDialog.show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onCompleted() {
try {
if (null != qmDialog) {
qmDialog.dismiss();
}
if (null != mlistener) {
mlistener.onCompleted();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable e) {
try {
if (context == null) {
return;
}
if (null == qmDialog) {
qmDialog = new QMUITipDialog.Builder(context)
.setIconType(QMUITipDialog.Builder.ICON_TYPE_FAIL)
.setTipWord("请求失败")
.create();
}
Log.e("窗体泄漏", "onError qmDialog.show()");
qmDialog.show();
if (null != mlistener) {
mlistener.onError(e);
}
if (null == mCancelHandler) {
mCancelHandler = new Handler();
}
mCancelHandler.postDelayed(new Runnable() {
@Override
public void run() {
onCompleted();
}
}, 2000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Override
public void onNext(T o) {
if (null != mlistener) {
mlistener.onNext(o);
}
}
public interface BaseObserverListener<T> {
public void onCompleted();
public void onError(Throwable e);
public void onNext(T o);
}
}
把loading弹框,error弹框,等等ui上的操作都封装到baseobserver中,然后当我们调用controller的逻辑方法时,就不用再调用什么showLoading啊,请求完成之后也不用再调用dismissLoading啊等等的方法了。