android-(Gradle和OrmLite配置)如何在Java编译之后但.apk生成之前添加资源文件?
作者:互联网
注意:我已经接受了答案,并授予了赏金,但最终决定我对这个问题的方法远非最佳.经过进一步思考,我得出的结论是,在构建过程中修改.apk可能不是实现此目标并使之长期运行的最安全或最可持续的方法.
我在此问题的最底部添加了一种替代方法,最终实现了相同的目的.我选择使用的这种方法虽然不完美,但不需要通过黑客弄乱.apk程序集的内部.
我想将OrmLite与pre-generated configuration file一起使用,它由普通的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--
#################################
每次修改自身或修改Model类之一时,都需要直接通过Java运行该类,以便使配置最新,并且OR映射可以按预期运行.
由于我最近切换到Android Studio和Gradle,并且喜欢构建过程的灵活性和自定义选项,因此我想通过我的应用程序的build.gradle自动生成上述ormlite_config.txt.我已经定义了一个工作任务,该任务从app / build / classes内部运行DatabaseConfigGenerator.class并生成配置,并且还将其与compileJava Gradle任务挂钩,因此在编译Java文件并生成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的工作原理的起步阶段,因此我对这里提到的某些事情的理解可能还很遥远.请告诉我是否要学习!
编辑1:
我认为让DatabaseConfigGenerator将文件写入NOT会更有意义:
app/src/main/res/raw
但在
app/build/res/all/<variant_name>/raw
由于AFAIK,这是将最终资源打包到.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 STILL包含一个较早版本的ormlite_config.txt,它不是我对模型类所做的更改的最新信息(例如,定义新的@DatabaseField)!
我的预感是:
>我将ormlite_config.txt写入错误的位置(
会很奇怪,因为它在一秒钟后被拾取到.apk中
构建),或
>提取app / build / res / all /< variant_name> / raw的内容
在执行< variant_name> Java之前
如果是以后的版本,我不知道该如何处理…任何建议,不胜感激!
编辑2:
看来2.确实是这样.我并排打开了两个目录(app / build / apk和app / build / res / all /< variant_name> / raw),事件的顺序是:
>最新的ormlite_config.txt在app / build / res / all /< variant_name> / raw中生成
> .apk在app / build / apk内部创建
>提取.apk,并在res / raw下查看后,一个内部版本中过时的ormlite_config.txt位于内部
如果有人熟悉Gradle .apk生成过程的内部流程,可以告诉我我在这里缺少的内容,我将非常感谢!
编辑3
我还没有放弃!经过更多研究,我找到了diagram of the android gradle build system workflow.
根据该图,资源(在/ res下)被合并和收集,并且R类在编译Java代码之前被更新.这是有道理的,因为如果类引用R中未包含的资源值,则编译将失败.
现在,我确定与我的情况相关的三个步骤的执行顺序为:
>资源被合并和组装,R.java被更新
> Java被编译成类
> .apk放在一起
现在,如果我的ormlite_config.txt是在Java编译后重新生成的(如我在EDIT 2中所包含的build.gradle片段中所定义的那样),但最后却不是生成的.apk的一部分(文件而是),即使我将其放在步骤3之前的/ build / res / all /< variant name> / raw下,也只能表示要包含在.apk中的实际资源文件已移至其他位置.在步骤1.和2之间比buid / res / all /< variant name>
现在我只需要弄清楚那里是什么,所以我可以将新生成的ormlite_config.txt放在任何可能或可行的地方…
和以前一样,如果有人能启发我解决这个问题,我将非常感激.
替代(简化)方法
正如问题的最开头所述,最终,我决定采用一种更简单的方法,而不是像我最初打算的那样对.apk汇编过程进行修改.
步骤如下:
>对数据库配置生成器类进行编码,以将ormlite_config.txt写入应用程序的src / main / res / raw中(您可以将我在本问题的开头部分包含的DatabaseConfigGenerator类用作模板).使此类与应用程序的包结构保持一致,不要将其制成单独的应用程序或模块,没有理由这样做.因此,您可以将其放入com.your.app.database或任何其他内容.
>在Android Studio的顶部工具栏中,单击“制作”和“运行”按钮之间的小下拉框:
>它将打开一个菜单,您可以在其中选择现有的运行配置之一或编辑配置.选择以后:
>将打开“运行/调试配置”窗口,在该窗口的左上角,应单击绿色的小加号,然后选择“应用程序”作为新配置的类型:
>将打开一个表单,您将在其中为DatabaseConfigGenerator定义运行配置,因为它需要作为Java应用程序与Android应用程序分开运行.您只需在此处修改几个字段.首先,给新的运行配置起一个名称(1),然后选择DatabaseConfigGenerator作为主类(2),然后在模块类路径下选择您的应用程序所在的模块,其中您的DatabaseConfigGenerator和模型类位于其中(3),然后删除所有通过选择“启动前”部分中的条目并单击红色减号(4),最后单击“应用”(5).现在,您可以单击左侧“ Android应用程序”部分下的应用程序(6).
>您在这里要做的最后一件事也是最重要的一件事,它将把所有内容放在一起,以使您的应用程序首先自行构建,然后生成最新的ormlite_config.txt,然后再次自行构建(尽管很多速度比第一次快),因此该新生成的配置实际上包含在最终的.apk中.为此,您需要修改应用程序运行配置(1)的“启动前”部分.您可能已经在这里有了一个“ Gradle-aware Make”,这实际上是在通常的构建过程中编译您的应用程序并将其打包为.apk的过程.如果没有,请将其添加为第一项.之后,添加另一个条目,但是这次是“数据库配置生成器”运行配置,您已经向后创建了几步,因为这将确保基于最新编译的模型类生成ormlite_config.txt并且是最新的. -日期.最后,添加另一个“ Gradle-aware Make”,以确保生成一个新的.apk,该文件现在还将包括此最新的ormlite_config.txt.现在单击“应用”(2),就是这样!
>从这一点开始,每当您单击Android Studio窗口顶部工具栏中的“运行”按钮时,在选择“应用程序”运行配置后,您可以确保结果中的ormlite_config.txt.对于您对模型类或DatabaseConfigGenerator本身所做的任何更改,apk都是最新的.
对于此解决方案,我从以下两个SO答案中得到了启发:
> Setup Gradle to run Java executable in Android Studio
> Android Studio run configuration for ORMLite config generation
最后,我决定将完整的解决方案放在一个地方,并在此处进行详细描述.
对于此方法,有三个小警告,关于是否可以使用它们,YMMV也需要注意以下三点:
>这仅适用于“运行”操作,而不适用于“制造”操作,这意味着即使您只想构建.apk而不实际运行它,也必须启动运行.然后,可以在app / build / apk /下找到生成的.apk,并根据要构建的变体进行命名(对于调试版本,通常将其命名为app-debug-unaligned.apk;对于发行版,则命名为app-release.apk ).
>这种方法本身意味着,每当您单击“运行”时,“ Gradle-aware make”将运行两次,这将导致构建时间略长,但是我并没有注意到很大的不同(android gradle插件很聪明足以识别自上次构建以来哪些资源没有发生变化,并且第二次将跳过很多不必要的步骤),从而可能使构建时间延长20%(请不要拘泥于数量).
>如果您在团队环境中工作并且正在使用版本控制,这有点让人无法识别此配置,因此团队中的每个开发人员都必须单独进行此过程,而不能简单地将其检出为仓库中的一部分,例如.git.这是由于以下事实:运行配置是在项目根目录下的.idea / workspace.xml下定义的,由于它是特定于计算机的,因此在版本控制中普遍认为这是不应跟踪的.
可能有一些方法可以在团队级别上删除定义运行配置的过程中的一些手动步骤,但是似乎不可能以一种干净的方式完全自动化它.我可能是错的,如果是这种情况,请随时告诉我.
希望这可以帮助!
解决方法:
问题在于,在生成R.java时(并且在javac之前),资源是由aapt编译的.然后,将这些编译后的资源用于构建apk. AFAIK那些已编译的资源只是一种zip存档.
因此,要实现您的目标,您需要在javac编译后修改那些已编译的资源.
您可以尝试定义一个新任务(将其称为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
或这个
aapt add <the_path_to_compiled_resources> ormlite_config.txt
在新的自定义任务中,addOrmToRes应该可以解决问题.因此,回答您的编辑3:aapt add可能是解决方案.
要将其集成到构建中,类似这样的方法应该起作用:
runOrmGenTask.dependsOn(variant.javaCompile)
addOrmToRes.dependsOn(runOrmGenTask)
addOrmToRes.dependsOn(<the task creating the apk>)
<the task signing the apk>.dependsOn(addOrmToRes)
请注意,我没有在此处识别所有任务.但是我想你明白了.
标签:android-studio,gradle,build,ormlite,android 来源: https://codeday.me/bug/20191121/2052977.html