Tabla de contenido
prefacio
Todo el artículo tinker
se TinkerResourceIdTask
amplía en torno a los puntos de conocimiento en .
aapt
aapt2
La diferencia entre y (entorno de ejecución y resultados de ejecución);id
fijación de recursos ;PUBLIC
marcar para ;
aapt
El entorno operativo es gradle:2.2.0
ygradle-wrapper:3.4.1
aapt2
El entorno operativo es gradle:3.3.2
ygradle-wrapper:5.6.2
El proyecto android-aapt-sample es mi propia muestra experimental. aapt
Hay aapt2
dos ramas, correspondientes a sus implementaciones.
Resumen de AAPT
Android Studio 3.0
A partir de , el compilador que se compila comogoogle
recurso está habilitado de forma predeterminada y su aparición brinda soporte para la compilación incremental de recursos. Por supuesto, habrá algunos problemas durante el uso, podemos cerrarlo configurando android.enableAapt2=false en gradle.properties .aapt2
aapt2
aapt2
recurso
Android
Nacido para hacer mucho trabajo para ser compatible con una variedad de dispositivos diferentes, como tamaño de pantalla, internacionalización, teclado, densidad de píxeles, etc., podemos usar recursos específicos para varios escenarios específicos para hacer compatibilidad en lugar de cambiar una línea de código, asumiendo que hemos adaptado diferentes recursos para varios escenarios, ¿cómo podemos aplicar estos recursos rápidamente? EstaAndroid
clase se nos proporciona , y se especifica un índice de recursos ( ), y luego solo necesitamos decirle al sistema que use el recurso correspondiente en diferentes escenarios comerciales. En cuanto a qué archivo específico en el recurso especificado, está determinado por The El sistema se determina de acuerdo con la configuración del desarrollador.R
id
En este escenario, suponiendo que damos un valor id
de x
, entonces cuando el negocio actual necesita usar este recurso, el estado del teléfono móvil es y
el valor Con ( x,y
), la ruta específica del archivo de recursos se puede ubicar rápidamente en un levantar la mesa Esta tabla es resources.arsc
, se aapt
compila .
De hecho, los recursos binarios (como las imágenes) no necesitan compilarse, pero este comportamiento de "compilación" es para generar resources.arsc
y binarizar xml
.resources.arsc
archivos Cuando llamemos a related , encontraremos el archivo correspondiente en esta tabla y lo leeremos.xml
AssetManager
R
id
Gradle
En el proceso de compilación de recursos, estos comandos aapt2 se llaman y los parámetros pasados también se presentan en este documento, pero los detalles de la llamada están ocultos para los desarrolladores.
aapt2
Se divide principalmente en dos pasos, uno se llama compile
y el otro se llama link
.
Cree un proyecto vacío: solo escriba dos xml
, a saber AndroidManifest.xml
y activity_main.xml
.
Compilar
mkdir compiled
aapt2 compile src/main/res/layout/activity_main.xml -o compiled/
En compiled
la carpeta, layout_activity_main.xml.flat
se genera este archivo, es aapt2
específico de , aapt
no hay ( aapt
el archivo de origen se copia) y aapt2
se puede usar para la compilación incremental. Si tenemos muchos archivos, necesitamos llamar compile
en De hecho –dir
, el parámetro también se puede usar aquí, pero este parámetro no tiene efecto de compilación incremental. Es decir, al pasar un directorio completo, AAPT2
todos los archivos del directorio se volverán a compilar incluso si solo ha cambiado un recurso.
Enlace
link
La carga de trabajo compile
es un , la entrada aquí es la suma de múltiples flat
archivos y AndroidManifest.xml
recursos externos, y la salida es la suma de solo apk
recursos R.java
. El comando es el siguiente:
aapt2 link -o out.apk \
-I $ANDROID_HOME/platforms/android-28/android.jar \
compiled/layout_activity_main.xml.flat \
--java src/main/java \
--manifest src/main/AndroidManifest.xml
- La segunda línea
-I
sonimport
los recursos externos, aquí hay principalmente algunos atributos definidos bajoandroid
el espacio de nombres , que generalmente usamos@android:xxx
se colocanjar
en este, de hecho, también podemos proporcionar nuestros propios recursos para que otros los vinculen; - La tercera línea es el
flat
archivo , si hay más de uno, puede unirlos más tarde; - La cuarta línea es el directorio
R.java
generado ; - La quinta línea es para especificar
AndroidManifest.xml
;
Link
Cuando se completa, se genera un archivo y out.apk
se incluye en formato . Solo los archivos de recursos pueden usar el sufijo .R.java
out.apk
resources.arsc
.ap_
Ver recursos compilados
Además de usar Android Studio
para ver resources.arsc
, también puede usar directamente aapt2 dump apk
la información para ver ID
el estado y el estado de los recursos:
aapt2 dump out.apk
Los resultados de salida son los siguientes:
Binary APK
Package name=com.geminiwen.hello id=7f
type layout id=01 entryCount=1
resource 0x7f010000 layout/activity_main
() (file) res/layout/activity_main.xml type=XML
Se puede ver layout/activity_main
que el correspondiente ID
es 0x7f010000
.
El intercambio de recursos
android.jar
Es solo un stub para compilar, y cuando se ejecuta realmente, Android OS
proporciona una biblioteca de tiempo de ejecución ( framework.jar
). android.jar
Muy parecido a one apk
, excepto que existe como class
un archivo , y luego un AndroidManifest.xml
y resources.arsc
. Esto quiere decir que también podemos usarlo aapt2 dump
, ejecuta el siguiente comando:
aapt2 dump $ANDROID_HOME/platforms/android-28/android.jar > test.out
Obtiene una gran cantidad de salida similar a la siguiente:
resource 0x010a0000 anim/fade_in PUBLIC
() (file) res/anim/fade_in.xml type=XML
resource 0x010a0001 anim/fade_out PUBLIC
() (file) res/anim/fade_out.xml type=XML
resource 0x010a0002 anim/slide_in_left PUBLIC
() (file) res/anim/slide_in_left.xml type=XML
resource 0x010a0003 anim/slide_out_right PUBLIC
() (file) res/anim/slide_out_right.xml type=XML
Tiene algunos PUBLIC
campos más. Si el recurso en un apk
archivo está marcado con esta etiqueta, puede ser apk
referenciado por otros. El método de referencia es @包名:类型/名字
, por ejemplo: @android:color/red
.
Si queremos proporcionar nuestros recursos, primero marque nuestros recursos PUBLIC
con y luego xml
cite el nombre de su paquete en , por ejemplo: @com.gemini.app:color/red
puede referirse a su color/red
definido , si no especifica un nombre de paquete, el valor predeterminado es usted mismo.
En cuanto a AAPT2
cómo generarlo PUBLIC
, los que estén interesados pueden seguir leyendo este artículo.
descripción general de ids.xml
ids.xml
: proporciona un recurso único para los recursos relacionados de la aplicación id
. id
es obtener xml
los parámetros requeridos por el objeto en, es decir, Object = findViewById(R.id.id_name);
en id_name
.
android.R.id
Estos valores se pueden referenciar en el código .
Si IDids.xml
se define en , se puede definir de la siguiente manera en , de lo contrario .layout
@id/price_edit
@+id/price_edit
ventaja
- La asignación de nombres es conveniente, podemos nombrar algunos controles específicos primero y citarlos directamente al usarlos
id
, lo que ahorra un proceso de asignación de nombres. - Optimice la eficiencia de la compilación:
id
Después de agregarlo, seR.java
generará en;- Con
ids.xml
la administración unificada, la compilación única se puede usar varias veces.
Sin embargo"@+id/btn_next"
, el formulario utilizado se volverá a detectar cada vez que se guarde el archivo(Ctrl+s
) . Si existe, no se generará. Si no existe, necesita para ser agregado Por lo tanto, se reduce la eficiencia de compilación.后R.java
id
id
ids.xml
contenido del documento:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="forecast_list" type="id"/>
<!-- <item name="app_name" type="string" />-->
</resources>
Algunas personas pueden tener curiosidad de que hay una línea de código comentada arriba. Si abre el comentario, encontrará que la compilación informará un error:
Execution failed for task ':app:mergeDebugResources'.
> [string/app_name] /Users/tanzx/AndroidStudioProjects/AaptDemo/app/src/main/res/values/strings.xml [string/app_name] /Users/tanzx/AndroidStudioProjects/AaptDemo/app/src/main/res/values/ids.xml: Error: Duplicate resources
Debido a que app_name
el recurso para ha value
sido declarado en .
descripción general de public.xml
Instrucciones oficiales relacionadas Sitio web oficial: Seleccione los recursos que se harán públicos .
Traducción original: Todos los recursos de la biblioteca son públicos por defecto. Para hacer que todos los recursos sean implícitamente privados, debe definir al menos una propiedad específica como pública. Los recursos incluyen todos los archivos, como imágenes, en
res/
el directorio . Para evitar que los usuarios de su biblioteca accedan a recursos exclusivamente internos, debe utilizar este mecanismo de identificación privada automática declarando uno o más recursos públicos. Alternativamente, puede hacer que todos los recursos sean privados agregando una<public />
etiqueta , que no hace que ningún recurso sea público, pero hace que todo (todos los recursos) sea privado.Al hacer que la propiedad sea implícitamente privada, no solo puede evitar que los usuarios de su biblioteca obtengan sugerencias de finalización de código de los recursos internos de la biblioteca, sino que también puede cambiar el nombre o eliminar recursos privados sin romper los clientes de su biblioteca. El sistema filtra los recursos privados de la finalización del código y Lint te avisará si intentas hacer referencia a recursos privados.
Al crear la biblioteca, el complemento Gradle de Android toma la definición de recurso público y la extrae en
public.txt
un archivo , que luego se empaqueta en un archivo AAR.
El resultado real de la prueba es solo que el código está automáticamente incompleto si no regresa, y el compilador informa rojo. Si está marcado lint
, ¡no hay advertencias para la compilación~!
Ahora, la mayoría de las explicaciones son: el archivo RES/value/public.xml se usa para ID
asignar a Android
los recursos.
stackoverfloew:¿Cuál es el uso del archivo res/values/public.xml en Android?
public.xml
contenido del documento:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public name="forecast_list" id="0x7f040001" type="id" />
<public name="app_name" id="0x7f070002" type="string" />
<public name="string3" id="0x7f070003" type="string" />
</resources>
ID de recurso fijo
La identificación del recurso fijo es extremadamente importante en la revisión y el complemento. En hotfix, al construir patch
, es necesario mantener patch
los recursos del paquete consistentes id
con los recursos del paquete de referencia id
; en el complemento, si el complemento necesita hacer referencia a los recursos del host, los recursos del host necesitan ser id
arreglados Por lo tanto, los recursos id
son fijos en estos dos Este escenario es particularmente importante.
En Android Gradle Plugin 3.0.0
, está habilitado de forma predeterminada aapt2
, y el método de reparación de recursos original de aapt public.xml
también será inválido. Es necesario encontrar un nuevo método de reparación de recursos en lugar de simplemente deshabilitar aapt
2. Por lo tanto, este artículo analiza aapt和aapt2
cómo reparar los recursos respectivamente id
.
aapt
Fijo id
_
Configuración del entorno del proyecto (PD: se quejan de que aapt ha sido reemplazado por aapt2, casi no hay información relacionada con aapt, ¡y el entorno es demasiado laborioso para construir ~!)
com.android.tools.build:gradle:2.2.0
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
compileSdkVersion 24
buildToolsVersion '24.0.0'
Primero genere el archivo correspondiente value
debajo del archivo de acuerdo con el contenido de la ids.xml
suma anterior y el nombre del archivo.public.xml
Compilar el resultado directamente
Al compilar directamente R文件
el contenido, podemos ver que los recursos que queremos configurar id
no se generan como esperábamos.
Copie
public.xml
el archivo enbuild/intermediates/res/merged
el directorio correspondiente
afterEvaluate {
for (variant in android.applicationVariants) {
def scope = variant.getVariantData().getScope()
String mergeTaskName = scope.getMergeResourcesTask().name
def mergeTask = tasks.getByName(mergeTaskName)
mergeTask.doLast {
copy {
int i=0
from(android.sourceSets.main.res.srcDirs) {
include 'values/public.xml'
rename 'public.xml', (i++ == 0? "public.xml": "public_${i}.xml")
}
into(mergeTask.outputDir)
}
}
}
}
Esta vez podemos ver directamente que los recursos id
se generan de acuerdo a nuestras necesidades.
¿Por qué es esto?
-
android gradle
Las siguientes versiones del complemento1.3
se puedenpublic.xml
colocar directamente en elres
directorio del código fuente para participar en la compilación; -
android gradle
Las versiones de complementos se ignoran al1.3+
realizar tareas , por lo que no hay contenido relevante en el directorio debajo del directorio completado . Por lo tanto, debe insertarse en el directorio bajo el directorio completo a través del script en tiempo de compilación . La razón por la que esto es factible es que es compatible por sí mismo , pero el complemento filtra los recursos cuando los preprocesa .mergeResource
public.xml
merge
build
res
public.xml
public.xml
merge
build
res
aapt
public.xml
gradle
(merge)
public.xml
aapt2
Fijo id
_
Después de aapt2
compilar (compilar los archivos de recursos en formato binario), merge
los recursos encontrados se compilaron previamente y flat
se generaron los archivos. En este momento, public.xml
copiar los archivos a este directorio provocará errores de compilación.
Pero en la fase de vinculaciónaapt2
de , observamos las opciones de vinculación relevantes :
opciones | ilustrar |
---|---|
--emit-ids path |
Genera un archivo en la ruta dada que contiene una lista de nombres de tipos de recursos y sus asignaciones de ID. Funciona bien --stable-ids con . |
--stable-ids outputfilename.ext |
Utilice un archivo --emit-ids generado que contenga los nombres de los tipos de recursos y una lista de ID asignados a ellos. Esta opción mantiene estables los ID asignados, incluso si elimina recursos o agrega otros nuevos durante la vinculación. |
El descubrimiento --emit-ids
y --stable-ids
la colocación de comandos pueden lograr id
la solución.
android {
aaptOptions {
File publicTxtFile = project.rootProject.file('public.txt')
//public文件存在,则应用,不存在则生成
if (publicTxtFile.exists()) {
project.logger.error "${publicTxtFile} exists, apply it."
//aapt2添加--stable-ids参数应用
aaptOptions.additionalParameters("--stable-ids", "${publicTxtFile}")
} else {
project.logger.error "${publicTxtFile} not exists, generate it."
//aapt2添加--emit-ids参数生成
aaptOptions.additionalParameters("--emit-ids", "${publicTxtFile}")
}
}
}
- Compile por primera vez, primero
--emit-ids
generando en el directorio raíz del proyectopublic.txt
; - Luego cambie
public.txt
el interior correspondienteid
a lo que desea arreglarid
; - Vuelva a compilar y corrija los recursos por
--stable-ids
y bajo el directorio raíz ;public.txt
id
--emit-ids
compilar resultado
Modifique
public.txt
el contenido del archivo y vuelva a compilar
R.txt · público.txt
El producto intermedio que normalmente empaquetamos y generamos es que build/intermediates/symbols/debug/R.txt
necesitamos convertirlo a public.txt
.
R.txt
Formato (int
type
name
id
) o (int[]
styleable
name
{id,id,xxxx}
)
public.txt
formato (applicationId:type/name = id
)
R.txt
Por lo tanto, el tipo en el archivo debe filtrarse durante el proceso de conversión styleable
.
android {
aaptOptions {
File rFile = project.rootProject.file('R.txt')
List<String> sortedLines = new ArrayList<>()
// 一行一行读取
rFile.eachLine {
line ->
//rLines.add(line)
String[] test = line.split(" ")
String type = test[1]
String name = test[2]
String idValue = test[3]
if ("styleable" != type) {
sortedLines.add("${applicationId}:${type}/${name} = ${idValue}")
}
}
Collections.sort(sortedLines)
File publicTxtFile = project.rootProject.file('public.txt')
if (!publicTxtFile.exists()) {
publicTxtFile.createNewFile()
sortedLines?.each {
publicTxtFile.append("${it}\n")
}
}
}
}
Marca PÚBLICA
En AAPT概述
esta parte dijimos que si los recursos en un apk
archivo están PUBLIC
marcados, pueden ser referenciados apk
por @包名:类型/名字
, por ejemplo: @android:color/red
.
Después de leer las dos partes anteriores de " aapt
Reparación id
en curso" a " aapt2
Reparación en curso ", sabemos que los métodos de reparación y reparación son diferentes.id
aapt
aapt2
id
De hecho, si usamos aapt2 dump build/intermediates/res/resources-debug.ap_
comandos para ver información relevante sobre los recursos generados.
aapt
La información del recurso se fija haciendo una public.xml
etiqueta con:id
PUBLIC
aapt2
2. El uso del método de fijación anterior no está marcado id
en la figura a continuación .PUBLIC
La razón todavía es causada por la diferencia entre aapt
y , y no es igual a , si desea agregar una marca en , todavía tiene que encontrar otra forma.aapt2
aapt2
public.txt
aapt
public.xml
aapt2
PUBLIC
Mirando hacia atrás
revisar
aapt
Paraid
fijación yPUBLIC
fijación de precios,public.xml
copie a${mergeResourceTask.outputDir}
;aapt2
Por el contrarioaapt
, se realiza la optimización de la compilación incremental.AAPT2
analizará el archivo y generará un archivo binario.flat
intermedio .
pensar
¿Puedo usarme aapt2
para public.xml
compilar como public.arsc.flat
, y copiarlo como aapt
un ${mergeResourceTask.outputDir}
;
Manos
android {
//将public.txt转化为public.xml,并对public.xml进行aapt2的编译将结果复制到${ergeResourceTask.outputDir}
//下面大部分代码是copy自tinker的源码
applicationVariants.all {
def variant ->
def mergeResourceTask = project.tasks.findByName("merge${variant.getName().capitalize()}Resources")
if (mergeResourceTask) {
mergeResourceTask.doLast {
//目标转换文件,注意public.xml上级目录必须带values目录,否则aapt2执行时会报非法文件路径
File publicXmlFile = new File(project.buildDir, "intermediates/res/public/${variant.getDirName()}/values/public.xml")
//转换public.txt文件为publicXml文件,最后一个参数true标识固定资源id
convertPublicTxtToPublicXml(project.rootProject.file('public.txt'), publicXmlFile, false)
def variantData = variant.getMetaClass().getProperty(variant, 'variantData')
def variantScope = variantData.getScope()
def globalScope = variantScope.getGlobalScope()
def androidBuilder = globalScope.getAndroidBuilder()
def targetInfo = androidBuilder.getTargetInfo()
def mBuildToolInfo = targetInfo.getBuildTools()
Map<BuildToolInfo.PathId, String> mPaths = mBuildToolInfo.getMetaClass().getProperty(mBuildToolInfo, "mPaths") as Map<BuildToolInfo.PathId, String>
//通过aapt2 compile命令自己生成public.arsc.flat并输出到${mergeResourceTask.outputDir}
project.exec(new Action<ExecSpec>() {
@Override
void execute(ExecSpec execSpec) {
execSpec.executable "${mPaths.get(BuildToolInfo.PathId.AAPT2)}"
execSpec.args("compile")
execSpec.args("--legacy")
execSpec.args("-o")
execSpec.args("${mergeResourceTask.outputDir}")
execSpec.args("${publicXmlFile}")
}
})
}
}
}
}
Convierte public.txt
un archivo en public.xml
un archivo.
public.txt
Haystyleable
un recurso de tipo en ,public.xml
pero no existe en , por lo que si se encuentra un tipo durante la conversiónstyleable
, debe ignorarse;vector
Si hay recursos internos para recursos de gráficos vectoriales, también deben ignorarse. Enaapt2
, su nombre$
comienza con , seguido del nombre del recurso principal, seguido del índice de incremento de número __. Estos recursos no se pueden referenciar externamente y solo necesitan para ser arregladoid
No hay necesidad de agregarPUBLIC
la etiqueta, y$
el símbolopublic.xml
es ilegal en , así que simplemente ignórelo;- Dado que
aapt2
hay una forma fija de recursosid
, se puede perder directamente durante el proceso de conversiónid
, y una declaración simple es suficiente (PD: aquí,withId
si necesita ser reparado o no, se controla mediante parámetrosid
);aapt2
El directorio principal del archivo compiladopublic.xml
debe servalues
una carpeta; de lo contrario, se informará una ruta ilegal durante el proceso de compilación;
/**
* 转换publicTxt为publicXml
* copy tinker:com.tencent.tinker.build.gradle.task.TinkerResourceIdTask#convertPublicTxtToPublicXml
*/
@SuppressWarnings("GrMethodMayBeStatic")
void convertPublicTxtToPublicXml(File publicTxtFile, File publicXmlFile, boolean withId) {
if (publicTxtFile == null || publicXmlFile == null || !publicTxtFile.exists() || !publicTxtFile.isFile()) {
throw new GradleException("publicTxtFile ${publicTxtFile} is not exist or not a file")
}
GFileUtils.deleteQuietly(publicXmlFile)
GFileUtils.mkdirs(publicXmlFile.getParentFile())
GFileUtils.touch(publicXmlFile)
project.logger.info "convert publicTxtFile ${publicTxtFile} to publicXmlFile ${publicXmlFile}"
publicXmlFile.append("<!-- AUTO-GENERATED FILE. DO NOT MODIFY -->")
publicXmlFile.append("\n")
publicXmlFile.append("<resources>")
publicXmlFile.append("\n")
Pattern linePattern = Pattern.compile(".*?:(.*?)/(.*?)\\s+=\\s+(.*?)")
publicTxtFile.eachLine {
def line ->
Matcher matcher = linePattern.matcher(line)
if (matcher.matches() && matcher.groupCount() == 3) {
String resType = matcher.group(1)
String resName = matcher.group(2)
if (resName.startsWith('$')) {
project.logger.info "ignore to public res ${resName} because it's a nested resource"
} else if (resType.equalsIgnoreCase("styleable")) {
project.logger.info "ignore to public res ${resName} because it's a styleable resource"
} else {
if (withId) {
publicXmlFile.append("\t<public type=\"${resType}\" name=\"${resName}\" id=\"${matcher.group(3)}\" />\n")
} else {
publicXmlFile.append("\t<public type=\"${resType}\" name=\"${resName}\" />\n")
}
}
}
}
publicXmlFile.append("</resources>")
}
A través del proceso anterior de pensamiento y práctica, no solo resolvimos el problema de aapt2
marcar PUBLIC
, sino que también encontramos un nuevo método de fijación aapt2
.id
Errores que se pueden encontrar:
no signature of method com.android.build.gradle.internal.variant.applicationvariantdata.getscope() is applicable for argument types: () values: []
La solución es modificar gradle
la versión a gradle:3.3.2
y gradle-wrapper:5.6.2
, después de todo, tinker
no es compatible con la última versión de gradle
.
referencia:
La identificación del recurso para la adaptación aapt2 es fija
El artículo se cuenta aquí, si tiene otras necesidades para comunicarse, ¡puede dejar un mensaje ~! ~!
Si quieres leer más artículos del autor, puedes consultar mi blog personal y cuenta pública: