上文总结了三种多模块开发的方法。

  • 第一种:在setting.gradle中定义子模块然后 api Project(':...'),直接引用 。
  • 第二种,使用 maven-publish 部署至本地仓库
  • 第三种,使用 jitpack.io 等部署至远程服务器

我的第一个开源项目就依次用过这三种方式:

  1. 最初使用第一种方法,好处在于,修改后可以直接构建和运行。但不利于维持较大公共库,容易造成整个编译进程变慢和分支混乱。
  2. 后来使用 maven-publish 插件,分开编译各个组件,源代码仓库瞬间清爽起来。
  3. 再后来陆续有人问我怎么编译,答曰需要将各模块分别编译,直到编译完成主程序为止。深感无法吸引开源参与者,于是花了一个晚上部署,终于启用 jitpack.io。

这是一个词典应用,名为“无限词典”,从2019年开源至今,现在编译起来应该很容易了:https://github.com/KnIfER/PlainDictionaryAPP

Jitpack.io 使用指南(踩坑记录)

在最外层的build.gradle添加 jitpack 作为maven仓库,建议排序排在最后面。

buildscript {
    repositories {
        mavenLocal()
        maven { url 'https://jitpack.io' }
        ……
}
allprojects {
    repositories {
        mavenLocal()
        maven { url 'https://jitpack.io' }
         ……
}

依赖引用方法见官方主页,支持gitee:https://jitpack.io/

  • 各个组件项目的settings.gralde中,勿包含与主模块同名的项目名称,如:
rootProject.name = "Paging"
include ':Paging'
include ':Logger'

settings.gralde文件用于指示gradle项目包含哪些模块文件夹(即使用第一种分模块方式),
其中 rootProject.name 可能会导致jitpack编译器报错:

* What went wrong:
A problem occurred evaluating project ':Logger'.
> Failed to apply plugin 'com.android.internal.library'.
   > Your project contains 2 or more modules with the same identification com.github.appxmod:Paging
     at ":" and ":Paging".
     You must use different identification (either name or group) for each modules.
  • 一些调用本地指令的gradle脚本不能用。比如我在“为Android Studio制作提示音,用音乐舒缓压力”一文中记录的提示音脚本就不能使用了。

  • 版本管理很麻烦,所以建议所有版本号都使用最新版本(:latest.release),如

    api('com.github.appxmod:Metaline:latest.release')
  • jitpack支持多模块项目(即使用第一种分模块方式),此时,需要注意:
  1. 依赖引用方式变为 com.github.用户名.主项目名称:子模块名称:版本号,而不是原来的 com.github.用户名:项目名称:版本号(参考jitpack.io的编译日志),如 appxmod/GlideModule项目中同时包含四个模块,引用起来是这个样子的:
   api('com.github.appxmod.GlideModule:GlideModule:latest.release')
   api('com.github.appxmod.GlideModule:GifDecoder:latest.release')
   api('com.github.appxmod.GlideModule:DiskLruCache:latest.release')
   compileOnly ('com.github.appxmod.GlideModule:Compiler:latest.release')
   annotationProcessor ('com.github.appxmod.GlideModule:Compiler:latest.release')
   implementation('com.github.appxmod.GlideModule:Annotation:latest.release')
  1. 模块之间相引用,应直接使用 project 指令,如:api project(':模块名称'),而不是 api 'com.github……'
  • jitpack支持github或gitee的组织,和用户名一样,如 com.gitee.组织名称

  • maven-publish 插件会影响 jitpack 编译。比如使用 from components.all 指令,就会生成 release、debug多种编译版本,可能会导致引用出错,比如gradle根据引用项目去 jitpack.io 服务器寻找 “某组件-release.aar”,但是没有这个文件,因为对方使用的是 from components.release,只有一个编译版本,只有 “某组件.aar”,没有 “某组件-release.aar”(参考编译日志)。

  • jitpack.io 的仓库可直接在浏览器中访问。

  • jitpack 一般是去编译 release tag,需在浏览器中访问,查看编译状态。编译速度很慢,似要排队。建议先通过本地编译,再去浏览器触发 jitpack 编译。编译必须有先后顺序,依赖项少的先来。如果编译失败,不一定要新建 release tag,可在 github 或 gitee 删除 release、tag,然后重建同名release,最后在 jitpack 页面上点“叉”图标,重新触发编译。

请添加图片描述

上面的log图标,点进去查看编译日志,绿色代表编译成功,可以正常使用了。

初次大量使用 jitpack,如有错误请指正。

混合使用本地仓库

jitpack上面的内容不是自己编译后上传的,而是远程机器编译。如果是独自研发,或者是原创者,那最好能够切换回本地仓库,自己去编译。

本地编译时,为每个组件加载额外的 gradle 属性文件,覆盖原有属性。

// Load keystore
def localMavenFile = rootProject.file("localMaven.properties");
if (localMavenFile.exists()) {
    def props = new Properties();
    props.load(new FileInputStream(localMavenFile ))
    props.each { prop ->
        project.ext.set(prop.key, prop.value) // 覆盖
    }
}

默认的项目属性文件 gradle.properties :

libs_lucene=com.github.appxmod:Lucene:latest.release

用于覆盖重写依赖项的属性文件 localMaven.properties :

libs_lucene=org.appxmod.lucene:Core:latest.release

最后使用:

dependencies {
  api libs_lucene
}

安卓打包debug使用与release相同的签名,使得调试版本和发行版本可相互覆盖安装

// Load keystore
def keystorePropertiesFile = rootProject.file("keystore.properties");
def keystoreDefined = keystorePropertiesFile.exists();
if (keystoreDefined) {
    def props = new Properties();
    props.load(new FileInputStream(keystorePropertiesFile))
    props.each { prop ->
        project.ext.set(prop.key, prop.value) // 直接加载到项目属性,方法来自 https://stackoverflow.com/questions/11749384/gradle-include-properties-file#answer-33484783
    }
}

项目根目录新建文件keystore.properties存储密码:

keys_storePassword=密码1
keys_keyPassword=密码2
keys_keyAlias=键名
keys_storeFile=C:\\文件路径.jks

注意没有 keys_ 前缀,会与默认的项目属性冲突

最后再在 gradle 中使用:

android { ……
//签名设置
    signingConfigs {
        debug {
            if (keystoreDefined) {
                storeFile file(keys_storeFile)
                storePassword keys_storePassword
                keyAlias keys_keyAlias
                keyPassword keys_keyPassword
            }
        }
    }

从TTS 、 legado等开源项目中学来的,不过有改进:1. 无需将密码存储在 gradle 中。 2. 无需加载到 keystoreProperties 对象,直接将密码等常量加载为全局属性。

修复?b问题

多次编译竟然会报错:This feature requires ASM7 ,rebuild,成功,接着修改一处字符串反复测试编译,一两三次增量编译,成功,然后再增量编译就会出现令人费解的 ASM7 问题。

毕竟是奇迹产品,一个小公司制作的世界级开发工具,虽然才华横溢,但出现种种问题也是情理之中。我常常在想,如果没有kotlin分心,如果那些上游开发者集中精力,我是否能使用更好的android studio?

报错模块是 glide 魔改组件:

error processing C:\Users\TEST\.m2\repository\org\appxmod\glide\Compiler\1.0.0\Compiler-1.0.0.jar
java.lang.UnsupportedOperationException: This feature requires ASM7
	at org.objectweb.asm.ClassVisitor.visitNestHost(ClassVisitor.java:150)
	at org.objectweb.asm.ClassReader.accept(ClassReader.java:541)
	at org.objectweb.asm.ClassReader.accept(ClassReader.java:391)
	at com.android.builder.desugaring.DesugaringClassAnalyzer.analyze(DesugaringClassAnalyzer.java:153)
	at com.android.builder.desugaring.DesugaringClassAnalyzer.analyzeJar(DesugaringClassAnalyzer.java:100)
	at com.android.builder.desugaring.DesugaringClassAnalyzer.analyze(DesugaringClassAnalyzer.java:68)
	at com.android.build.gradle.internal.tasks.DesugarIncrementalHelper.lambda$getInitalGraphData$4(DesugarIncrementalHelper.java:146)
	at java.base/java.util.concurrent.ForkJoinTask$AdaptedCallable.exec(ForkJoinTask.java:1448)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)

最后发现是连续用 build > make project 菜单构建项目就会报错,也不知道什么原因,反正是直接运行反而不会报错,不用修复。这个问题以前遇到过,没写笔记记住。

安卓打包release前执行脚本

无限词典打包release前需要编译一下某些js或html内容,需要打包一些资源文件。

算了。太难实现了,谁会?

还是用单独运行IDEA吧。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