1 Star 0 Fork 0

brilliantzhao / JetPackMVVMBase

Create your Gitee Account
Explore and code with more than 6 million developers,Free private repositories !:)
Sign up
Clone or download
Cancel
Notice: Creating folder will generate an empty file .keep, because not support in Git
Loading...
README.md

一. 更新日志

v1.0.4

2021年1月30日

  • 添加公共类的生命周期日志;
  • 添加懒加载的示例代码;
  • 升级gradle依赖;
  • 修改例子程序;
  • 修改文档说明。

更多日志

java实现AndroidX+RxJava3+Retrofit2+ViewModel +LiveData+DataBinding+Glide等MVVM框架

java实现组件化分支

kotlin实现JetPack全套MVVM框架

kotlin实现JetPack全套MVVM组件化分支

二. JetPackMVVMBase

  • 基于MVVM模式集成谷歌官方推荐的JetPack组件库:LiveData、ViewModel、Lifecycle、Navigation等组件
  • 使用kotlin语言,添加大量拓展函数,简化代码
  • 加入Retrofit网络请求,协程,帮你简化各种操作,让你快速请求网络
整个页面框架使用BottomNavigationView + Navigation来搭建。通过menu来配置底部菜单。
通过NavHostFragment来配置各个fragment。系统提供的FragmentNavigator的navigate方法中是通过replace来加载fragment。
这就导致在切换Tab的时候fargment会重复的调用onCreateView方法。这肯定不是我们需要的。那么我们需要自定义自己的FragmentNavigator来替换系统
的,通过show和hide的方式来加载fragment。具体方式会在后续系列文章中进行讲解。

框架流程

框架特点

  • 快速开发

    只需要写项目的业务逻辑,不用再去关心网络请求、权限申请、View的生命周期等问题,撸起袖子就是干。

  • 维护方便

    MVVM开发模式,低耦合,逻辑分明。Model层负责将请求的数据交给ViewModel;ViewModel层负责将请求到的数据做业务逻辑处理,最后交给View层去展示,与View一一对应;View层只负责界面绘制刷新,不处理业务逻辑,非常适合分配独立模块开发。

  • 流行框架

    retrofit+okhttp+rxJava负责网络请求;

    gson负责解析json数据;

    glide负责加载图片;

    rxlifecycle负责管理view的生命周期;与网络请求共存亡;

    rxbinding结合databinding扩展UI事件;

    rxpermissions负责Android 6.0权限申请;

    material-dialogs一个漂亮的、流畅的、可定制的material design风格的对话框。

  • 数据绑定

    满足google目前控件支持的databinding双向绑定,并扩展原控件一些不支持的数据绑定。例如将图片的url路径绑定到ImageView控件中,在BindingAdapter方法里面则使用Glide加载图片;View的OnClick事件在BindingAdapter中方法使用RxView防重复点击,再把事件回调到ViewModel层,实现xml与ViewModel之间数据和事件的绑定。

  • 基类封装

    专门针对MVVM模式打造的BaseActivity、BaseFragment、BaseViewModel,在View层中不再需要定义ViewDataBinding和ViewModel,直接在BaseActivity、BaseFragment上限定泛型即可使用。普通界面只需要编写Fragment,然后使用ContainerActivity盛装(代理),这样就不需要每个界面都在AndroidManifest中注册一遍。

  • 全局操作

    1. 全局的Activity堆栈式管理,在程序任何地方可以打开、结束指定的Activity,一键退出应用程序。
    2. LoggingInterceptor全局拦截网络请求日志,打印Request和Response,格式化json、xml数据显示,方便与后台调试接口。
    3. 全局Cookie,支持SharedPreferences和内存两种管理模式。
    4. 通用的网络请求异常监听,根据不同的状态码或异常设置相应的message。
    5. 全局的异常捕获,程序发生异常时不会崩溃,可跳入异常界面重启应用。
    6. 全局事件回调,提供RxBus、Messenger两种回调方式。
    7. 全局任意位置一行代码实现文件下载进度监听(暂不支持多文件进度监听)。
    8. 全局点击事件防抖动处理,防止点击过快。

三. 准备工作

