(Gradle 和 OrmLite 配置) 如何在 Java 编译后但生成 .apk 之前添加资源文件?

2023-11-30

注意:我已经接受了答案并授予了赏金,但是,最终决定我解决这个问题的方法远非最佳。经过进一步思考,我得出的结论是,在构建过程中修改 .apk 可能不是实现这一目标并保持其长期运行的最安全或最可持续的方法。

我在这个问题的最底部添加了一种替代方法,最终完成了同样的事情。我选择使用的这种方法虽然并不完美,但不需要通过黑客手段搞乱 .apk 程序集的内部结构。


我想用OrmLite以其预先生成的配置文件,它是由一个普通的 Java 类生成的,如下所示:

public final class DatabaseConfigGenerator extends OrmLiteConfigUtil{

private static final Class<?>[] MODELS = {
        Table1.class
};

private static final String ORMLITE_CONFIGURATION_FILE_NAME = "ormlite_config.txt";

public static void main(String[] args) throws Exception {
    File configFile = new File(new File("").getAbsolutePath().split("app" +File.separator + "build")[0] +
                           File.separator +
                           "app" + File.separator +
                           "src" + File.separator +
                           "main" + File.separator +
                           "res" + File.separator +
                           "raw" + File.separator +
                           ORMLITE_CONFIGURATION_FILE_NAME);
    if (configFile.exists()){
        configFile.delete();
    }
    writeConfigFile(configFile, MODELS);
}
}

生成的 ormlite_config.txt 文件将放置在 res/raw/ 下,如下所示:

#
# generated on 2014/06/20 10:30:42
#
# --table-start--
dataClass=com.example.app
tableName=table1
# --table-fields-start--
# --field-start--
fieldName=field1
# --field-end--
# --table-fields-end--
# --table-end--
#################################

每次修改该类本身或其中一个模型类时,都需要通过 Java 直接运行该类,以便配置是最新的并且 OR 映射可以按预期运行。

由于我最近切换到 Android Studio 和 Gradle,并且我喜欢构建过程的灵活性和自定义选项,因此我想通过我的应用程序的 build.gradle 自动生成上述 ormlite_config.txt。我已经定义了一个工作任务,它从 app/build/classes 内部运行 DatabaseConfigGenerator.class 并生成配置,并且我还将它与compileJava Gradle 任务挂钩,因此配置是在 Java 文件编译后生成的, .class 文件是最新的:

android.applicationVariants.all { variant ->
    ext.variantname = "compile" + variant.name.capitalize() + "Java"
    def javaTask = project.tasks.findByName("${variantname}")
    if (javaTask != null) {
        println "Adding post-compile hook to ${variant.name}"
        javaTask.finalizedBy runOrmGenTask
    }
}

这工作得很好,我可以看到 app/src/main/res/raw 中的 ormlite_config.txt 发生变化,但由于某种原因(我猜任务顺序不正确),当我提取 .apk 时,它仍然包含先前版本中的 ormlite_config.txt 已过时...

谁能告诉我或向我推荐 Android Gradle 构建系统的构建任务顺序的链接?我已经找了好几天了,还是找不到。我需要找到一种方法在编译 Java 文件之后但在打包 .apk 之前生成 ormlite_config.txt,因此它将被包含在内。

像这样自动化它真的很棒,因为这样它就会在每次构建过程中一步发生,因为配置将始终与模型类保持同步,我永远不必再考虑它。我有一种直觉,他可以做到,我只需要弄清楚到底是如何做到的。

免责声明:我仍处于学习 Gradle 工作原理的最初阶段,因此我对此处提到的一些内容的理解可能还有很大差距。如果有请告诉我,我想学习!


EDIT 1:

我认为让 DatabaseConfigGenerator 将文件写入不在以下位置会更有意义:

app/src/main/res/raw 

但在

app/build/res/all/<variant_name>/raw

因为,据我所知,这是最终资源在打包到 .apk 之前放置的位置(我可能是错的,所以如果我错了,请纠正我)。

根据@pepyakin的回答,我还稍微更新了我的build.gradle:

gradle.projectsEvaluated {
    android.applicationVariants.all { variant ->
        def ormGenTask = project.tasks.findByName("genOrmConfig" + variant.name.capitalize())
        def javaCompileTask = project.tasks.findByName("compile" + variant.name.capitalize() + "Java")
        def packageTask = project.tasks.findByName("package" + variant.name.capitalize())
        ormGenTask.dependsOn(javaCompileTask)
        packageTask.dependsOn(ormGenTask)
    }
}

