Android 将多个模块生成一个 AAR 后提供 SDK

Android Gradle 打包每个库工程都会导出一个 AAR 文件。之前有尝试使用第三方插件 fat-aar 来合并打包,但打包时经常报错,合并时间也略长。此外此次导出的 SDK 需要做代码混淆,如果对每一个库都进行混淆文件非常麻烦,不便于统一管理,也不便于统一暴露接口。工程库之间的引用逻辑比较多,也增加了导包的配置成本,此外还要支持 AIDL 合并。

GitHub源码地址:https://github.com/RuiRay/MergeModuleAAR

这里选择将多个工程库合并到一个工程库后再打包的方式实现。

合并支持:

  • 源代码和资源文件:java/aidl/assets/libs/res
  • 清单文件:AndroidManifest.xml
  • 添加资源前缀(支持drawable/layout/string等类型,暂不支持style类型)
  • 统一包名,和BuildConfig、R包名路径的替换;
  • 多模块的合并,绝对路劲使用 path> 开头;
  • 自定义标签动作,如 //MergeReplaceNext> 用注释内容替换下一行;
  • DataBinding 生成 findViewById() 代码;

javaMerge 快速使用

请先下载jar文件: MergeModuleAAR/produce/script/javaMerge.jar

java -jar javaMerge.jar $ProjectPath "$ProjectPath/produce" $PACKAGE_NAME $RES_PREFIX "exo" ${MergeModuleName[*]}

具体使用参数参见: /MergeModuleAAR/produce/script/runSDK.sh

javaMerge 项目在 IDEA 中运行步骤

  • 打开 IDEA 编辑器;
  • File -> Open,选择 javaMerge 目录导入;
  • File -> Project Structure
    • Project Setting -> Project
      • Project SDK: Java1.8
      • Project language level: 8 - Lambdas ... etc.
      • Project compiler output: $projectPath/javaMerge/out
    • Project Setting -> Modules
      • 选中左侧的 javaMerge
      • 右侧中选择 Sources 卡片标签
      • 选中目录中的 src,右键操作窗中选择 Sources。(此时右侧 SourceFolders 看到 src 即正常);
    • Project Setting -> Libraries
      • 点击上面 + 号,选择 Java
      • 文件选择框中选择 ../javaMerge/lib/*.jar(全选所有jar)
      • 点击OK
  • 打开 OneSetup.kt 文件,运行;

或使用 kotlin 命令行编译,具体参考:https://www.runoob.com/kotlin/kotlin-command-line.html



代码实现逻辑

一、遍历读取需要合并的文件名

分析需要合并的文件,包含 libs/、src/main/ 和 build.gradle 文件。不需要合并的文件,例如:build/、test/ 等。

方便起见,代码中定义了需要读取文件的目录:

ItemFile("libs"),
ItemFile("src").addItemFile(
        ItemFile("main").addItemFile(
                ItemFile("aidl"),
                ItemFile("assets"),
                ItemFile("cpp"),
                ItemFile("java"),
                ItemFile("jniLibs"),
                ItemFile("res"),
                ItemFile("res-night"),
                ItemFile("AndroidManifest.xml", false)
        )
),
ItemFile("build.gradle", false)

当然也可以使用类似于 .gitignore 的方式,定义排斥的目录名来实现。

二、写文件

写文件逻辑比较繁琐,需要先读取资源文件和清单文件的内容提取后再合并写出。

  1. aidl/ assets/ java/ 等目录下的文件直接复制,不存在重复不会有冲突

  2. res/values/ 下同名文件(strings、colors、styles等)内容合并

  3. AndroidManifest 合并

    • 合并权限,过滤掉重复权限
    • 合并 标签下内容,如:“meta-data”, “activity”, “service”, “receiver”, “provider”
    • 将以 . 开头的相对 name 替换成绝对路径
      如:android:name=".ui.activity.PlayerActivity" 改成 android:name="com.xxx.ui.activity.PlayerActivity"
  4. build.gradle 合并

    • 解析 android 节点下配置
    • 解析 dependencies 节点下依赖
    • 去除重复

    用正则表达式匹配,生成结果不是很准确,仅用于最后参考对比。

三、遇到的问题

  1. 统一导包、RBuildConfig

    由于这两个类是自动生成跟随库的包名的,每个库对应一组。合并工程后只剩下一组,之前在代码中引入的这两个文件包名现在无效。

    代码中使用正则表达式进行替换,例如:

    "import com\\.[a-zA-Z_0-9.]*?\\.R;" to "import $packageName.R;"
    

四、补充 DataBinding 的替换

在开发应用时为开发方便实用了 databinding 功能,打包后由于要移植到一个老的项目中,而那个项目的 gradle 版本还是 2.10,如果更新到 SDK 实用的 4.1 版本又会带来很多麻烦。不修改 2.10 生成目录是在应用包名下,导致 SDK 中的引用找不到生成后的 DataBinding。自己代码中虽然用到了 databinding,不过只停留在用 findViewbyId() 的功能上,想到这里就决定按照它的格式自己生成一个壳,移除 databinding 来兼容不同版本。

代码实现方式
  1. 通过读取 layout 文件,生成 findViewbyId() 功能的代码

    • layout/ 目录下的文件以 标签开头的就是 databinding 文件
    • 读取文件,匹配包含 id 的标签,并生成代码
  2. 移除文件中的 标签,转移域名到下一级根目录

  3. 替换导包 ViewDataBindingDataBindingUtil

示例代码结构:

创建与 databinding 一样的类名和函数,用于移花接木的壳:

public interface ViewDataBinding {

    View getRoot();
}

public class DataBindingUtil {

    public static <SV extends ViewDataBinding> SV inflate(LayoutInflater layoutInflater, int layoutId, Object o, boolean b) {
        if (layoutId == R.layout.activity_base) {
            return (SV) new ActivityBaseBinding(layoutInflater, layoutId);
        }
        return null;
    }
}

自动生成 findViewById 功能的代码,成员函数命名与 databinding 一致:

public class ActivityBaseBinding implements ViewDataBinding {

    private final View mRootView;
    public final android.support.v7.widget.Toolbar toolBar;

    public ActivityBaseBinding(LayoutInflater layoutInflater, int layoutId) {
        mRootView = layoutInflater.inflate(layoutId, null);
        toolBar = (android.support.v7.widget.Toolbar) mRootView.findViewById(R.id.tool_bar);
    }

    @Override
    public View getRoot() {
        return mRootView;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值