网上的很多有关MVVM的资料,在此就不再阐述什么是MVVM了,不清楚的朋友可以先去了解一下。todo-mvvm-live

1、启用databinding

在主工程app的build.gradle的android {}中加入:

    // 该属性对Gradle插件版本配置有要求,最低版本 4.1.0
    buildFeatures {
        dataBinding = true
    }

2、依赖Library

从远程依赖:

在根目录的build.gradle中加入

allprojects {
    repositories {
		...
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

在主项目app的build.gradle中依赖

dependencies {
    ...
    implementation 'com.gitee.brillantzhao:JetPackMVVMBase:vxxx'
}

下载例子程序,在主项目app的build.gradle中依赖例子程序中的jetpackmvvmbase

dependencies {	
    ...
    implementation project(path: ':jetpackmvvmbase')
}

3、配置config.gradle

如果不是远程依赖,而是下载的例子程序,那么还需要将例子程序中的config.gradle放入你的主项目根目录中,然后在根目录build.gradle的第一行加入:

apply from: "config.gradle"

注意: config.gradle中的

android = [] 是你的开发相关版本配置,可自行修改

roots = [] 是你的系统提供的相关配置,可自行修改

thirdFrame = [] 是依赖第三方库的配置,可以加新库,但不要去修改原有第三方库的版本号,不然可能会编译不过

4、配置AndroidManifest

添加权限:

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

在jetpackmvvmbase中已经添加了 如下权限:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

配置Application:

继承jetpackmvvmbase中的BaseApplication,或者调用

BaseApplication.setApplication(this);

来初始化你的Application

可以在你的自己AppApplication中配置

    @Override
    public void onCreate() {
        super.onCreate();
        // 调用父类的初始化方法
        initUtilCodeXLog(true);
        //初始化全局异常崩溃
        initHabitCrash();
    }

    /**
     * crash初始化
     */
    private void initHabitCrash() {
        CaocConfig.Builder.create()
                .backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT) //背景模式,开启沉浸式
                .enabled(true) //是否启动全局异常捕获
                .showErrorDetails(true) //是否显示错误详细信息
                .showRestartButton(true) //是否显示重启按钮
                .trackActivities(true) //是否跟踪Activity
                .minTimeBetweenCrashesMs(2000) //崩溃的间隔时间(毫秒)
                .errorDrawable(R.mipmap.ic_launcher) //错误图标
                .restartActivity(LoginActivity.class) //重新启动后的activity
                .errorActivity(ErrorActivity.class) //崩溃后的错误activity
//                .eventListener(new YourCustomEventListener()) //崩溃后的错误监听
                .apply();
    }

四. 快速上手

1、编写一个登录功能

1.1 编写fragment_login.xml界面

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/tools">
    <data>
       
    </data>
    <LinearLayout>
       ....
    </LinearLayout>
 </layout>   

1.2 创建LoginViewModel类继承BaseViewModel

class LoginViewModel : BaseViewModel() {
  
}

1.3 创建LoginFragment 继承基类传入相关泛型

第一个泛型为你创建的LoginViewModel,第二个泛型为ViewDataBind,保存fragment_login.xml后databinding会生成一个FragmentLoginBinding类。(如果没有生成,试着点击Build->Clean Project)

class LoginFragment : BaseFragment<LoginViewModel, FragmentLoginBinding>() {
    
    /**
     *  当前fragment绑定的布局
     */
    override fun layoutId() = R.layout.fragment_login
    
    /**
     *  初始化操作
     */
    override fun initView(savedInstanceState: Bundle?) {
        ...
    }
    
    /**
     *  fragment 懒加载
     */
    override fun lazyLoadData() { 
        ...
    }
}

2、网络请求(Retrofit+协程)

1.1 新建请求配置类继承 BaseNetworkApi 示例

class NetworkApi : BaseNetworkApi() {

   companion object {
         
        val instance: NetworkApi by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { NetworkApi() }

        //双重校验锁式-单例 封装NetApiService 方便直接快速调用
        val service: ApiService by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            instance.getApi(ApiService::class.java, ApiService.SERVER_URL)
        }
    }
   
    /**
     * 实现重写父类的setHttpClientBuilder方法,
     * 在这里可以添加拦截器,可以对 OkHttpClient.Builder 做任意你想要做的骚操作
     */
    override fun setHttpClientBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder {
        builder.apply {
            //示例:添加公共heads,可以存放token,公共参数等, 注意要设置在日志拦截器之前,不然Log中会不显示head信息
            addInterceptor(MyHeadInterceptor())
            // 日志拦截器
            addInterceptor(LogInterceptor())
            //超时时间 连接、读、写
            connectTimeout(10, TimeUnit.SECONDS)
            readTimeout(5, TimeUnit.SECONDS)
            writeTimeout(5, TimeUnit.SECONDS)
        }
        return builder
    }

    /**
     * 实现重写父类的setRetrofitBuilder方法,
     * 在这里可以对Retrofit.Builder做任意骚操作,比如添加GSON解析器,protobuf等
     */
    override fun setRetrofitBuilder(builder: Retrofit.Builder): Retrofit.Builder {
        return builder.apply {
            addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
            addCallAdapterFactory(CoroutineCallAdapterFactory())
        }
    }
}

