最终打包出来的那些资源id,是会重新分配的(同一个资源生成的id,就算不在同一个R.class里,打包出来时也是相同的)。
做了个测试:
app依赖了模块test;
test模块中的string.xml有个test_value;
在test模块中引用(com.test.R),看到这个test_value的id值是-1900002;
而在app中(com.app.R)引用时,id值为-1900023;
这时候可以看出,相同的资源,id确实是不同。
打包成apk后,把它拖进AS里,点开classes.dex:
首先找到app包名下的R$string,右键查看字节码,发现test_value的id值由原来的-1900023,变成了0x7f10005d;
接着找test模块包名下的R$string,查看字节码发现id值一样是0x7f10005d;
由此看来,id值在打包成apk的时候,确实是重新分配了,而且同一个资源的id值,也统一了。
源码分析
那它究竟是如何重新分配这些id值的呢?
上一次也分析过https://www.wanandroid.com/wenda/show/8985,很多任务都是在ApplicationTaskManager里的createTasksForVariantScope方法中创建的。
那现在再来看一下这个方法:
@Override
public void createTasksForVariantScope(
@NonNull final VariantScope variantScope,
@NonNull List variantScopesForLint) {
......
// Add a task to create the BuildConfig class
createBuildConfigTask(variantScope);
// Add a task to process the Android Resources and generate source files
createApkProcessResTask(variantScope);
......
}
可以看到,创建BuildConfig任务之后,接着调用了一个createApkProcessResTask方法,看上面注释,是用来处理资源和生成源码文件的。一步步点进去看看:
createApkProcessResTask() ->
createProcessResTask() ->
createNonNamespacedResourceTasks() ->
GenerateLibraryRFileTask.doFullTaskAction() ->
GenerateLibRFileRunnable.run() ->
SymbolExportUtils.processLibraryMainSymbolTable()
看看这个processLibraryMainSymbolTable方法:
fun processLibraryMainSymbolTable() {
......
val tablesToWrite = processLibraryMainSymbolTable()
// Generate R.java files for main and dependencies
tablesToWrite.forEach { SymbolIo.exportToJava(it, sourceOut, false) }
......
}
看中间的注释,可以确定下面一句就是生成R.java的了,它会为tablesToWrite里面的每一个item都生成一个R文件,看一下这个tablesToWrite怎么来的:
internal fun processLibraryMainSymbolTable(): List {
// Merge all the symbols together.
// We have to rewrite the IDs because some published R.txt inside AARs are using the
// wrong value for some types, and we need to ensure there is no collision in the
// file we are creating.
val allSymbols: SymbolTable = mergeAndRenumberSymbols(
finalPackageName, librarySymbols, depSymbolTables, platformSymbols
)
val mainSymbolTable = if (namespacedRClass) allSymbols.filter(librarySymbols) else allSymbols
// Generate R.txt file.
Files.createDirectories(symbolFileOut.parent)
SymbolIo.writeForAar(mainSymbolTable, symbolFileOut)
val tablesToWrite =
RGeneration.generateAllSymbolTablesToWrite(allSymbols, mainSymbolTable, depSymbolTables)
return tablesToWrite
}
妈耶!看开头的注释:"We have to rewrite the IDs",也就是必须重写这些id的意思了,再看一下它接下来调用的mergeAndRenumberSymbols方法:
fun mergeAndRenumberSymbols(): SymbolTable {
......
// the ID value provider.
val idProvider = IdProvider.sequential()
......
}
可以看到调用了IdProvider的sequential方法,点开看下:
fun sequential(): IdProvider {
return object : IdProvider {
private val next = ShortArray(ResourceType.values().size)
override fun next(resourceType: ResourceType): Int {
val typeIndex = resourceType.ordinal
return 0x7f shl 24 or (typeIndex + 1 shl 16) or (++next[typeIndex]).toInt()
}
}
}
emmm,0x7f开头的id值,确实是对应的我们开头打包后的那个test_value的id值(0x7f10005d)。
到这里基本可以确定,最终打包的资源id,就是通过这个IdProvider的匿名子类来重新创建的,而且同一个资源所对应的id,也是一样的。