ARoute+Rxjava2+Retrofit2+Okhttp+MVVM

3月份快结束了,天气也渐渐变暖,希望2020年android行情也能够逐渐回暖吧,等待之余,我们也要不断的提升自己的技术,跟上技术更新的步伐。这不眼看着2020年Google IO大会即将发布新的功能,android10.0系统还没有玩熟,android11即将来临。是不是很可怕,是不是瞬间感觉android开发好痛苦,不要慌!!!饭要一口一口吃,路要一步一步走。是不是有些同学,对AAC(android架构组件)还没有真正的使用到项目中,是不是还在使用MVP设计模式开发。读过这篇文章,就会让你从此爱上MVVM模式。

一、MVC、MVP、MVVM对比

  • MVC :与MVP最大区别在于activity是作为Controller层存在,这样就会导致activity的代码量十分臃肿,不易于维护。
  • MVP:activity与布局作为V层,业务逻辑放在M层进行处理,通过P层接收V层的请求,然后将请求交给M层处理,将处理结果通过P层回到给V层。这种方式,在业务量很大的时候,会导致P层回调给V层的接口数量大量增加,导致接口地狱。
  • MVVM:需要借助dataBinding,V层代表布局文件,我们可以在布局文件中添加逻辑代码,VM层负责业务交互,需要继承ViewModel。并与V层建立数据绑定,M层负责业务的处理。解决接口地狱问题,代码量减少,能够灵活地控制activity相关的生命周期。当然,目前发现的缺点就是会产生很多的中间类,apk体积会相应增加。随着布局的增加,编译速度也会越来越慢。

二、4个现代化

前不久看到一篇文章,说到一个完整的app应该包括4个"现代化"

  • 层次化:各个模块的相互依赖关系,主次分明。能够快速切换组件环境和集成环境。
  • 模块化:业务模块,base模块、common模块、网络模块等
  • 控件化:自定义控件
  • 插件化:动态加载的未安装的apk文件

我认为这种说法是正确的,区分这些能够加快代码的开发,以及功能之间的解耦。

1、层次化

这里其实最重要的就是gradle的配置,自定义config.gradle。把通用的gradle配置和第三方依赖放进来:

ext {

    //定以变量,决定当前环境是集成环境还是组件化环境
    //集成环境:把所有的module作为library打包到app中,不可单独运行
    //组件化环境:就是可以单独运行的module
    isRelease = true

    //配置defaultConfig下的信息
    versionConfig = [
            "compileSdkVersion": 29,
            "buildToolsVersion": "29.0.2",
            "minSdkVersion"    : 21,
            "targetSdkVersion" : 29,
            "versionCode"      : 1,
            "versionName"      : "1.0"
    ]

    //配置appId 就是applicationId
    appId = [
            "app" : "com.xinyartech.mymvvmmaster",
            "main": "com.xinyartech.main",
    ]

    appcompatVersion = "1.1.0"
    retrofitVersion = "2.6.2"
    arouteVersion = "1.4.1"
    glideVersion = "4.11.0"

    //测试版本和发布版本url地址
    url = [
            "debug"  : "http://www.baidu.com",
            "release": "http://www.google.com"
    ]

    //配置第三方依赖
    dependencies = [
            "appcompat"             : "androidx.appcompat:appcompat:$rootProject.appcompatVersion",
            //网络
            "rxjava2"               : 'io.reactivex.rxjava2:rxjava:2.2.12',
            "rxandroid"             : 'io.reactivex.rxjava2:rxandroid:2.1.1',
            "retrofit2_adapter"     : "com.squareup.retrofit2:adapter-rxjava2:$rootProject.retrofitVersion",
            "okhttp_log_interceptor": 'com.squareup.okhttp3:logging-interceptor:3.9.0',
            "retrofit2_convert"     : "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion",
            "retrofit2"             : "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion",
            //解析
            "gson"                  : 'com.google.code.gson:gson:2.8.6',
            "commonstool"           : 'org.apache.commons:commons-lang3:3.7',
            //动画
            "lottie"                : 'com.airbnb.android:lottie:2.8.0',
            //错误界面
            "loadsir"               : 'com.kingja.loadsir:loadsir:1.3.6',
            //生命周期
            "lifecycle_extensions"  : 'android.arch.lifecycle:extensions:1.1.1',
            "lifecycle_viewmodel"   : 'androidx.lifecycle:lifecycle-viewmodel:2.2.0',
            //ARouter
            "aroute"                : "com.alibaba:arouter-api:$rootProject.arouteVersion",
            //glide
            "glide"                 : "com.github.bumptech.glide:glide:$rootProject.glideVersion",
            //recyclerView
            "recyclerview"          : "androidx.recyclerview:recyclerview:$rootProject.appcompatVersion"

    ]

    apts = [
            "aroute_compiler": 'com.alibaba:arouter-compiler:1.2.2',
            "glide_compiler" : "com.github.bumptech.glide:compiler:$rootProject.glideVersion"
    ]

}