1.2如果请求服务器返回的数据有基类(没有可忽略这一步)

{
    "data": ...,
    "errorCode": 0,
    "errorMsg": ""
}

该示例格式是 玩Android Api返回的数据格式,如果errorCode等于0 请求成功,否则请求失败 作为开发者的角度来说,我们主要是想得到脱壳数据-data,且不想每次都判断errorCode==0请求是否成功或失败 这时我们可以在服务器返回数据基类中继承BaseResponse,实现相关方法:

data class ApiResponse<T>(var errorCode: Int, var errorMsg: String, var data: T) : BaseResponse<T>() {

    // 这里是示例,wanandroid 网站返回的 错误码为 0 就代表请求成功,请你根据自己的业务需求来编写
    override fun isSucces() = errorCode == 0

    override fun getResponseCode() = errorCode

    override fun getResponseData() = data

    override fun getResponseMsg() = errorMsg

}

1.3 在ViewModel中发起请求,

所有请求都是在viewModelScope中启动,请求会发生在IO线程,最终回调在主线程上,当页面销毁的时候,请求会统一取消,不用担心内存泄露的风险,框架做了2种请求使用方式

1、将请求数据包装给ResultState,在Activity/Fragment中去监听ResultState拿到数据做处理

class RequestLoginViewModel: BaseViewModel {

  //自动脱壳过滤处理请求结果,自动判断结果是否成功
    var loginResult = MutableLiveData<ResultState<UserInfo>>()
    
  //不用框架帮脱壳
    var loginResult2 = MutableLiveData<ResultState<ApiResponse<UserInfo>>>()
    
  fun login(username: String, password: String){
   //1.在 Activity/Fragment的监听回调中拿到已脱壳的数据(项目有基类的可以用)
        request(
            { HttpRequestCoroutine.login(username, password) }, //请求体
            loginResult,//请求的结果接收者,请求成功与否都会改变该值,在Activity或fragment中监听回调结果,具体可看loginActivity中的回调
            true,//是否显示等待框,,默认false不显示 可以默认不传
            "正在登录中..."//等待框内容,可以默认不填请求网络中...
        )
        
   //2.在Activity/Fragment中的监听拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类的可以用)
        requestNoCheck(
          {HttpRequestCoroutine.login(username,password)},
          loginResult2,
          true,
          "正在登录中...") 
}


class LoginFragment : BaseFragment<LoginViewModel, FragmentLoginBinding>() {
    
    private val requestLoginRegisterViewModel: RequestLoginRegisterViewModel by viewModels()
    
    /**
     *  当前fragment绑定的布局
     */
    override fun layoutId() = R.layout.fragment_login
    
    /**
     *  初始化操作
     */
    override fun initView(savedInstanceState: Bundle?) {
        ...
    }
    
    /**
     *  fragment 懒加载
     */
    override fun lazyLoadData() { 
        ...
    }
    
