目次
序文
記事全体はtinker
、TinkerResourceIdTask
のナレッジ ポイントを中心に展開されています。
aapt
と (実行環境と実行結果)aapt2
の違い。- リソース
id
の固定; PUBLIC
をマークします。
aapt
動作環境はgradle:2.2.0
、gradle-wrapper:3.4.1
aapt2
動作環境はgradle:3.3.2
、gradle-wrapper:5.6.2
android-aapt-sampleプロジェクトは、私自身の実験的なサンプルです。実装に対応する 2 つのブランチがありaapt
ます。aapt2
AAPT の概要
Android Studio 3.0
から、google
リソースaapt2
としてaapt2
、その出現により、リソースのインクリメンタル コンパイルがサポートされます。もちろん、使用中にいくつかの問題が発生します。gradle.propertiesでandroid.enableAapt2=false を構成することで閉じることができますaapt2
。
リソース
Android
画面サイズ、国際化、キーボード、ピクセル密度など、さまざまなデバイスと互換性を持たせるために多くの作業を行うために生まれました。さまざまなシナリオにさまざまなリソースを適応させたと仮定すると、これらのリソースをすばやく適用するにはどうすればよいでしょうか? このAndroid
クラスは私たちのために提供され、リソース インデックス ( ) が指定されます。次に、対応するリソースをさまざまなビジネス シナリオで使用するようにシステムに指示するだけで済みます。指定されたリソース内のどの特定のファイルについては、によって決定されます。システムは、開発者の構成に応じて決定されます。R
id
このシナリオでは、 の値id
をx
、現在のビジネスでこのリソースを使用する必要がある場合、携帯電話のステータスはy
の値 ( x,y
) を使用すると、リソース ファイルの特定のパスをすばやく見つけることができます。テーブルアップ。この表はresources.arsc
、からaapt
まとめた。
実際には、バイナリ リソース (画像など) をコンパイルする必要はありませんが、この「コンパイル」の動作はresources.arsc
、ファイルを生成してバイナリ化することでありxml
、relatedを呼び出すと、このテーブルで対応するファイルを見つけて読み取ります。resources.arsc
xml
AssetManager
R
id
Gradle
リソースをコンパイルする過程で、これらのaapt2 コマンドが呼び出され、渡されるパラメーターもこのドキュメントで紹介されていますが、呼び出しの詳細は開発者には隠されています。
aapt2
主に2段階に分かれており、1つは と呼ばれcompile
、もう1つは と呼ばれますlink
。
空のプロジェクトを作成します。とxml
の2 つだけを記述します。AndroidManifest.xml
activity_main.xml
コンパイル
mkdir compiled
aapt2 compile src/main/res/layout/activity_main.xml -o compiled/
compiled
フォルダ内に、layout_activity_main.xml.flat
このファイルが生成され、aapt2
固有、aapt
存在せず (aapt
ソース ファイルがコピーされます)、aapt2
インクリメンタル コンパイルに使用できます。ファイルがたくさんある場合は、順番compile
に を. 実際–dir
、ここでも パラメーターを使用できますが、このパラメーターはインクリメンタル コンパイルの効果はありません。つまり、ディレクトリ全体を渡すと、AAPT2
リソースが 1 つだけ変更された場合でも、ディレクトリ内のすべてのファイルが再コンパイルされます。
リンク
link
ワークロードcompile
は、ここでの入力は複数のflat
ファイルとAndroidManifest.xml
外部リソースの合計であり、出力はapk
リソースR.java
。コマンドは次のとおりです。
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
- 2 行目
-I
はimport
外部リソースです。主にandroid
名前空間。通常使用するものはこの中に@android:xxx
配置されますjar
。実際、他の人がリンクできるように独自のリソースを提供することもできます。 - 3 行目は入力
flat
ファイル。複数ある場合は、後でスプライスできます。 - 4 行目は
R.java
生成された。 - 5行目は指定することです
AndroidManifest.xml
;
Link
完了すると、ファイルが生成され、にout.apk
含まれます。接尾辞 を使用できるのは、リソース ファイルだけです。R.java
out.apk
resources.arsc
.ap_
コンパイルされたリソースを表示する
Android Studio
を使用して表示するだけでなくresources.arsc
、aapt2 dump apk
情報をID
。
aapt2 dump out.apk
出力結果は次のとおりです。
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
layout/activity_main
対応してID
いるわかります0x7f010000
。
リソース共有
android.jar
あくまでコンパイル用のスタブであり、実際に実行するとAndroid OS
ランタイムライブラリ(framework.jar
)を提供します。android.jar
とよく似ていますapk
が、class
ファイル、その後AndroidManifest.xml
にresources.arsc
. aapt2 dump
これは、次のコマンドを実行して、それを使用することもできることを意味します。
aapt2 dump $ANDROID_HOME/platforms/android-28/android.jar > test.out
次のような多くの出力が得られます。
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
いくつかのPUBLIC
フィールドがあります.apk
ファイル,apk
他のリソースから参照できます. 参照方法は です@包名:类型/名字
.例: @android:color/red
.
リソースを提供したい場合は、まずリソースPUBLIC
を で、xml
パッケージ名を で引用します。たとえば、定義済みの@com.gemini.app:color/red
を参照できます。パッケージ名を指定しない場合、デフォルトは自分自身です。color/red
AAPT2
生成方法についてはPUBLIC
、興味のある方は引き続きこの記事をお読みください。
ids.xml の概要
ids.xml
: アプリケーションの関連リソースに固有のリソースを提供しますid
。のオブジェクトが必要とするパラメータをid
取得することです。xml
Object = findViewById(R.id.id_name);
id_name
これらの値はコードでandroid.R.id
参照できます。IDが で定義されている
場合は で、そうでない場合は で次のように定義できます。ids.xml
layout
@id/price_edit
@+id/price_edit
アドバンテージ
- 名前付けは便利です。最初に特定のコントロールに名前を付け、それらを使用するときにそれらを直接引用できます
id
。これにより、名前付けプロセスが節約されます。 - コンパイル効率を最適化します。
- 追加する
id
と、次のようR.java
に生成されます。 - 一元管理することで
ids.xml
、1回の編集で複数回利用することができます.
ただし"@+id/btn_next"
、ファイルを保存するたびに利用されているフォームが再検出されます.存在する場合は生成されません.存在しない場合は生成が必要です(Ctrl+s
.追加予定。したがって、コンパイル効率が低下します。后R.java
id
id
- 追加する
ids.xml
ドキュメントの内容:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="forecast_list" type="id"/>
<!-- <item name="app_name" type="string" />-->
</resources>
上記のコード行にコメントがあることに興味がある人もいるかもしれません. コメントを開くと、コンパイルでエラーが報告されることがわかります:
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
app_name
のリソースが で宣言されているためですvalue
。
public.xml の概要
公式関連の指示公式ウェブサイト: 公開するリソースを選択します。
元の翻訳: ライブラリ内のすべてのリソースは、デフォルトで公開されています。すべてのリソースを暗黙的にプライベートにするには、少なくとも 1 つの特定のプロパティをパブリックとして定義する必要があります。リソースには、プロジェクトの
res/
ディレクトリ。ライブラリのユーザーが内部のみのリソースにアクセスできないようにするには、1 つ以上のパブリック リソースを宣言して、この自動プライベート識別メカニズムを使用する必要があります。または、空の<public />
タグを。これにより、リソースは公開されず、すべて (すべてのリソース) が非公開になります。プロパティを暗黙的に非公開にすることで、ライブラリのユーザーが内部ライブラリ リソースからコード補完候補を取得できないようにするだけでなく、ライブラリのクライアントを壊すことなく、非公開リソースの名前を変更または削除することもできます。システムはプライベート リソースをコード補完から除外します。プライベート リソースを参照しようとすると、Lint によって警告が表示されます。
ライブラリをビルドするとき、Android Gradle プラグインはパブリック リソース定義を取得して
public.txt
ファイル、それを AAR ファイルにパッケージ化します。
実際のテスト結果は、コードが返されない場合、コードが自動的に不完全であるということだけであり、コンパイラは赤を報告します。チェックを入れるとlint
、コンパイルの警告が出ません~!
現在、ほとんどの説明は次のとおりです。ファイルRES/value/public.xmlは、固定リソースをリソースにID
割り当てるAndroid
。
stackoverflow: Android での res/values/public.xml ファイルの使用は何ですか?
public.xml
ドキュメントの内容:
<?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
固定リソース ID は、ホットフィックスとプラグインで非常に重要です。ホットフィックスでは、ビルド時に、パッケージのリソースと参照パッケージのリソースの一貫性patch
を保つ必要があります。プラグインでは、プラグインがホストのリソースを参照する必要がある場合、ホストのリソースこのシナリオは特に重要です。patch
id
id
id
id
ではAndroid Gradle Plugin 3.0.0
、デフォルトで有効になっておりaapt2
、aapt 独自のリソース固定方法もpublic.xml
無効になるため、単純に無効にするのではなく、新しいリソース固定方法を見つける必要がありますaapt
2. そのため、この記事でaapt和aapt2
はそれぞれのリソース固定方法について説明しますid
.
aapt
固定id
_
プロジェクトの環境設定(PS: aapt が aapt2 に置き換わったこと、aapt 関連の情報がほとんどないこと、環境を構築するのが面倒すぎることを訴える ~!)
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'
まず、上記和value
の内容とファイル名に従い、ファイル配下に対応するファイルを生成します。ids.xml
public.xml
結果を直接コンパイルする
コンテンツを直接コンパイルするとR文件
、設定したいリソースがid
期待どおりに生成されていないことがわかります。
public.xml
ファイルをbuild/intermediates/res/merged
対応するディレクトリにコピーします
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)
}
}
}
}
id
今回は、必要に応じてリソースが生成されていることを直接確認できます。
どうしてこれなの?
-
android gradle
次のバージョンのプラグインは、ソース コード ディレクトリに1.3
直接配置して、コンパイルに参加できます。public.xml
res
-
android gradle
1.3+
タスクの実行時にプラグインのバージョンはmergeResource
無視されるpublic.xml
ため、merge
完了したディレクトリのbuild
下res
のディレクトリにpublic.xml
関連するコンテンツはありません。そのため、コンパイル時にスクリプトを介して、完成したディレクトリの下のディレクトリpublic.xml
に挿入する必要があります。これが実現可能である理由は、プラグイン自体がサポートされているためですが、プラグインはリソースの前処理時にリソースをフィルタリングします。merge
build
res
aapt
public.xml
gradle
(merge)
public.xml
aapt2
固定id
_
aapt2
コンパイル(リソースファイルをバイナリ形式にコンパイル)後、merge
見つかったリソースはプリコンパイルされ、flat
ファイルが生成されます.このとき、public.xml
ファイルをこのディレクトリにコピーすると、コンパイルエラーが発生します.
しかし、 のリンク フェーズではaapt2
、関連するリンク オプションを確認します。
オプション | 例証する |
---|---|
--emit-ids path |
リソースタイプ名とその ID マッピングのリストを含むファイルを指定されたパスに生成します。--stable-ids とうまく動作します。 |
--stable-ids outputfilename.ext |
リソース タイプの名前とそれらに割り当てられた ID のリストを含む、で--emit-ids 生成された。このオプションは、リンク中にリソースを削除したり、新しいリソースを追加したりしても、割り当てられた ID を安定させます。 |
検出--emit-ids
とコマンドのコロケーションにより、問題を--stable-ids
解決できます。id
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}")
}
}
}
- 最初に
--emit-ids
プロジェクトのルート ディレクトリに生成して、初めてコンパイルしますpublic.txt
。 - 次に、
public.txt
対応する内部をid
修正したいものに変更しますid
。 - 再度コンパイルし、
--stable-ids
ルート ディレクトリとその下のpublic.txt
リソースを修正しますid
。
--emit-ids
コンパイル結果
public.txt
ファイルの内容を変更して再度コンパイルする
R.txt转public.txt
通常、パッケージ化して生成する中間製品は、build/intermediates/symbols/debug/R.txt
それを に変換する必要がありますpublic.txt
。
R.txt
フォーマット (int
type
name
id
) または (int[]
styleable
name
{id,id,xxxx}
)
public.txt
フォーマット (applicationId:type/name = id
)
R.txt
したがって、変換プロセス中にファイル内のタイプを除外する必要があります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")
}
}
}
}
PUBLICマーク
この部分では、ファイルAAPT概述
内のリソースがマークされている場合、他のから述べました。apk
PUBLIC
apk
@包名:类型/名字
@android:color/red
上記の「aapt
修正id
中」から「aapt2
修正中」までのid
2つの部分を読んだ後、修正と修正の方法が異なることaapt
がわかりました。aapt2
id
実際、aapt2 dump build/intermediates/res/resources-debug.ap_
コマンドを使用して、生成されたリソースに関する関連情報を表示するとします。
aapt
リソース情報は、次のタグをpublic.xml
作成することで修正されます。id
PUBLIC
2. 上記のaapt2
固定方法を使用することは、id
下の図ではPUBLIC
マークされていません。
その理由は依然としてaapt
とaapt2
の違いによるものであり、aapt2
は とpublic.txt
等しくありませんaapt
。にマークを追加したいpublic.xml
場合は、別の方法を見つける必要があります。aapt2
PUBLIC
思い返す
レビュー
aapt
リソースのid
固定とPUBLIC
価格設定については、public.xml
にコピーしてください${mergeResourceTask.outputDir}
。aapt2
対照的にaapt
、増分コンパイルの最適化が行われます。AAPT2
ファイルを解析し、拡張子が . の.flat
中間。
考え
aapt2
自分自身を使用しpublic.xml
て としてコンパイルしpublic.arsc.flat
、それをaapt
; のますか${mergeResourceTask.outputDir}
?
手
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}")
}
})
}
}
}
}
public.txt
ファイルをpublic.xml
ファイルに変換します。
public.txt
styleable
にはタイプ リソースがありますpublic.xml
が、 には存在しないため、変換中にタイプが検出された場合はstyleable
無視する必要があります。vector
ベクトル グラフィック リソース用の内部リソースがある場合は、それらも無視する必要があります. では、aapt2
その名前は$
で始まり、その後にメイン リソースの名前が続き、その後に __ 番号のインクリメント インデックスが続きます. これらのリソースは外部から参照することはできず、必要なのは修正予定.タグをid
追加する必要はなく、シンボルはでは違法であるため、無視してください。PUBLIC
$
public.xml
aapt2
リソースには固定された方法があるためid
、変換プロセス中に直接失われる可能性がありid
、簡単な宣言で十分です (PS: ここでは、withId
固定する必要があるかどうかはパラメーターによって制御されますid
)。aapt2
コンパイルされたファイルの親ディレクトリはフォルダーpublic.xml
でなければなりません。そうしないと、コンパイル プロセス中に不正なパスが報告されます。values
/**
* 转换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>")
}
上記の思考と実践のプロセスを通じて、aapt2
マーキングの問題を解決しただけでなくPUBLIC
、新しい修正方法も発見しましたaapt2
。id
発生する可能性のあるエラー:
no signature of method com.android.build.gradle.internal.variant.applicationvariantdata.getscope() is applicable for argument types: () values: []
解決策は、gradle
バージョンをgradle:3.3.2
および変更することですgradle-wrapper:5.6.2
。結局のところ、 は最新バージョンの をサポートtinker
していませんgradle
。
参考:
記事はここですべて説明します。他に連絡が必要な場合は、メッセージを残してください〜!〜!
著者の記事をもっと読みたい場合は、私の個人ブログと公開アカウントをチェックしてください。