同样,它运行良好并在 Gradle 控制台中输出以下内容:

...

:app:processDebugResources UP-TO-DATE
:app:generateDebugSources UP-TO-DATE
:app:compileDebugJavaNote: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

:app:preDexDebug UP-TO-DATE
:app:dexDebug
:app:genOrmConfigDebug
Writing configurations to /home/user/development/app/com.example.app.android/app/build/res/all/debug/raw/ormlite_config.txt
Wrote config for class com.example.app.model.Table1
Done.
:app:processDebugJavaRes UP-TO-DATE
:app:validateDebugSigning
:app:packageDebug
:app:assembleDebug

...

所以上面我看到app:genOrmConfigDebug任务巧妙地夹在 Java 编译和打包之间。

然而,由于某种原因,生成的 .apk 仍然包含来自早期版本的 ormlite_config.txt,它与我对模型类所做的更改(例如定义一个新的 @DatabaseField)不是最新的!

我的直觉是:

  1. 我将 ormlite_config.txt 写入错误的位置(其中 会很奇怪,因为一秒钟后它就会被提取到 .apk 中 构建),或
  2. 的内容app/build/res/all/<variant_name>/raw被捡起 前compile<variant_name>Java被执行

如果是后者,我不知道如何处理这个......任何建议表示赞赏!


EDIT 2:

看起来2.确实如此。我并排打开两个目录(app/build/apk and app/build/res/all/<variant_name>/raw)并且事件的顺序是:

  1. 里面生成了最新的ormlite_config.txtapp/build/res/all/<variant_name>/raw
  2. .apk 是在里面创建的app/build/apk
  3. 解压.apk后,查看下res/raw,来自一个构建之前的过时的 ormlite_config.txt 位于里面

如果熟悉 Gradle .apk 生成过程内部情况的人能够告诉我我在这里缺少什么,我将非常感激!


EDIT 3

我还没有放弃这个!经过更多研究后,我发现了一个Android Gradle 构建系统工作流程图.

根据该图,资源(在/res下)被合并和收集,并且R类在编译Java代码之前被更新。这是有道理的,因为如果类引用 R 中未包含的资源值,编译就会失败。

所以现在我确信与我的案例相关的三个步骤的执行顺序是:

  1. 资源合并组装,R.java更新
  2. Java被编译成类
  3. .apk 放在一起

现在,如果我的 ormlite_config.txt 在 Java 编译后重新生成(如我在编辑 2 中包含的 build.gradle 片段中定义的那样),但最终不是生成的 .apk 的一部分(早期版本的文件是相反)即使我把它放在下面/build/res/all/<variant name>/raw在步骤 3 之前,这只能意味着要包含在 .apk 中的实际资源文件已经移动到除buid/res/all/<variant name>,在步骤 1. 和 2 之间。

现在我只需要弄清楚它在哪里,所以我可以将新生成的 ormlite_config.txt 放在那里,如果它以任何可能或可行的方式......

和以前一样,如果有人能在这个问题上启发我,我将非常感激。


另一种(更简单)的方法

正如问题最上面所述,最后我决定采用一种替代方法,该方法更简单,并且不是对 .apk 组装过程的黑客攻击,这正是我最初打算做的。

步骤如下:

  • 对数据库配置生成器类进行编码以编写ormlite_config.txtint 你的应用程序的src/main/res/raw(您可以使用我在这个问题的最顶部包含的 DatabaseConfigGenerator 类作为模板)。将此类保留在应用程序的包结构中,不要将其作为单独的应用程序或模块,没有理由这样做。所以你可以把它放在 com.your.app.database 或其他什么地方。

  • 在 Android Studio 的顶部工具栏中,单击“Make”和“Run”按钮之间的小下拉框:

Run configuration button

  • 它将打开一个菜单,您可以在其中选择现有的运行配置之一或编辑配置。选择后者:

Edit configurations

  • 将打开“运行/调试配置”窗口,您应该在左上角单击绿色小加号,然后选择“应用程序”作为新配置的类型:

Add run configuration

  • 将打开一个表单,您将在其中定义 DatabaseConfigGenerator 的运行配置,因为它需要作为 Java 应用程序运行,与 Android 应用程序分开。这里只有几个字段需要修改。首先,为新的运行配置命名 (1),然后选择 DatabaseConfigGenerator 作为主类 (2),然后在模块类路径下选择 DatabaseConfigGenerator 和模型类所在应用程序的模块 (3),然后删除所有通过选择条目并单击红色减号 (4),最后单击“应用”(5)。现在,您可以在左侧的“Android 应用程序”部分下单击您的应用程序 (6)。