然后不要忘记在工程的build.gradle文件第一行添加一句

apply from: "config.gradle"
buildscript{
	xxx
}
allprojects{
	xxx
}

这样才能在各个模块下使用ext扩展名。如何使用呢? 比如我在主模块app下使用,在app的build.gradle中添加如下:

def versionConfig = rootProject.ext.versionConfig
xxx
android {
    compileSdkVersion versionConfig.compileSdkVersion
    xxx
}

这样就可以通过自定义变量进行赋值了。

2、模块化

首先看一下整个app模块的截图:
在这里插入图片描述
这一块也是我们研究的重中之重。

2.1 网络模块network

这里主要是对Okhhtp、rxjava以及retrofit进行了二次封装,方便调用。举个例子,调用一个网络请求接口:

	NetworkApi
	.getInstance()
	.getService(IMainService.class)
	.getNewsList("5572a108b3cdc86cf39001cd", "国内焦点", "1")
    .compose(
    	NetworkApi.getInstance().applySchedulers(new BaseObserver<MainBean>(this,this))
    );

入口类是 NetworkApi.java,伪代码如下:

public class NetworkApi{

		getInstance(){
			xxx
		}
		
		public <T> T getService(Class<T> service) {
       	 	return getInstance().getRetrofit(service).create(service);
    	}
}

getService返回的是一个接口。负责retrofit相关的网络请求路径及参数进行统一配置。这里重点关注applySchedulersBaseObserver

(1)applySchedulers

/**
     * 统一处理错误 、线程切换
     */
    public <T> ObservableTransformer<T, T> applySchedulers(final BaseObserver<T> observer) {
        return new ObservableTransformer<T, T>() {
            @Override
            public ObservableSource<T> apply(Observable<T> upstream) {
                Observable<T> observable = (Observable<T>) upstream
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .map(getAppErrorHandler())
                        .onErrorResumeNext(new HttpErrorHandler<T>());
                observable.subscribe(observer);
                return observable;
            }
        };
    }

其实就是统一处理线程的切换,以及统一错误处理。最终异常的数据流向都会到达HttpErrorHandler中。

(2)HttpErrorHandler

/**
 * 处理错误类型
 * 1、http请求相关的错误,例如:404,403,socket timeout等等;
 * 2、应用数据的错误会抛RuntimeException,最后也会走到这个函数来统一处理;
 */
public class HttpErrorHandler<T> implements Function<Throwable, Observable<T>> {
    @Override
    public Observable<T> apply(Throwable throwable) throws Exception {
        return Observable.error(ExceptionHandler.handleException(throwable));
    }
}

最终交于ExceptionHandler进行异常处理。

(3)BaseObserver

public class BaseObserver<T> extends DisposableObserver<T> {

    //model基类
    private BaseModel model;
	//回调网络数据到model层
    private IBaseModelListener<T> modelListener;

    public BaseObserver(BaseModel model, IBaseModelListener<T> listener) {
        this.model = model;
        this.modelListener = listener;
        model.addDisposable(this);

    }

    @Override
    public void onNext(T t) {

        Log.e("onNext>>>>>>>",t.toString());

        this.modelListener.onSuccess(t);
    }

    @Override
    public void onError(Throwable e) {
        if(e instanceof ExceptionHandler.ResponseThrowable){
            this.modelListener.onFail(e);
        } else {
            this.modelListener.onFail(new ExceptionHandler.ResponseThrowable(e, ExceptionHandler.ERROR.UNKNOWN));
        }
    }

    @Override
    public void onComplete() {
        Log.e("onComplete>>>>>>>","onCompleteonCompleteonCompleteonCompleteonCompleteonCompleteonCompleteonComplete");
    }
}

这个类主要负责数据的回调,通过IBaseModelListener接口回调给model层数据。另外通过

	model.addDisposable(this);

这一行代码,把相关操作添加到CompositeDisposable管理者中,以便在合适的时机,将相关请求移除,防止内存泄漏。代码如下:

/**
     * 订阅
     */
    public void addDisposable(Disposable disposable) {
        if (disposable == null) {
            return;
        }
        if (compositeDisposable == null) {
            compositeDisposable = new CompositeDisposable();
        }
        compositeDisposable.add(disposable);
    }
2.2 base模块

这个模块可以说是核心模块了,所有的MVVM设计都是基于这个模块开展而来,先瞅一下包含了哪些功能:
在这里插入图片描述
(1)bindAdapter
不说了,主要是DataBinding自定义属性。注意RecyclerViewBindAdapter.java类。其实之前有篇文章已经对RecycleView实现DataBinding做了实现,recyclerView+dataBinding

(2)loadSir
主要是展示异常界面,比如界面没有数据,需要显示一个自定义空数据界面,loadSir可以帮你完成,使用起来很方便,强力推荐。

(3)model
model层

  • BaseModel:model层基类,负责将数据回调给VM层,通过接口形式。另外,可以在基类中将网络数据缓存起来。该类的主要代码如下:
public void register(IBaseViewModelListener listener) {
        if (listener == null) {
            return;
        }
        ...
         WeakReference<IBaseViewModelListener> weakListener =
                    new WeakReference<IBaseViewModelListener>(listener, mReferenceQueue);
        mWeakListenerArrayList.add(weakListener);
           
        }
    }

存的是IBaseViewModelListener ,用于数据回调给ViewModel层。回调如下:

/**
     * 成功:有需要可以在此处保存到本地缓存
     *
     * @param data 数据
     */
    protected void loadSuccess(D data) {
        if (mWeakListenerArrayList == null || mReferenceQueue == null) {
            return;
        }
        if (mWeakListenerArrayList.size() > 0) {
            for (WeakReference<IBaseViewModelListener> reference :
                    mWeakListenerArrayList) {
                IBaseViewModelListener listener = reference.get();
                if (listener != null) {
                    listener.onLoadSuccess(this, data);
                }
            }
        }
    }
  • IBaseModelListener:网络数据回调接口,用于数据回调给Model层。

(4)viewModel
在这里插入图片描述

  • BaseViewModel:继承ViewModel类,统一封装了数据请求成功和失败的数据更新

public abstract class BaseViewModel<M extends BaseModel, D> extends ViewModel implements LifecycleObserver, IBaseViewModelListener<D> {

    protected M model;
    //model层回复数据
    public MutableLiveData<D> returnData = new MutableLiveData<>();
    //界面状态数据,比如成功、正在加载等
    public MutableLiveData<ViewStatus> viewData = new MutableLiveData<>();
    //model层回复错误信息
    public MutableLiveData<String> errorMsg = new MutableLiveData<>();
	xxx
	@Override
    public void onLoadSuccess(BaseModel model, D data) {

        returnData.setValue(data);
        returnData.postValue(returnData.getValue());
        viewData.setValue(ViewStatus.SHOW_CONTENT);
        viewData.postValue(viewData.getValue());
    }

    @Override
    public void onLoadFail(BaseModel model, String msg) {
        viewData.setValue(ViewStatus.REFRESH_ERROR);
        viewData.postValue(viewData.getValue());
        errorMsg.setValue(msg);
    }
    xxx
}
  • IBaseViewModelListener:负责将Model层数据回调给ViewModel层。

(5)view
在这里插入图片描述

  • BaseActivity:view层基类,添加ViewModel层数据监听,以及数据回调处理
public abstract class BaseActivity<V extends ViewDataBinding, VM extends BaseViewModel> extends AppCompatActivity implements Observer, IBaseView {
@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        dataBinding = DataBindingUtil.setContentView(this, getLayoutId());
        Log.d(getStringTag(), "Activity: " + this + " : " +
                "onCreate");
        //观察viewModel
        viewModel = getViewModel();
        //同步生命周期到ViewModel
        getLifecycle().addObserver(viewModel);
        viewModel.returnData.observe(this, this);
        viewModel.errorMsg.observe(this,this);
        viewModel.viewData.observe(this,this);
    }
    
	@Override
    public void onChanged(Object obj) {
        xxx

    }
    xxx
}
 
  • ViewStatus:枚举类,页面状态枚举。

(6)recyclerView

封装了recyclerView相关的基类。
在这里插入图片描述

  • BaseRecyclerCustomView:自定义View,继承LinearLayout,动态将每一项添加到recyclerView中。局部代码如下:
public abstract class BaseRecyclerCustomView<VM extends BaseRecyclerViewModel, B extends ViewDataBinding> extends LinearLayout implements IBaseRecyclerCustomView<VM> {

	xxx
	
	private void init() {
        LayoutInflater inflater =(LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (getLayoutId() != 0 && inflater != null) {
            dataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), this, false);
            this.addView(dataBinding.getRoot());
        }
    }
    
	@Override
    public void setDataToView(VM data) {
        viewModel = data;
		//交给子类实现
        setData(viewModel);

        if (dataBinding != null) {
            dataBinding.executePendingBindings();
        }
    }
}
  • BaseRecyclerViewAdapter:继承recyclerView.Adapter。重写onBindViewHoldergetItemViewType方法。完整代码如下:
public abstract class BaseRecyclerViewAdapter<VH extends BaseRecyclerViewHolder,
        VM extends BaseRecyclerViewModel> extends RecyclerView.Adapter<VH> {

    protected ObservableList<VM> mItems;

    @NonNull
    @Override
    public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull VH holder, int position) {
        holder.setData(mItems.get(position));
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    public void setItems(ObservableList<VM> items) {
        mItems = items;
        notifyDataSetChanged();
    }

    @Override
    public int getItemViewType(int position) {
        //符合开闭原则
        return mItems.get(position).getItemType();
    }
}
  • BaseRecyclerViewHolder:继承RecyclerView.ViewHolder,完整代码如下:
public class BaseRecyclerViewHolder<VM extends BaseRecyclerViewModel> extends RecyclerView.ViewHolder {
    private IBaseRecyclerCustomView view;

    public BaseRecyclerViewHolder(@NonNull IBaseRecyclerCustomView itemView) {
        super((View) itemView);
        this.view = itemView;
    }

    public void setData(VM data){
        this.view.setDataToView(data);
    }
}
  • IBaseRecyclerCustomView:源码如下:
public interface IBaseRecyclerCustomView<D extends BaseRecyclerViewModel> {
    @LayoutRes
    int getLayoutId();

    void setDataToView(D data);
}

具体的两个方法交于子类实现。

  • BaseRecyclerViewModel:源码如下:
public abstract class BaseRecyclerViewModel implements Serializable, IBaseRecyclerItemType {
    public String tag;

    @Override
    public int getItemType() {
        return Integer.MAX_VALUE;
    }
}

具体itemType交于子类实现。

为什么要这样设计adapter,搞了那么多类出来???
我们普遍的做法是把ViewHolder都写在adapter类中,如果有多个itemType,就要写多个ViewHolder。这样就违背了SOLID原则中的单一职责原则。adapter的职责是适配功能,真正的刷新界面应该交给对应的viewHodlder去实现。那么究竟如何使用呢?在common模块中会体现。

2.3 common模块

在这里插入图片描述
公共库,所有人都可以在里面进行修改,增加功能。比如ARouter的路径配置

public class ARoutePath {

    /**
     * 主界面
     */
    public static final class Main{

        //路由组名
        static final String GROUP_NAME = "main";
        //主界面
        public static final String PATH_MAIN = "/"+GROUP_NAME+"/MainActivity";

    }

    /**
     * 登录界面
     */
    public static final class Login{
        //路由组名
        static final String GROUP_NAME = "login";
        //主界面
        public static final String PATH_LOGIN = "/"+GROUP_NAME+"/LoginActivity";
    }
}

这里重点说一下针对不同的itemType,是如何自定义viewHolder的?在BaseRecyclerViewHolder类中,需要将IBaseRecyclerCustomView类型作为参数,传递给BaseRecyclerViewHolder。那么IBaseRecyclerCustomView具体实现类是谁呢?这里就是AdapterImageTextViewAdapterTextView

AdapterImageTextView代码:

public class AdapterImageTextView extends BaseRecyclerCustomView<AdapterImageTextViewModel, AdapterImageTextviewBinding> {

