背景
某些客户因为工作环境的特殊性,无法正常地去通过远程依赖的方式来对sdk包进行依赖,只能通过本地依赖的方式进行依赖,这个时候我们就需要将我们的sdk进行aar打包处理。
在这里将会着重对aar包化时的踩坑和排查进行介绍。
aar包的注意点
通常我们编写了一个项目时是需要引入多个第三方库,可以大量节省我们的时间。我们可以通过下面的make Module ‘xxx' 的方式进行aar包打包。 但是当我们将这个aar包直接提供其他项目进行依赖时就会发现出现一个问题,xxx class not found,这个时候就会发现有很多类都没有找到,仔细进行排查时发现这些类都是第三方库的类。没错,这个就是aar包打包的一个缺陷,他不能将第三方库的包一并打入进去,需要你收到将这些第三方包的jar包、aar包逐个添加进去才行。
其实我们通过远程依赖方式也是很容易产生冲突,例如版本冲突、类名相同冲突,A包依赖库与B包依赖库相同等冲突,这些冲突都是交给了Gradle来替我们解决的,当然某些情况还是需要我们人为介入将通过Gradle来决定如何解决冲突。
在早期没有Gradle、Maven等这些构建工具的时候,只能人为手动一个一个去处理冲突问题。
回到正题,我们这里也是一样,只能人为将这些依赖库的jar包和aar包挑选出来,然后放入到依赖的项目里面去。当然这样人为手动去进行打包是比较苦逼的,可以去使用Gradle插件:fat-aar-android https://github.com/kezong/fat-aar-android
但是很可惜这个插件是有很多问题的例如不支持注解处理,aar包找不到R资源等,这些都需要根据你自己的项目进行调整。所以这里只能人为地去筛选aar包导入到项目中,并没有去使用这个插件
第三方aar包导入
普通依赖库导入
这里我先抛出一个概念,导入的依赖库我这里是分为两种,一种就是普通的依赖库,一种是注解处理器的依赖库(这个让我吃尽苦头,下面也是着重介绍这种依赖库怎么处理)。
对于这种普通的依赖库其实处理起来很容易,通过gradlew demo:dependencies来进行打印即可知道我们的项目依赖了什么第三方库,然后去.gradle\caches\modules-2或是.gradle\caches\transforms-2中去找对应的jar包即可了,缺少什么就去导入什么就好了。
注解处理依赖库导入
源起
按照我们上面的处理后,能够编译成功并安装apk了,但是当我们运行程序的时候就闪退崩溃了,然后看log提示
redpack\model\datasource\database\dao\RedpackCacheDAO_Impl class not fou
这个是什么鬼?我们的项目根本就没有用到这个类,这个是什么东西?从这个类的命名方式、包名来看,貌似是注解处理器生成出来的东西。看了一下我们的项目,我们的是使用了room这个第三方库,用于去管理数据库的,这么一看貌似是这个第三方库没有导进去,但是奇怪的是我们在dependencies中是没有看到这个库打印出来,并且在External Libraries中是没有看到这个库的,奇怪了。。。(这个会在下面进行解释)
那么我们直接将这个库的jar包扔进libs吧,既然他提示没有生成这个库中。但是他提示出了这一个错误
大概提示就是说该库存在注解处理器,需要特别声明。在该log中提示你可以配置android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true。
但是按照上述配置发现是不成功的,提示没有includeCompileClasspath 属性,这个属性在高版本的Gradle中移除了。
上外网找了一圈后发现我们可以通过这种方式进行依赖 annotationProcessor files('/libs/room-compiler-2.0.0.jar')
破局
尝试一下,发现的确没有了上面的那一个错误了,但是报了一个这样的错误出来,然后没有其他信息了。 只有这个错误信息让我百思不得其解,google了一圈也没有什么头绪,这个类是什么鬼?
后面经过一番查找发现该类是在一个com.google.auto:auto-common这个包中发现有这么一个类。然后就猜想是否是这个注解处理器需要依赖到这个类呢?然后我就把这个包扔进libs中看一下。
然后我就使用这种方式来进行依赖 implementation files('/libs/auto-common-0.6.jar')
发现还是上面的报错信息,这个时候陷入困局了,然后也是一顿google还是没有什么头绪,后面突然想到了注解处理器声明需要使用annotationProcessor声明,那么这个依赖是否也需要使用这个依赖呢?尝试一下
annotationProcessor files('/libs/auto-common-0.6.jar')
发现这个错误没有了,换成另外一个类的错误提示了。
在这里发现我们可以通过annotationProcessor files(libs/xxx)的方式为注解处理器的依赖进行声明。
那么好了,我们可以通过这种方式逐个将注解处理器需要的类依赖进去就行了,但是这里就出现一个问题了,这里报的错误信息只有包名,但是并没有指明是那一个第三方库持有了这个类,这里只能到**.gradle\caches\modules-2**中逐个依赖包去查找,幸运的是大部分库的包名是与这个类相识的,所以还是能找到大部分的依赖库,仅剩下的小部分是没有的,所以只能一个一个去找了,这个就很痛苦了。
思考:按理来讲我们是可以通过Gradle dependencies来将库的依赖打印出来的,但是通过该命令是没有打印出来了,后面经过查找发现这里涉及到了 编译时和运行时的问题。
在网上找的都是打印运行时的依赖,并没有编译时的依赖,按理来讲是可以通过自定义task来讲编译时的依赖进行打印,但是没有找到类似的案例,也许某些第三方Gradle Plugin可以做到,但是也没有找到,这里只能用笨方法一个一个去找了,幸运的的是我们这里只有room这个第三方库有这个问题,其他的没有,所以并不算多。
在这里涉及到了编译时和运行时的概念,如果有使用过ARouter的和使用过注解处理器的朋友一定不陌生,这里简单介绍一下这两个的不同
编译时是不会将jar包打进去apk的,只是在编译时期使用到,在这里room注解处理器中就是用于生成java文件,然后一同编译。
运行时时会将jar包打进apk的,在运行时就会使用到的,例如retrofit2的注解,他是通过反射的方式获取到注解的信息,这个过程是在应用运行时才会使用到,所以这里会将需要包打进来的。
在我们的项目中也使用到的了Retrofit2的,Retrofit2也是用到了注解,但我们并不需要像上面导入room.jar包那样为他的依赖进行多个声明,Retrofit2也能正常运行,就是因为Retrofit2是运行时使用的,通过反射来获取到API接口的参数,所以只需要将Retrofit使用到的依赖库直接放入libs中即可。
回到正题,为什么room这个库要这么复杂,需要为他的依赖类逐个进行声明?这个就是因为这个room库的注解处理器是在编译时处理,在上面提到了编译时使用的jar包是不会编译,而我们的依赖第三方库是使用下面的这种方式进行依赖
api fileTree(dir: 'libs', include: ['*.aar','*.jar'])
这种api依赖方式貌似是运行时进行的依赖,而编译时需要的依赖是导不进去的,所以就需要使用到了annotationProcessor这个关键字进行声明,通过annotationProcessor声明后在编译时就能依赖到了。
这个也正好回应到了上面为什么执行gradle demo:dependencies命令不能打印出注解处理器的依赖出来,因为这个命令只能将运行时的依赖打印出来,并不能将编译时的依赖打印出来。
而room的这个库并不是独立依靠自己就完成所有的注解处理,他还依赖了很多第三方库来进行注解处理,例如javaPoet这个库,所以我们就需要同时将这个javaPoet依赖进去。
这个也是痛点问题,A库依赖B库,B库依赖C库,就需要将ABC都找出来,逐个进行annotationProcessor声明才可以
疑点:如果按照我上面的猜测,按道理来说 使用compileOnly关键字去声明应该是可以的,但是却依然依赖不成功,只有使用annotationProcessor才成功,这就非常奇怪了
风云再起
按照上面的配置其实基本就能完成本地库处理了,但是在这里还遇到了另外一个问题,编译时通过了,但是到了最后打包的时候出现了一个冲突问题
依赖的androidx.annotation:annotation:1.0.0 和 我们room中需要的依赖库 annotation-1.3.0.jar产生了冲突了。
而room中一定是需要 annotation-1.3.0.jar。
所以这里我们的解决方式是 新开一个module,这个module专门用于注解处理的,而Gradle有对project进行隔离的特性,所以这个module中去进行注解处理器声明,这样就不会影响到其他module,从而引起类冲突的问题。
源灭
这里总结一下,就是aar包的打包并不能将其需要用到的子包一并打进aar包里面,这个其实是合理的,如果都能打进aar包的话,很容易产生依赖冲突,而且很不好解决。
面对上面的情况我们就需要手动将用到的子包一并添加进去才行。
而如果依赖到了注解处理器的话,我们要分清楚这个注解处理器是否是编译时,如果是的话就需要使用annotationProcessor进行声明,并且这个注解处理器所依赖的子库都要用annotationProcessor进行声明。
为了项目的更好管理,最好是新开一个module用于管理注解处理,而我们只需要通过
annotationProcessor project (':anootationproject')
声明这个anootationproject module用于注解处理即可了 这里我贴一下room这个库中用到的jar包,方便后续管理
自此我们的所有依赖问题都解决了,项目也能够正常跑起来了
奇淫巧技
其实如果遇到了上面的问题,我们还有一些比较特殊的技巧,可以快速进行接入。
巧技一:
上面最主要的问题是,无法在编译时生成出RedpackCacheDAO_Impl这个类,那么简单我们在能正常使用远程依赖的项目中运行起来,那么肯定能正常生成出这个类,然后我们就根据这个类所在的包,直接将该类 复制到 aar包依赖项目对应的位置即可,缺少那一个类就生成出来,复制过去就行了。
但这里是有一个问题就是,如果我们的sdk对于使用到这一部分功能有所变动,那么生成出来的类就会发生变动,需要将发生变动的类一同移交给客户,这个就不太利于后续的维护,不过如果是为了快速接入,使用这个方法也是可以的
巧技二:
使用远程依赖时,一般会将下载下来的库放入到
.gradle\caches\modules-2\files-2.1
或.gradle\caches\transforms-2\files-2.1
这两个地方,当需要依赖的时候就会优先从缓存库中取出来,所以可以直接将这两个地方的文件拷贝处来,直接交给客户,然后使用远程依赖的方式进行依赖,不过使用这个方式弊端巨大,所以就没有尝试过这种方式了,不过按理来讲应该是可行的