Define a run configuration for the generator

  • 您需要在这里做的最后一件事也是最重要的一件事,它将把所有内容放在一起,使您的应用程序首先构建自身,然后生成最新的ormlite_config.txt,然后再次构建自身(尽管比第一次快得多),以便这个新生成的配置实际上包含在最终的 .apk 中。为了实现此目的,您需要修改应用程序运行配置的“启动前”部分 (1)。您可能已经在此处有了一个“Gradle-aware Make”,它实际上是在通常的构建过程中编译您的应用程序并将其打包到 .apk 中的。如果没有,请将其添加为第一个条目。之后,添加另一个条目,但这次是为“数据库配置生成器”运行配置,您之前创建了该配置,因为这将确保ormlite_config.txt是基于新编译的模型类生成的并且是最新的。最后,添加另一个“Gradle-aware Make”,以确保生成一个新的 .apk,它现在也包含这个最新的ormlite_config.txt. 现在点击“应用”(2),就这样!

Put it all together

  • 从此时起,每次单击 Android Studio 窗口顶部工具栏中的“运行”按钮,同时选择“应用程序”运行配置,您都可以确定ormlite_config.txt生成的 .apk 中的内容将与您对模型类或 DatabaseConfigGenerator 本身所做的任何更改保持同步。

对于这个解决方案,我从以下两个答案中获得了灵感:

  1. 设置 Gradle 以在 Android Studio 中运行 Java 可执行文件

  2. 用于生成 ORMLite 配置的 Android Studio 运行配置

最后,我决定将完整的解决方案放在一个地方,并在这里详细描述它。

这种方法有三个小警告,YMMV 你是否可以接受它们:

  1. 这仅适用于“运行”操作,不适用于“制作”操作,这意味着即使您只想构建 .apk 但不实际运行它,也必须启动运行。然后可以在下面找到生成的 .apkapp/build/apk/并根据您正在构建的变体进行命名(对于调试版本,通常是 app-debug-unaligned.apk,对于发布版本,通常是 app-release.apk)。

  2. 这种方法本身意味着“Gradle-aware make”将在每次单击“运行”时运行两次,这将导致构建时间稍长,但我没有注意到太大的差异(android gradle插件足够智能)来识别哪些资源自上次构建以来没有更改,并且会在第二次跳过许多不必要的步骤),从而导致构建时间可能延长 20%(不要让我知道这个数字)。

  3. 如果您在团队环境中工作并正在使用版本控制,那么此配置不可跟踪会有点糟糕,因此团队中的每个开发人员都必须单独完成此过程,并且不能简单地将其作为一部分进行检查例如,.git 中的存储库。这是因为运行配置是在项目根目录内的 .idea/workspace.xml 下定义的,人们普遍认为不应该在版本控制中跟踪它,因为它是特定于机器的。

可能有一些方法可以在团队级别上删除像这样定义运行配置的过程中的一些手动步骤,但似乎不可能以干净的方式完全自动化它。不过,我可能是错的,如果是这样的话,请随时告诉我。

希望这可以帮助!


问题是资源没了compiled by aapt when R.java被生产出来(所以beforejavac)。那些编译资源然后用于构建 apk。据我所知那些编译资源只是一种 zip 存档。

因此,为了实现您的目标,您需要修改这些compiledjavac编译后的资源。

您可以尝试定义一个新任务(我们称之为addOrmToRes)调用aapt带有修改所需的参数编译资源.

aapt 位于<ANDROID_SDK_HOME>/build-tools/<version>/

aapt 用法表明:

aapt a[dd] [-v] file.{zip,jar,apk} file1 [file2 ...]
   Add specified files to Zip-compatible archive.

所以执行这样的事情:

aapt add /build/apk/myapp.apk ormlite_config.txt

or this

aapt add <the_path_to_compiled_resources> ormlite_config.txt

在您的新自定义任务中addOrmToRes应该可以解决问题。所以回答你的edit 3 : aapt add可能是解决方案。

要将其集成到构建中,应该像这样:

runOrmGenTask.dependsOn(variant.javaCompile) 
addOrmToRes.dependsOn(runOrmGenTask)
addOrmToRes.dependsOn(<the task creating the apk>)
<the task signing the apk>.dependsOn(addOrmToRes)

请注意,我并未在此列出所有任务。但我想你明白了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(Gradle 和 OrmLite 配置) 如何在 Java 编译后但生成 .apk 之前添加资源文件? 的相关文章

随机推荐