    public AdapterImageTextView(Context context) {
        super(context);
    }

    @Override
    public int getLayoutId() {
        return R.layout.adapter_image_textview;
    }

    @Override
    public void setData(AdapterImageTextViewModel viewModel) {
        getDataBinding().setVm(viewModel);
    }
}

具体的数据定义交给AdapterImageTextViewModel。代码如下:

public class AdapterImageTextViewModel extends BaseRecyclerViewModel {
    public String title;
    public String imageUrl;
    public String content;

    public static final int type1 = 1;

    @Override
    public int getItemType() {
        return type1;
    }
}

通过AdapterImageTextViewsetData() 方法,将布局与viewModel进行绑定,达到数据的更新。

3、控件化

其实上面自定义AdapterImageTextView以及AdapterTextView已经实现了自定义view,也是属于控件化的。

4、组件化

使用ARouter实现。

5、插件化

可以参考支付宝、微信等优秀app。里面大量集成了插件,也可以参考文章架构师学习–插件化之Hook方式(5.0版本~9.0版本)

三、使用

这里用Main模块做个演示。

1、布局
<layout 
	xxx

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/adapter"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            rv:dataList="@{dataList}"
            rv:adapter="@{adapter}" />
	xxx
</layout>
2、activity
@Route(path = ARoutePath.Main.PATH_MAIN)
public class MainActivity extends BaseActivity<ActivityMainBinding,MainViewModel> {

    @Override
    public int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected MainViewModel getViewModel() {
        ViewModelProvider viewModelProvider = new ViewModelProvider(this);
        viewModel = viewModelProvider.get(MainViewModel.class);
        dataBinding.setViewModel(viewModel);

        //模拟数据
        ObservableList<BaseRecyclerViewModel> nameClassObservableList = new ObservableArrayList<>();
        ...
        dataBinding.setVariable(BR.adapter,new MyAdapter());
        dataBinding.setVariable(BR.dataList,nameClassObservableList);
        return viewModel;
    }

}

实际的网络发起请求在MainViewModel的构造方法中。

3、MainViewModel
public class MainViewModel extends BaseViewModel<MainModel, MainBean>{


    public MainViewModel() {
        super(MainModel.class);
        model.loadData();
    }

    @Override
    public void onLoadSuccess(BaseModel model, MainBean data) {
        super.onLoadSuccess(model, data);
    }
}

最终交给MainModel完成网络操作

4、MainModel
public class MainModel extends BaseModel<MainBean> {
  	...
    @Override
    public void loadLocal() {

        Observable<MainBean> ob =
                NetworkApi.getInstance().getService(IMainService.class).getNewsList(
                "5572a108b3cdc86cf39001cd", "国内焦点", "1")
                .compose(NetworkApi.getInstance().applySchedulers(new BaseObserver<MainBean>(this
                        , this)));

    }
   ...
    @Override
    public void onSuccess(MainBean data) {

        loadSuccess(data);
    }
    @Override
    public void onFail(Throwable e) {
        loadFail(e.getMessage());
    }
}

结果回调给onSuccess()方法。调用父类的loadSuccess()方法,最后回调给MainViewModel

protected void loadSuccess(D data) {
        ...
			IBaseViewModelListener listener = reference.get();
                if (listener != null) {
                    listener.onLoadSuccess(this, data);
                }
		...
    }
5、MainAdapter

列表适配器

public class MyAdapter extends BaseRecyclerViewAdapter {

    @NonNull
    @Override
    public BaseRecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == AdapterTextViewModel.type0) {
            IBaseRecyclerCustomView adapterTextView = new AdapterTextView(parent.getContext());
            return new BaseRecyclerViewHolder(adapterTextView);

        } else if (viewType == AdapterImageTextViewModel.type1) {
            IBaseRecyclerCustomView adapterImageTextView =
                    new AdapterImageTextView(parent.getContext());
            return new BaseRecyclerViewHolder(adapterImageTextView);
        }

        return super.onCreateViewHolder(parent, viewType);
    }

根据不同的itemType,传入不同的自定义view。具体操作交于自定义view实现。
这样的操作,是不是很简单,谁都会用!!!

最后分享一下源码地址,来之不易。打赏点积分吧

mvvm+rxjava2+retrofit+ARouter

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值