    override fun createObserver(){
      //脱壳
       requestLoginRegisterViewModel.loginResult.observe(viewLifecycleOwner,
            Observer { resultState ->
                parseState(resultState, {
                    //登录成功 打印用户
                    it.username.logd()
                }, {
                    //登录失败(网络连接问题,服务器的结果码不正确...异常都会走在这里)
                    showMessage(it.errorMsg)
                })
            })
    
       //不脱壳
       requestLoginRegisterViewModel.loginResult2.observe(viewLifecycleOwner, Observer {resultState ->
               parseState(resultState,{
                   if(it.errorCode==0){
                       //登录成功 打印用户名
                       it.data.username.logd()
                   }else{
                       //登录失败
                       showMessage(it.errorMsg)
                   }
               },{
                   //请求发生了异常
                   showMessage(it.errorMsg)
               })
           })
   } 
}

2、 直接在当前ViewModel中拿到请求结果

class RequestLoginViewModel : BaseViewModel() {
    
  fun login(username: String, password: String){
   //1.拿到已脱壳的数据(项目有基类的可以用)
     request({HttpRequestCoroutine.login(username,password)},{
             //请求成功 已自动处理了 请求结果是否正常
             it.username.logd()
         },{
             //请求失败 网络异常,或者请求结果码错误都会回调在这里
             it.errorMsg.logd()
         },true,"正在登录中...")
        
   //2.拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类或者不想框架帮忙脱壳的可以用)
       requestNoCheck({HttpRequestCoroutine.login(username,password)},{
            //请求成功 自己拿到数据做业务需求操作
            if(it.errorCode==0){
                //结果正确
                it.data.username.logd()
            }else{
                //结果错误
                it.errorMsg.logd()
            }
        },{
            //请求失败 网络异常回调在这里
            it.errorMsg.logd()
        },true,"正在登录中...")
}

注意:使用该请求方式时需要注意,如果该ViewModel并不是跟Activity/Fragment绑定的泛型ViewModel,而是

val mainViewModel:MainViewModel by viewModels() 或者 val mainViewModel:MainViewModel by activityViewModels() 获取的

如果请求时要弹出loading,你需要在Activity | Fragment中添加以下代码:addLoadingObserve(viewModel)

3、获取ViewModel

我们的activity/fragment会有多个ViewModel,按传统的写法感觉有点累

 val mainViewModel = ViewModelProvider(this,
            ViewModelProvider.AndroidViewModelFactory(application)).get(MainViewModel::class.java)

现在官方Ktx有拓展函数可以轻松调用

//在activity中获取当前Activity级别作用域的ViewModel
 private val mainViewModel:MainViewModel by viewModels()
 
//在activity中获取Application级别作用域的ViewModel(注,这个是本框架提供的,Application类继承框架的BaseApp才有用)
 private val mainViewModel by lazy { getAppViewModel<MainViewModel>()}

//在fragment中获取当前Fragment级别作用域的ViewModel
 private val mainViewModel:MainViewModel by viewModels()

//在fragment中获取父类Activity级别作用域的ViewModel
private val mainViewModelMainViewModel by activityViewModels()

//在fragment中获取Application级别作用域的ViewModel(注,这个是本框架提供的,Application类继承框架的BaseApp才有用)
private val mainViewModel by lazy { getAppViewModel<MainViewModel>()}

五. 辅助功能

一个完整的快速开发框架,当然也少不了常用的辅助类。下面来介绍一下MVVMabit中有哪些辅助功能。

1、事件总线

事件总线存在的优点想必大家都很清楚了,android自带的广播机制对于组件间的通信而言,使用非常繁琐,通信组件彼此之间的订阅和发布的耦合也比较严重,特别是对于事件的定义,广播机制局限于序列化的类(通过Intent传递),不够灵活。

1.1、RxBus

RxBus并不是一个库,而是一种模式。相信大多数开发者都使用过EventBus,对RxBus也是很熟悉。由于MVVMabit中已经加入RxJava,所以采用了RxBus代替EventBus作为事件总线通信,以减少库的依赖。

使用方法:

在ViewModel中重写registerRxBus()方法来注册RxBus,重写removeRxBus()方法来移除RxBus

//订阅者
private Disposable mSubscription;
//注册RxBus
@Override
public void registerRxBus() {
    super.registerRxBus();
    mSubscription = RxBus.getDefault().toObservable(String.class)
        .subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {

            }
        });
    //将订阅者加入管理站
    RxSubscriptions.add(mSubscription);
}

//移除RxBus
@Override
public void removeRxBus() {
    super.removeRxBus();
    //将订阅者从管理站中移除
    RxSubscriptions.remove(mSubscription);
}

在需要执行回调的地方发送

RxBus.getDefault().post(object);

1.2、Messenger

Messenger是一个轻量级全局的消息通信工具,在我们的复杂业务中,难免会出现一些交叉的业务,比如ViewModel与ViewModel之间需要有数据交换,这时候可以轻松地使用Messenger发送一个实体或一个空消息,将事件从一个ViewModel回调到另一个ViewModel中。

使用方法:

定义一个静态String类型的字符串token

public static final String TOKEN_LOGINVIEWMODEL_REFRESH = "token_loginviewmodel_refresh";

在ViewModel中注册消息监听

//注册一个空消息监听 
//参数1:接受人(上下文)
//参数2:定义的token
//参数3:执行的回调监听
Messenger.getDefault().register(this, LoginViewModel.TOKEN_LOGINVIEWMODEL_REFRESH, new BindingAction() {
    @Override
    public void call() {
	
    }
});

//注册一个带数据回调的消息监听 
//参数1:接受人(上下文)
//参数2:定义的token
//参数3:实体的泛型约束
//参数4:执行的回调监听
Messenger.getDefault().register(this, LoginViewModel.TOKEN_LOGINVIEWMODEL_REFRESH, String.class, new BindingConsumer<String>() {
    @Override
    public void call(String s) {
         
    }
});

在需要回调的地方使用token发送消息

//发送一个空消息
//参数1:定义的token
Messenger.getDefault().sendNoMsg(LoginViewModel.TOKEN_LOGINVIEWMODEL_REFRESH);

//发送一个带数据回调消息
//参数1:回调的实体
//参数2:定义的token
Messenger.getDefault().send("refresh",LoginViewModel.TOKEN_LOGINVIEWMODEL_REFRESH);

token最好不要重名,不然可能就会出现逻辑上的bug,为了更好的维护和清晰逻辑,建议以aa_bb_cc的格式来定义token。aa:TOKEN,bb:ViewModel的类名,cc:动作名(功能名)。

为了避免大量使用Messenger,建议只在ViewModel与ViewModel之间使用,View与ViewModel之间采用ObservableField去监听UI上的逻辑,可在继承了Base的Activity或Fragment中重写initViewObservable()方法来初始化UI的监听

注册了监听,当然也要解除它。在BaseActivity、BaseFragment的onDestroy()方法里已经调用Messenger.getDefault().unregister(viewModel);解除注册,所以不用担心忘记解除导致的逻辑错误和内存泄漏。

2、文件下载

文件下载几乎是每个app必备的功能,图文的下载,软件的升级等都要用到,RXMVVMBase使用Retrofit+Okhttp+RxJava+RxBus实现一行代码监听带进度的文件下载。

下载文件

String loadUrl = "你的文件下载路径";
String destFileDir = context.getCacheDir().getPath();  //文件存放的路径
String destFileName = System.currentTimeMillis() + ".apk";//文件存放的名称
DownLoadManager.getInstance().load(loadUrl, new ProgressCallBack<ResponseBody>(destFileDir, destFileName) {
    @Override
    public void onStart() {
        //RxJava的onStart()
    }

    @Override
    public void onCompleted() {
        //RxJava的onCompleted()
    }

    @Override
    public void onSuccess(ResponseBody responseBody) {
        //下载成功的回调
    }

    @Override
    public void progress(final long progress, final long total) {
        //下载中的回调 progress:当前进度 ,total:文件总大小
    }

    @Override
    public void onError(Throwable e) {
        //下载错误回调
    }
});

在ProgressResponseBody中使用了RxBus,发送下载进度信息到ProgressCallBack中,继承ProgressCallBack就可以监听到下载状态。回调方法全部执行在主线程,方便UI的更新,详情请参考例子程序。

3、ContainerActivity

