-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core.manifest-parser): 实现编译期解析插件Manifest生成java类
通过PluginClassLoader新增方法loadPluginManifest可加载该类。 #696
- Loading branch information
Showing
26 changed files
with
1,063 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
apply plugin: 'kotlin' | ||
|
||
dependencies { | ||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | ||
implementation 'com.squareup:javapoet:1.11.1' | ||
implementation project(':runtime') | ||
testImplementation 'junit:junit:4.12' | ||
testImplementation 'commons-io:commons-io:2.9.0' | ||
testImplementation 'com.tencent.shadow.coding:java-build-config' | ||
} | ||
|
||
compileKotlin { | ||
kotlinOptions { | ||
jvmTarget = "1.6" | ||
} | ||
} | ||
|
||
compileTestKotlin { | ||
kotlinOptions { | ||
jvmTarget = "1.6" | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...est-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/AndroidManifestKeys.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.tencent.shadow.core.manifest_parser | ||
|
||
sealed class AndroidManifestKeys { | ||
companion object { | ||
const val appComponentFactory = "android:appComponentFactory" | ||
const val `package` = "package" | ||
const val name = "android:name" | ||
const val theme = "android:theme" | ||
const val configChanges = "android:configChanges" | ||
const val windowSoftInputMode = "android:windowSoftInputMode" | ||
const val authorities = "android:authorities" | ||
const val `intent-filter` = "intent-filter" | ||
const val action = "action" | ||
const val manifest = "manifest" | ||
const val application = "application" | ||
const val activity = "activity" | ||
const val service = "service" | ||
const val provider = "provider" | ||
const val receiver = "receiver" | ||
} | ||
} | ||
typealias ComponentMapKey = String | ||
typealias ComponentMapValue = Any | ||
typealias ComponentMap = Map<ComponentMapKey, ComponentMapValue> | ||
typealias MutableComponentMap = MutableMap<ComponentMapKey, ComponentMapValue> |
177 changes: 177 additions & 0 deletions
177
...t-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/AndroidManifestReader.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package com.tencent.shadow.core.manifest_parser | ||
|
||
import org.w3c.dom.Document | ||
import org.w3c.dom.Element | ||
import org.w3c.dom.Node | ||
import java.io.File | ||
import javax.xml.parsers.DocumentBuilderFactory | ||
|
||
typealias ManifestMap = Map<String, Any> | ||
|
||
/** | ||
* 读取xml格式的Manifest到内存Map中 | ||
*/ | ||
class AndroidManifestReader { | ||
/** | ||
* 读取入口方法 | ||
* | ||
* @param xmlFile com.android.build.gradle.tasks.ManifestProcessorTask任务的输出文件, | ||
* 一般位于apk工程的build/intermediates/merged_manifest目录中。 | ||
*/ | ||
fun read(xmlFile: File): ManifestMap { | ||
val manifest = readXml(xmlFile).documentElement | ||
val application = readApplication(manifest) | ||
val globalAttributes = readGlobalAttributes(manifest, application) | ||
val components = readComponents(application) | ||
return globalAttributes.plus(components) | ||
} | ||
|
||
private fun readXml(xmlFile: File): Document { | ||
try { | ||
val documentBuilderFactory = DocumentBuilderFactory.newDefaultInstance() | ||
val documentBuilder = documentBuilderFactory.newDocumentBuilder() | ||
return documentBuilder.parse(xmlFile)!! | ||
} catch (e: Exception) { | ||
throw RuntimeException("xml应该是AGP生成的合法文件,所以不兼容任何xml读取错误", e) | ||
} | ||
} | ||
|
||
private fun readApplication(manifest: Element): Element? { | ||
val elements = manifest.getElementsByTagName(AndroidManifestKeys.application) | ||
return if (elements.length == 1) { | ||
val node = elements.item(0) | ||
assert(node.nodeType == Node.ELEMENT_NODE) | ||
elements.item(0) as Element | ||
} else { | ||
null | ||
} | ||
} | ||
|
||
/** | ||
* 读取Manifest中那些唯一的属性 | ||
*/ | ||
private fun readGlobalAttributes(manifest: Element, application: Element?): Map<String, Any> { | ||
val globalAttributes = mutableMapOf<String, Any>() | ||
|
||
fun manifestAttribute(name: String) { | ||
globalAttributes[name] = manifest.getAttribute(name) | ||
} | ||
|
||
fun applicationAttribute(name: String) { | ||
if (application != null) { | ||
val attribute = application.getAttribute(name) | ||
if (attribute.isNotEmpty()) { | ||
globalAttributes[name] = attribute | ||
} | ||
} | ||
} | ||
|
||
manifestAttribute(AndroidManifestKeys.`package`) | ||
listOf( | ||
AndroidManifestKeys.name, | ||
AndroidManifestKeys.theme, | ||
AndroidManifestKeys.appComponentFactory, | ||
).forEach(::applicationAttribute) | ||
return globalAttributes | ||
} | ||
|
||
private fun readComponents(application: Element?) = | ||
listOf( | ||
AndroidManifestKeys.activity to ::parseActivity, | ||
AndroidManifestKeys.service to ::parseService, | ||
AndroidManifestKeys.receiver to ::parseReceiver, | ||
AndroidManifestKeys.provider to ::parseProvider, | ||
).map { (componentKey, parseMethod) -> | ||
val componentArray = parseComponents(application, componentKey, parseMethod) | ||
componentKey to componentArray | ||
} | ||
|
||
|
||
private fun parseComponents( | ||
application: Element?, | ||
componentKey: String, | ||
parseFunction: (Element) -> ComponentMap | ||
): Array<ComponentMap> { | ||
if (application == null) { | ||
return emptyArray() | ||
} | ||
val nodeList = application.getElementsByTagName(componentKey) | ||
val length = nodeList.length | ||
val collectionList = mutableListOf<ComponentMap>() | ||
for (i in 0 until length) { | ||
val node = nodeList.item(i) | ||
assert(node.nodeType == Node.ELEMENT_NODE) | ||
val map = parseFunction(node as Element) | ||
collectionList.add(map) | ||
} | ||
return collectionList.toTypedArray() | ||
} | ||
|
||
private fun parseActivity(element: Element): ComponentMap { | ||
val activityMap = parseComponent(element).toMutableMap() | ||
|
||
listOf( | ||
AndroidManifestKeys.theme, | ||
AndroidManifestKeys.configChanges, | ||
AndroidManifestKeys.windowSoftInputMode, | ||
).forEach { attributeKey -> | ||
activityMap.putAttributeIfNotNull(element, attributeKey) | ||
} | ||
return activityMap | ||
} | ||
|
||
private fun parseService(element: Element): ComponentMap { | ||
return parseComponent(element) | ||
} | ||
|
||
private fun parseReceiver(element: Element): ComponentMap { | ||
val receiverMap = parseComponent(element).toMutableMap() | ||
|
||
val receiverActions = parseReceiverActions(element) | ||
if (receiverActions.isNotEmpty()) { | ||
receiverMap[AndroidManifestKeys.action] = receiverActions | ||
} | ||
|
||
return receiverMap | ||
} | ||
|
||
private fun parseReceiverActions(receiverElement: Element): List<String> { | ||
val intentFilters = receiverElement.getElementsByTagName(AndroidManifestKeys.`intent-filter`) | ||
val collectionList = mutableListOf<String>() | ||
for (i in 0 until intentFilters.length) { | ||
val intentFilter = intentFilters.item(i) | ||
assert(intentFilter.nodeType == Node.ELEMENT_NODE) | ||
val actions = (intentFilter as Element).getElementsByTagName(AndroidManifestKeys.action) | ||
for (j in 0 until actions.length) { | ||
val action = actions.item(j) | ||
assert(action.nodeType == Node.ELEMENT_NODE) | ||
val actionName = (action as Element).getAttribute(AndroidManifestKeys.name) | ||
collectionList.add(actionName) | ||
} | ||
} | ||
return collectionList | ||
} | ||
|
||
private fun parseProvider(element: Element): ComponentMap { | ||
val providerMap = parseComponent(element).toMutableMap() | ||
|
||
providerMap.putAttributeIfNotNull(element, AndroidManifestKeys.authorities) | ||
|
||
return providerMap | ||
} | ||
|
||
private fun parseComponent(element: Element): ComponentMap { | ||
val componentName = element.getAttribute(AndroidManifestKeys.name) | ||
return mapOf( | ||
AndroidManifestKeys.name to componentName | ||
) | ||
} | ||
|
||
private fun MutableComponentMap.putAttributeIfNotNull( | ||
componentElement: Element, | ||
attributeKey: String) { | ||
if (componentElement.hasAttribute(attributeKey)) { | ||
this[attributeKey] = componentElement.getAttribute(attributeKey) | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
...manifest-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/ManifestParser.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.tencent.shadow.core.manifest_parser | ||
|
||
import java.io.File | ||
|
||
/** | ||
* manifest-parser的入口方法 | ||
* | ||
* @param xmlFile com.android.build.gradle.tasks.ManifestProcessorTask任务的输出文件, | ||
* 一般位于apk工程的build/intermediates/merged_manifest目录中。 | ||
* @param outputDir 生成文件的输出目录 | ||
* @param packageName 生成类的包名 | ||
* @param packageForR 生成对R.java引用时需要的R文件的包名 | ||
*/ | ||
fun generatePluginManifest(xmlFile: File, outputDir: File, packageName: String, packageForR: String) { | ||
val androidManifest = AndroidManifestReader().read(xmlFile) | ||
val generator = PluginManifestGenerator(packageForR) | ||
generator.generate(androidManifest, outputDir, packageName) | ||
} |
Oops, something went wrong.