Skip to content

Commit 09c6bd9

Browse files
committed
1、完善支持Dynamic模块
1 parent 99e76d6 commit 09c6bd9

17 files changed

Lines changed: 390 additions & 84 deletions

File tree

android-aop-plugin/src/main/kotlin/com/flyjingfish/android_aop_plugin/plugin/CompilePlugin.kt

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,28 @@ import com.android.build.gradle.DynamicFeaturePlugin
99
import com.android.build.gradle.LibraryExtension
1010
import com.android.build.gradle.LibraryPlugin
1111
import com.flyjingfish.android_aop_plugin.config.AndroidAopConfig
12+
import com.flyjingfish.android_aop_plugin.scanner_visitor.SuspendReturnScanner
1213
import com.flyjingfish.android_aop_plugin.scanner_visitor.WovenIntoCode
1314
import com.flyjingfish.android_aop_plugin.tasks.CompileAndroidAopTask
1415
import com.flyjingfish.android_aop_plugin.tasks.DebugModeFileTask
1516
import com.flyjingfish.android_aop_plugin.utils.ClassFileUtils
17+
import com.flyjingfish.android_aop_plugin.utils.ClassPoolUtils
18+
import com.flyjingfish.android_aop_plugin.utils.FileHashUtils
1619
import com.flyjingfish.android_aop_plugin.utils.InitConfig
1720
import com.flyjingfish.android_aop_plugin.utils.RuntimeProject
1821
import com.flyjingfish.android_aop_plugin.utils.Utils
22+
import com.flyjingfish.android_aop_plugin.utils.WovenInfoUtils
1923
import com.flyjingfish.android_aop_plugin.utils.adapterOSPath
2024
import com.flyjingfish.android_aop_plugin.utils.getBuildDirectory
2125
import com.flyjingfish.android_aop_plugin.utils.getRelativePath
2226
import org.codehaus.groovy.runtime.DefaultGroovyMethods
27+
import org.gradle.BuildListener
28+
import org.gradle.BuildResult
2329
import org.gradle.api.Project
2430
import org.gradle.api.execution.TaskExecutionGraph
2531
import org.gradle.api.execution.TaskExecutionGraphListener
32+
import org.gradle.api.initialization.Settings
33+
import org.gradle.api.invocation.Gradle
2634
import org.gradle.api.plugins.JavaPluginExtension
2735
import org.gradle.api.tasks.PathSensitivity
2836
import org.gradle.api.tasks.compile.AbstractCompile
@@ -159,12 +167,36 @@ class CompilePlugin(private val fromRootSet:Boolean): BasePlugin() {
159167
val hasConfig = project.extensions.findByName("androidAopConfig") != null
160168
val syncConfig = !fromRootSet && hasConfig && isApp
161169
if (syncConfig){
162-
project.afterEvaluate {
163-
AndroidAopConfig.syncConfig(project)
164-
if (AndroidAopConfig.cutInfoJson){
165-
InitConfig.initCutInfo(runtimeProject,false)
170+
var startTask = false
171+
project.rootProject.gradle.taskGraph.addTaskExecutionGraphListener(object :
172+
TaskExecutionGraphListener {
173+
override fun graphPopulated(it: TaskExecutionGraph) {
174+
startTask = true
175+
AndroidAopConfig.syncConfig(project)
176+
if (AndroidAopConfig.cutInfoJson){
177+
InitConfig.initCutInfo(runtimeProject,true)
178+
}
179+
project.rootProject.gradle.taskGraph.removeTaskExecutionGraphListener(this)
166180
}
167-
}
181+
182+
})
183+
project.rootProject.gradle.addBuildListener(object : BuildListener{
184+
override fun settingsEvaluated(settings: Settings) {
185+
}
186+
187+
override fun projectsLoaded(gradle: Gradle) {
188+
}
189+
190+
override fun projectsEvaluated(gradle: Gradle) {
191+
}
192+
193+
override fun buildFinished(result: BuildResult) {
194+
if (startTask){
195+
endAllTask()
196+
}
197+
}
198+
199+
})
168200
}
169201

170202

@@ -202,7 +234,7 @@ class CompilePlugin(private val fromRootSet:Boolean): BasePlugin() {
202234
}
203235
val kotlinBuildPath = File(project.getBuildDirectory().path + "/tmp/kotlin-classes/".adapterOSPath() + variantName)
204236
javaCompile.doLast{
205-
doAopTask(runtimeProject,isApp, variantName, buildTypeName, javaCompile, kotlinBuildPath)
237+
doAopTask(runtimeProject,isApp, variantName, buildTypeName, javaCompile, kotlinBuildPath,true,isDynamicLibrary)
206238
}
207239
}
208240
if (hasBuildConfig()){
@@ -320,7 +352,7 @@ class CompilePlugin(private val fromRootSet:Boolean): BasePlugin() {
320352
}
321353

322354
private fun doAopTask(project: RuntimeProject,isApp:Boolean, variantName: String, buildTypeName: String,
323-
javaCompile:AbstractCompile, kotlinDefaultPath: File, isAndroidModule : Boolean = true){
355+
javaCompile:AbstractCompile, kotlinDefaultPath: File, isAndroidModule : Boolean = true, isDynamic : Boolean = false){
324356
val cacheDir = if (isAndroidModule) {
325357
kotlinCompileFilePathMap[project.buildDir.absolutePath+"@"+"compile${variantName.capitalized()}Kotlin"]
326358
}else{
@@ -385,7 +417,7 @@ class CompilePlugin(private val fromRootSet:Boolean): BasePlugin() {
385417
val task = CompileAndroidAopTask(jarInputFiles,localInputFiles,output,project,isApp,
386418
File(Utils.aopCompileTempDir(project,variantName)),
387419
File(Utils.invokeJsonFile(project,variantName)),
388-
variantName,javaCompile,isAndroidModule
420+
variantName,javaCompile,isAndroidModule,isDynamic
389421
)
390422
task.taskAction()
391423
}
@@ -416,4 +448,11 @@ class CompilePlugin(private val fromRootSet:Boolean): BasePlugin() {
416448
return file
417449
}
418450
}
451+
452+
private fun endAllTask(){
453+
FileHashUtils.clearScanRecord()
454+
WovenInfoUtils.clear()
455+
ClassPoolUtils.clear()
456+
SuspendReturnScanner.hasSuspendReturn = false
457+
}
419458
}

android-aop-plugin/src/main/kotlin/com/flyjingfish/android_aop_plugin/plugin/TransformPlugin.kt

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package com.flyjingfish.android_aop_plugin.plugin
22

33
import com.android.build.api.variant.AndroidComponentsExtension
44
import com.android.build.gradle.AppPlugin
5+
import com.android.build.gradle.DynamicFeaturePlugin
56
import com.android.build.gradle.internal.tasks.DexArchiveBuilderTask
67
import com.flyjingfish.android_aop_plugin.config.AndroidAopConfig
78
import com.flyjingfish.android_aop_plugin.tasks.AssembleAndroidAopTask
8-
import com.flyjingfish.android_aop_plugin.utils.InitConfig
9+
import com.flyjingfish.android_aop_plugin.utils.AppClasses
910
import com.flyjingfish.android_aop_plugin.utils.RuntimeProject
1011
import io.github.flyjingfish.fast_transform.tasks.DefaultTransformTask
1112
import io.github.flyjingfish.fast_transform.toTransformAll
@@ -16,57 +17,68 @@ object TransformPlugin : BasePlugin() {
1617
override fun apply(project: Project) {
1718
super.apply(project)
1819
val isApp = project.plugins.hasPlugin(AppPlugin::class.java)
19-
if (!isApp) {
20+
val isDynamic = project.plugins.hasPlugin(DynamicFeaturePlugin::class.java)
21+
if (!isApp&&!isDynamic) {
2022
if (project.rootProject != project){
2123
project.afterEvaluate {
2224
val isAndroidProject: Boolean = project.extensions.findByName(CompilePlugin.ANDROID_EXTENSION_NAME) != null
2325
val isApp2 = project.plugins.hasPlugin(AppPlugin::class.java)
2426
if (isApp2 && isAndroidProject){
25-
throw RuntimeException("In the module of ${project.name}, [id 'android.aop'] must be written below [id 'com.android.application']")
27+
throw RuntimeException("In the module of ${project.name}, [id 'android.aop'] must be written below [id 'com.android.application' or id 'com.android.dynamic-feature']")
2628
}
2729
}
2830
}
2931
return
3032
}
31-
project.rootProject.gradle.taskGraph.addTaskExecutionGraphListener {
32-
try {
33-
var lastCanModifyTask : Task? =null
34-
var dexTask : DexArchiveBuilderTask? =null
35-
var aopTask : AssembleAndroidAopTask? =null
33+
if (isApp){
34+
project.rootProject.gradle.taskGraph.addTaskExecutionGraphListener {
35+
AppClasses.clearModuleNames()
3636
for (task in it.allTasks) {
37-
if (task is AssembleAndroidAopTask){
38-
aopTask = task
37+
val project = task.project
38+
val isApp = project.plugins.hasPlugin(AppPlugin::class.java)
39+
val isDynamic = project.plugins.hasPlugin(DynamicFeaturePlugin::class.java)
40+
if (isApp || isDynamic){
41+
AppClasses.addModuleName(project.name)
3942
}
40-
if (task is DexArchiveBuilderTask){
41-
dexTask = task
42-
break
43-
}
44-
lastCanModifyTask = task
4543
}
46-
if (lastCanModifyTask != null && dexTask != null && aopTask != null){
47-
if (lastCanModifyTask !is AssembleAndroidAopTask && lastCanModifyTask !is DefaultTransformTask){
48-
if (aopTask.isFastDex){
49-
val hintText = "When fastDex is enabled, you should put [id 'android.aop'] at the end to make ${aopTask.name} execute after ${lastCanModifyTask.name}"
50-
project.logger.error(hintText)
51-
aopTask.doLast {
52-
project.logger.error(hintText)
53-
}
54-
it.allTasks[it.allTasks.size - 1].doLast {
44+
try {
45+
var lastCanModifyTask : Task? =null
46+
var dexTask : DexArchiveBuilderTask? =null
47+
var aopTask : AssembleAndroidAopTask? =null
48+
for (task in it.allTasks) {
49+
if (task is AssembleAndroidAopTask){
50+
aopTask = task
51+
}
52+
if (task is DexArchiveBuilderTask){
53+
dexTask = task
54+
break
55+
}
56+
lastCanModifyTask = task
57+
}
58+
if (lastCanModifyTask != null && dexTask != null && aopTask != null){
59+
if (lastCanModifyTask !is AssembleAndroidAopTask && lastCanModifyTask !is DefaultTransformTask){
60+
if (aopTask.isFastDex){
61+
val hintText = "When fastDex is enabled, you should put [id 'android.aop'] at the end to make ${aopTask.name} execute after ${lastCanModifyTask.name}"
5562
project.logger.error(hintText)
63+
aopTask.doLast {
64+
project.logger.error(hintText)
65+
}
66+
it.allTasks[it.allTasks.size - 1].doLast {
67+
project.logger.error(hintText)
68+
}
5669
}
5770
}
5871
}
72+
} catch (_: Exception) {
5973
}
60-
} catch (_: Exception) {
6174
}
6275
}
6376
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
6477
androidComponents.onVariants { variant ->
65-
val androidAopConfig = project.extensions.getByType(AndroidAopConfig::class.java)
66-
androidAopConfig.initConfig()
6778
val runtimeProject = RuntimeProject.get(project)
68-
if (androidAopConfig.cutInfoJson){
69-
InitConfig.initCutInfo(runtimeProject)
79+
val androidAopConfig = project.extensions.getByType(AndroidAopConfig::class.java)
80+
if (isApp){
81+
androidAopConfig.initConfig()
7082
}
7183
val buildTypeName = variant.buildType
7284
if (androidAopConfig.enabled && !isDebugMode(buildTypeName,variant.name)){
@@ -76,6 +88,8 @@ object TransformPlugin : BasePlugin() {
7688
it.reflectInvokeMethod = isReflectInvokeMethod(buildTypeName,variant.name)
7789
it.reflectInvokeMethodStatic = isReflectInvokeMethodStatic()
7890
it.variant = variant.name
91+
it.isDynamic = isDynamic
92+
it.isApp = isApp
7993
}
8094
variant.toTransformAll(task,fastDex)
8195
}

android-aop-plugin/src/main/kotlin/com/flyjingfish/android_aop_plugin/scanner_visitor/ReplaceBaseClassVisitor.kt

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.flyjingfish.android_aop_plugin.scanner_visitor
22

3+
import com.flyjingfish.android_aop_plugin.utils.AppClasses
34
import com.flyjingfish.android_aop_plugin.utils.ClassPoolUtils
45
import com.flyjingfish.android_aop_plugin.utils.InitConfig
6+
import com.flyjingfish.android_aop_plugin.utils.Utils
57
import com.flyjingfish.android_aop_plugin.utils.Utils.dotToSlash
68
import com.flyjingfish.android_aop_plugin.utils.Utils.slashToDot
79
import com.flyjingfish.android_aop_plugin.utils.Utils.slashToDotClassName
@@ -10,8 +12,10 @@ import com.flyjingfish.android_aop_plugin.utils.computeMD5
1012
import com.flyjingfish.android_aop_plugin.utils.inRules
1113
import com.flyjingfish.android_aop_plugin.utils.printError
1214
import org.objectweb.asm.ClassVisitor
15+
import org.objectweb.asm.Label
1316
import org.objectweb.asm.MethodVisitor
1417
import org.objectweb.asm.Opcodes
18+
import org.objectweb.asm.Type
1519
import org.objectweb.asm.commons.AdviceAdapter
1620
import org.objectweb.asm.signature.SignatureReader
1721
import org.objectweb.asm.signature.SignatureVisitor
@@ -187,11 +191,45 @@ open class ReplaceBaseClassVisitor(
187191
override fun visitInsn(opcode: Int) {
188192
if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
189193
modifyed = true
190-
val className = "$thisClassName\$Inner${thisClassName.computeMD5()}"
191-
mv.visitTypeInsn(NEW, dotToSlash(className));
192-
mv.visitInsn(DUP);//压入栈
193-
//弹出一个对象所在的地址,进行初始化操作,构造函数默认为空,此时栈大小为1(到目前只有一个局部变量)
194-
mv.visitMethodInsn(INVOKESPECIAL, dotToSlash(className),"<init>","()V",false);
194+
for (moduleName in AppClasses.getAllModuleNames()) {
195+
val tryStart = Label()
196+
val tryEnd = Label()
197+
val labelCatch = Label()
198+
val tryCatchBlockEnd = Label()
199+
200+
mv.visitTryCatchBlock(
201+
tryStart,
202+
tryEnd,
203+
labelCatch,
204+
"java/lang/NoClassDefFoundError"
205+
)
206+
mv.visitLabel(tryStart)
207+
208+
val className = "$thisClassName\$Inner${thisClassName.computeMD5()}_${moduleName.computeMD5()}"
209+
mv.visitTypeInsn(NEW, dotToSlash(className));
210+
mv.visitInsn(DUP);//压入栈
211+
//弹出一个对象所在的地址,进行初始化操作,构造函数默认为空,此时栈大小为1(到目前只有一个局部变量)
212+
mv.visitMethodInsn(INVOKESPECIAL, dotToSlash(className),"<init>","()V",false)
213+
mv.visitInsn(POP) // 保证 try 分支栈深为 0
214+
215+
mv.visitLabel(tryEnd)
216+
mv.visitJumpInsn(Opcodes.GOTO, tryCatchBlockEnd)
217+
218+
mv.visitLabel(labelCatch)
219+
mv.visitVarInsn(Opcodes.ASTORE, 0)
220+
221+
mv.visitVarInsn(Opcodes.ALOAD, 0)
222+
mv.visitMethodInsn(
223+
Opcodes.INVOKEVIRTUAL,
224+
"java/lang/NoClassDefFoundError",
225+
"printStackTrace",
226+
"()V",
227+
false
228+
)
229+
230+
mv.visitLabel(tryCatchBlockEnd)
231+
232+
}
195233

196234
}
197235
super.visitInsn(opcode)

android-aop-plugin/src/main/kotlin/com/flyjingfish/android_aop_plugin/scanner_visitor/WovenIntoCode.kt

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.flyjingfish.android_aop_plugin.beans.AopCollectCut
66
import com.flyjingfish.android_aop_plugin.beans.CutFileJson
77
import com.flyjingfish.android_aop_plugin.beans.MethodRecord
88
import com.flyjingfish.android_aop_plugin.beans.ReplaceMethodInfo
9+
import com.flyjingfish.android_aop_plugin.utils.AppClasses
910
import com.flyjingfish.android_aop_plugin.utils.ClassFileUtils
1011
import com.flyjingfish.android_aop_plugin.utils.ClassNameToConversions
1112
import com.flyjingfish.android_aop_plugin.utils.ClassPoolUtils
@@ -17,6 +18,7 @@ import com.flyjingfish.android_aop_plugin.utils.Utils.CONVERSIONS_CLASS
1718
import com.flyjingfish.android_aop_plugin.utils.Utils.JOIN_LOCK
1819
import com.flyjingfish.android_aop_plugin.utils.Utils.JOIN_POINT_CLASS
1920
import com.flyjingfish.android_aop_plugin.utils.Utils.KEEP_CLASS
21+
import com.flyjingfish.android_aop_plugin.utils.Utils.dotToSlash
2022
import com.flyjingfish.android_aop_plugin.utils.WovenInfoUtils
2123
import com.flyjingfish.android_aop_plugin.utils.adapterOSPath
2224
import com.flyjingfish.android_aop_plugin.utils.addPublic
@@ -976,10 +978,46 @@ object WovenIntoCode {
976978

977979
fun wovenStaticCode(cw:ClassWriter,thisClassName:String){
978980
val mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "<clinit>", "()V", null, null)
979-
val className1 = "$thisClassName\$Inner${thisClassName.computeMD5()}"
980-
mv.visitTypeInsn(AdviceAdapter.NEW,Utils.dotToSlash(className1))
981-
mv.visitInsn(AdviceAdapter.DUP)
982-
mv.visitMethodInsn(AdviceAdapter.INVOKESPECIAL,Utils.dotToSlash(className1),"<init>","()V",false)
981+
for (moduleName in AppClasses.getAllModuleNames()) {
982+
val tryStart = Label()
983+
val tryEnd = Label()
984+
val labelCatch = Label()
985+
val tryCatchBlockEnd = Label()
986+
987+
mv.visitTryCatchBlock(
988+
tryStart,
989+
tryEnd,
990+
labelCatch,
991+
"java/lang/NoClassDefFoundError"
992+
)
993+
mv.visitLabel(tryStart)
994+
995+
val className1 = "$thisClassName\$Inner${thisClassName.computeMD5()}_${moduleName.computeMD5()}"
996+
mv.visitTypeInsn(AdviceAdapter.NEW,Utils.dotToSlash(className1))
997+
mv.visitInsn(AdviceAdapter.DUP)
998+
mv.visitMethodInsn(AdviceAdapter.INVOKESPECIAL,Utils.dotToSlash(className1),"<init>","()V",false)
999+
mv.visitInsn(POP) // 保证 try 分支栈深为 0
1000+
1001+
mv.visitLabel(tryEnd)
1002+
mv.visitJumpInsn(Opcodes.GOTO, tryCatchBlockEnd)
1003+
1004+
mv.visitLabel(labelCatch)
1005+
mv.visitVarInsn(Opcodes.ASTORE, 0)
1006+
1007+
mv.visitVarInsn(Opcodes.ALOAD, 0)
1008+
mv.visitMethodInsn(
1009+
Opcodes.INVOKEVIRTUAL,
1010+
"java/lang/NoClassDefFoundError",
1011+
"printStackTrace",
1012+
"()V",
1013+
false
1014+
)
1015+
1016+
mv.visitLabel(tryCatchBlockEnd)
1017+
1018+
1019+
}
1020+
9831021
mv.visitInsn(RETURN)
9841022
mv.visitMaxs(0, 0)
9851023
mv.visitEnd()
@@ -1105,15 +1143,15 @@ object WovenIntoCode {
11051143
return outFile
11061144
}
11071145

1108-
fun createCollectClass(output:File) = runBlocking{
1146+
fun createCollectClass(output:File,moduleName: String) = runBlocking{
11091147
val collectDirJobs = mutableListOf<Deferred<Unit>>()
11101148
val classPool = ClassPoolUtils.getNewClassPool()
11111149
WovenInfoUtils.getAopCollectClassMap().forEach {(key,value) ->
11121150
if (value == null){
11131151
return@forEach
11141152
}
11151153
val job = async(Dispatchers.IO) {
1116-
val className = "$key\$Inner${key.computeMD5()}"
1154+
val className = "$key\$Inner${key.computeMD5()}_${moduleName.computeMD5()}"
11171155
val cw = ClassWriter(COMPUTE_FRAMES or COMPUTE_MAXS)
11181156
cw.visit(V1_8, ACC_PUBLIC+ACC_STATIC, Utils.dotToSlash(className), null, "java/lang/Object", null)
11191157

0 commit comments

Comments
 (0)