一个盛装Fragment的一个容器(代理)Activity,普通界面只需要编写Fragment,使用此Activity盛装,这样就不需要每个界面都在AndroidManifest中注册一遍

使用方法:

在ViewModel中调用BaseViewModel的方法开一个Fragment

startContainerActivity(你的Fragment类名.class.getCanonicalName())

在ViewModel中调用BaseViewModel的方法,携带一个序列化实体打开一个Fragment

Bundle mBundle = new Bundle();
mBundle.putParcelable("entity", entity);
startContainerActivity(你的Fragment类名.class.getCanonicalName(), mBundle);

在你的Fragment中取出实体

Bundle mBundle = getArguments();
if (mBundle != null) {
    entity = mBundle.getParcelable("entity");
}

4、6.0权限申请

对RxPermissions已经熟悉的朋友可以跳过。

使用方法:

例如请求相机权限,在ViewModel中调用

//请求打开相机权限
RxPermissions rxPermissions = new RxPermissions((Activity) context);
rxPermissions.request(Manifest.permission.CAMERA)
    .subscribe(new Consumer<Boolean>() {
        @Override
        public void accept(Boolean aBoolean) throws Exception {
            if (aBoolean) {
                ToastUtils.showShort("权限已经打开,直接跳入相机");
            } else {
                ToastUtils.showShort("权限被拒绝");
            }
        }
    });

更多权限申请方式请参考RxPermissions原项目地址

5、图片压缩

为了节约用户流量和加快图片上传的速度,某些场景将图片在本地压缩后再传给后台,所以特此提供一个图片压缩的辅助功能。

使用方法:

RxJava的方式压缩单张图片,得到一个压缩后的图片文件对象

String filePath = "mnt/sdcard/1.png";
ImageUtils.compressWithRx(filePath, new Consumer<File>() {
    @Override
    public void accept(File file) throws Exception {
        //将文件放入RequestBody
        ...
    }
});

RxJava的方式压缩多张图片,按集合顺序每压缩成功一张,都将在onNext方法中得到一个压缩后的图片文件对象

List<String> filePaths = new ArrayList<>();
filePaths.add("mnt/sdcard/1.png");
filePaths.add("mnt/sdcard/2.png");
ImageUtils.compressWithRx(filePaths, new Subscriber() {
    @Override
    public void onCompleted() {
	
    }
	
    @Override
    public void onError(Throwable e) {
	
    }
	
    @Override
    public void onNext(File file) {

    }
});

6、其他辅助类

ToastUtils: 吐司工具类

SPUtils: SharedPreferences工具类

SDCardUtils: SD卡相关工具类

ConvertUtils: 转换相关工具类

StringUtils: 字符串相关工具类

RegexUtils: 正则相关工具类

六. 附加

1、编译错误解决方法

使用databinding其实有个缺点,就是会遇到一些编译错误,而AS不能很好的定位到错误的位置,这对于刚开始使用databinding的开发者来说是一个比较郁闷的事。那么我在此把我自己在开发中遇到的各种编译问题的解决方法分享给大家,希望这对你会有所帮助。

1.1、绑定错误

绑定错误是一个很常见的错误,基本都会犯。比如TextView的 android:text="" ,本来要绑定的是一个String类型,结果你不小心,可能绑了一个Boolean上去,或者变量名写错了,这时候编辑器不会报红错,而是在点编译运行的时候,在AS的Messages中会出现错误提示,如下图:

解决方法:把错误提示拉到最下面 (上面的提示找不到BR类这个不要管它),看最后一个错误 ,这里会提示是哪个xml出了错,并且会定位到行数,按照提示找到对应位置,即可解决该编译错误的问题。

注意: 行数要+1,意思是上面报出第33行错误,实际是第34行错误,AS定位的不准确 (这可能是它的一个bug)

1.2、xml导包错误

在xml中需要导入ViewModel或者一些业务相关的类,假如在xml中导错了类,那一行则会报红,但是res/layout却没有错误提示,有一种场景,非常特殊,不容易找出错误位置。就是你写了一个xml,导入了一个类,比如XXXUtils,后来因为业务需求,把那个XXXUtils删了,这时候res/layout下不会出现任何错误,而你在编译运行的时候,才会出现错误日志。苦逼的是,不会像上面那样提示哪一个xml文件,哪一行出错了,最后一个错误只是一大片的报错报告。如下图:

解决方法:同样找到最后一个错误提示,找到Cannot resolve type for xxx这一句 (xxx是类名),然后使用全局搜索 (Ctrl+H) ,搜索哪个xml引用了这个类,跟踪点击进去,在xml就会出现一个红错,看到错误你就会明白了,这样就可解决该编译错误的问题。

1.3、build错误

构建多module工程时,如出现【4.1.1、绑定错误】,且你能确定这个绑定是没有问题的,经过修改后出现下图错误:

解决方法: 这种是databinding比较大的坑,清理、重构和删build都不起作用,网上很难找到方法。经过试验,解决办法是手动创建异常中提到的文件夹,或者拷贝上一个没有报错的版本中对应的文件夹,可以解决这个异常

1.4、自动生成类错误

有时候在写完xml时,databinding没有自动生成对应的Binding类及属性。比如新建了一个activity_login.xml,按照databinding的写法加入<layout> <variable>后,理论上会自动对应生成ActivityLoginBinding.java类和variable的属性,可能是as对databding的支持还不够吧,有时候偏偏就不生成,导致BR.xxx报红等一些莫名的错误。

解决方法:其实确保自己的写法没有问题,是可以直接运行的,报红不一定是你写的有问题,也有可能是编译器抽风了。或者使用下面的办法 第一招:Build->Clean Project;第二招:Build->Rebuild Project;第三招:重启大法。

1.5、gradle错误

如果遇到以下编译问题:

错误: 无法将类 BindingRecyclerViewAdapters中的方法 setAdapter应用到给定类型; 需要: RecyclerView,ItemBinding,List,BindingRecyclerViewAdapter,ItemIds<? super T>,ViewHolderFactory 找到: RecyclerView,ItemBinding,ObservableList,BindingRecyclerViewAdapter<CAP#1>,ItemIds,ViewHolderFactory 原因: 推断类型不符合等式约束条件 推断: CAP#1 等式约束条件: CAP#1,NetWorkItemViewModel 其中, T是类型变量: T扩展已在方法 setAdapter(RecyclerView,ItemBinding,List,BindingRecyclerViewAdapter,ItemIds<? super T>,ViewHolderFactory)中声明的Object 其中, CAP#1是新类型变量: CAP#1从?的捕获扩展Object

一般是由于gradle plugin版本3.5.1造成的,请换成gradle plugin 3.5.0或以下版本

七. 混淆

例子程序中给出了最新的【RXMVVMBase混淆规则】,包含RXMVVMBase中依赖的所有第三方library,可以将规则直接拷贝到自己app的混淆规则中。在此基础上你只需要关注自己业务代码以及自己引入第三方的混淆,【RXMVVMBase混淆规则】请参考app目录下的proguard-rules.pro文件。

八. 组件化

进阶Android组件化方案,请移步:JetPackMVVMBaseComponent

九. About

本人喜欢尝试新的技术,以后发现有好用的东西,我将会在企业项目中实战,没有问题了就会把它引入到JetPackMVVMBase中,一直维护着这套框架,谢谢各位朋友的支持。如果觉得这套框架不错的话,麻烦点个 star,你的支持则是我前进的动力!

十. License

Copyright 2021 brilliantzhao
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
    http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Comments ( 0 )

Sign in for post a comment

About

一个Jetpack结合MVVM的快速开发框架,基于MVVM模式集成谷歌官方推荐的JetPack组件库:LiveData、ViewModel、Lifecycle、Navigation等组件 使用Kotlin语言,添加大量拓展函数,简化代码 加入Retrofit网络请求,协程,帮你简化各种操作,让你快速开发项目 spread retract
Cancel

Contributors

All

Activities

load more
can not load any more
1
https://gitee.com/brillantzhao/JetPackMVVMBase.git
git@gitee.com:brillantzhao/JetPackMVVMBase.git
brillantzhao
JetPackMVVMBase
JetPackMVVMBase
master

Search