From 7dc6446196f76bf365d792acafdeaaaf91336d05 Mon Sep 17 00:00:00 2001 From: neighbWang Date: Fri, 7 Jun 2019 10:59:47 +0800 Subject: [PATCH] ActivityThread optimization --- .../build.gradle | 7 ++ .../instrument/ActivityThreadHooker.java | 76 +++++++++++ .../build.gradle | 9 ++ .../ActivityThreadTransformer.kt | 119 ++++++++++++++++++ .../ActivityThreadVarinatProcessor.kt | 22 ++++ booster-transform-all/build.gradle | 1 + settings.gradle | 2 + 7 files changed, 236 insertions(+) create mode 100644 booster-android-instrument-activity-thread/build.gradle create mode 100644 booster-android-instrument-activity-thread/src/main/java/com/didiglobal/booster/instrument/ActivityThreadHooker.java create mode 100644 booster-transform-activity-thread/build.gradle create mode 100644 booster-transform-activity-thread/src/main/kotlin/com/didiglobal/booster/transform/activitythread/ActivityThreadTransformer.kt create mode 100644 booster-transform-activity-thread/src/main/kotlin/com/didiglobal/booster/transform/activitythread/ActivityThreadVarinatProcessor.kt diff --git a/booster-android-instrument-activity-thread/build.gradle b/booster-android-instrument-activity-thread/build.gradle new file mode 100644 index 0000000..1138240 --- /dev/null +++ b/booster-android-instrument-activity-thread/build.gradle @@ -0,0 +1,7 @@ +sourceCompatibility = JavaVersion.VERSION_1_7 +targetCompatibility = JavaVersion.VERSION_1_7 + +dependencies { + compileOnly project(':booster-android-api') + compile project(':booster-android-instrument') +} diff --git a/booster-android-instrument-activity-thread/src/main/java/com/didiglobal/booster/instrument/ActivityThreadHooker.java b/booster-android-instrument-activity-thread/src/main/java/com/didiglobal/booster/instrument/ActivityThreadHooker.java new file mode 100644 index 0000000..e106ccf --- /dev/null +++ b/booster-android-instrument-activity-thread/src/main/java/com/didiglobal/booster/instrument/ActivityThreadHooker.java @@ -0,0 +1,76 @@ +package com.didiglobal.booster.instrument; + +import android.os.Handler; +import android.util.Log; +import com.didiglobal.booster.android.bugfix.CaughtCallback; + +import static com.didiglobal.booster.android.bugfix.Constants.TAG; +import static com.didiglobal.booster.android.bugfix.Reflection.getFieldValue; +import static com.didiglobal.booster.android.bugfix.Reflection.getStaticFieldValue; +import static com.didiglobal.booster.android.bugfix.Reflection.invokeMethod; +import static com.didiglobal.booster.android.bugfix.Reflection.setFieldValue; + +/** + * @author neighbWang + */ +public class ActivityThreadHooker { + + private volatile static boolean hooked; + + public static void hook() { + if (hooked) { + return; + } + + Object thread = null; + try { + thread = android.app.ActivityThread.currentActivityThread(); + } catch (final Throwable t1) { + Log.w(TAG, "ActivityThread.currentActivityThread() is inaccessible", t1); + try { + thread = getStaticFieldValue(android.app.ActivityThread.class, "sCurrentActivityThread"); + } catch (final Throwable t2) { + Log.w(TAG, "ActivityThread.sCurrentActivityThread is inaccessible", t1); + } + } + + if (null == thread) { + Log.w(TAG, "ActivityThread instance is inaccessible"); + return; + } + + try { + final Handler handler = getHandler(thread); + if (null == handler || !(hooked = setFieldValue(handler, "mCallback", new CaughtCallback(handler)))) { + Log.i(TAG, "Hook ActivityThread.mH.mCallback failed"); + } + } catch (final Throwable t) { + Log.w(TAG, "Hook ActivityThread.mH.mCallback failed", t); + } + if(hooked) { + Log.i(TAG, "Hook ActivityThread.mH.mCallback success!"); + } + } + + private static Handler getHandler(final Object thread) { + Handler handler; + + if (null != (handler = getFieldValue(thread, "mH"))) { + return handler; + } + + if (null != (handler = invokeMethod(thread, "getHandler"))) { + return handler; + } + + try { + if (null != (handler = getFieldValue(thread, Class.forName("android.app.ActivityThread$H")))) { + return handler; + } + } catch (final ClassNotFoundException e) { + Log.w(TAG, "Main thread handler is inaccessible", e); + } + + return null; + } +} diff --git a/booster-transform-activity-thread/build.gradle b/booster-transform-activity-thread/build.gradle new file mode 100644 index 0000000..2c04676 --- /dev/null +++ b/booster-transform-activity-thread/build.gradle @@ -0,0 +1,9 @@ +apply from: '../gradle/booster.gradle' + +dependencies { + kapt "com.google.auto.service:auto-service:1.0-rc4" + implementation project(':booster-android-gradle-api') + implementation project(':booster-task-spi') + implementation project(':booster-transform-asm') + compileOnly 'com.android.tools.build:gradle:3.0.0' +} diff --git a/booster-transform-activity-thread/src/main/kotlin/com/didiglobal/booster/transform/activitythread/ActivityThreadTransformer.kt b/booster-transform-activity-thread/src/main/kotlin/com/didiglobal/booster/transform/activitythread/ActivityThreadTransformer.kt new file mode 100644 index 0000000..55fca58 --- /dev/null +++ b/booster-transform-activity-thread/src/main/kotlin/com/didiglobal/booster/transform/activitythread/ActivityThreadTransformer.kt @@ -0,0 +1,119 @@ +package com.didiglobal.booster.transform.activitythread + +import com.didiglobal.booster.kotlinx.file +import com.didiglobal.booster.kotlinx.touch +import com.didiglobal.booster.transform.ArtifactManager +import com.didiglobal.booster.transform.TransformContext +import com.didiglobal.booster.transform.activity.thread.Build +import com.didiglobal.booster.transform.asm.ClassTransformer +import com.didiglobal.booster.transform.asm.className +import com.didiglobal.booster.transform.asm.findAll +import com.didiglobal.booster.util.ComponentHandler +import com.google.auto.service.AutoService +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Opcodes.ACC_PUBLIC +import org.objectweb.asm.Opcodes.ALOAD +import org.objectweb.asm.Opcodes.ATHROW +import org.objectweb.asm.Opcodes.INVOKESPECIAL +import org.objectweb.asm.Opcodes.INVOKESTATIC +import org.objectweb.asm.Opcodes.RETURN +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.InsnList +import org.objectweb.asm.tree.InsnNode +import org.objectweb.asm.tree.MethodInsnNode +import org.objectweb.asm.tree.MethodNode +import org.objectweb.asm.tree.VarInsnNode +import java.io.PrintWriter +import javax.xml.parsers.SAXParserFactory + +/** + * @author neighbWang + */ +@AutoService(ClassTransformer::class) +class ActivityThreadTransformer : ClassTransformer { + + private lateinit var logger: PrintWriter + private val applications = mutableSetOf() + + override fun onPreTransform(context: TransformContext) { + val parser = SAXParserFactory.newInstance().newSAXParser() + context.artifacts.get(ArtifactManager.MERGED_MANIFESTS).forEach { manifest -> + val handler = ComponentHandler() + parser.parse(manifest, handler) + applications.addAll(handler.applications) + } + + this.logger = context.reportsDir.file(Build.ARTIFACT).file(context.name).file("report.txt").touch().printWriter() + } + + override fun onPostTransform(context: TransformContext) { + this.logger.close() + } + + override fun transform(context: TransformContext, klass: ClassNode): ClassNode { + if (!this.applications.contains(klass.className)) { + return klass + } + + val clinit = klass.methods?.find { + "${it.name}${it.desc}" == "()V" + } ?: klass.defaultClinit + + clinit.instructions?.findAll(RETURN, ATHROW)?.forEach { + clinit.instructions?.insertBefore(it, MethodInsnNode(INVOKESTATIC, ACTIVITY_THREAD_HOOKER, "hook", "()V", false)) + logger.println(" + $ACTIVITY_THREAD_HOOKER.hook()V before @${if (it.opcode == ATHROW) "athrow" else "return"}: ${klass.name}.${clinit.name}${clinit.desc}") + } + + + val init = klass.methods?.find { + "${it.name}${it.desc}" == "()V" + } ?: klass.defaultInit + + init.instructions?.findAll(RETURN, ATHROW)?.forEach { + init.instructions?.insertBefore(it, MethodInsnNode(INVOKESTATIC, ACTIVITY_THREAD_HOOKER, "hook", "()V", false)) + logger.println(" + $ACTIVITY_THREAD_HOOKER.hook()V before @${if (it.opcode == ATHROW) "athrow" else "return"}: ${klass.name}.${init.name}${init.desc}") + } + + val onCreate = klass.methods?.find { + "${it.name}${it.desc}" == "onCreate()V" + } ?: klass.defaultOnCreate + + onCreate.instructions?.findAll(RETURN, ATHROW)?.forEach { + onCreate.instructions?.insertBefore(it, MethodInsnNode(INVOKESTATIC, ACTIVITY_THREAD_HOOKER, "hook", "()V", false)) + logger.println(" + $ACTIVITY_THREAD_HOOKER.hook()V before @${if (it.opcode == ATHROW) "athrow" else "return"}: ${klass.name}.${onCreate.name}${onCreate.desc}") + } + return klass + } +} + +private val ClassNode.defaultClinit: MethodNode + get() = MethodNode(Opcodes.ACC_STATIC, "", "()V", null, null).apply { + maxStack = 1 + instructions.insert(InsnNode(RETURN)) + methods?.add(this) + } + +private val ClassNode.defaultInit: MethodNode + get() = MethodNode(ACC_PUBLIC, "", "()V", null, null).apply { + maxStack = 1 + instructions.insert(InsnList().apply { + add(VarInsnNode(ALOAD, 0)) + add(MethodInsnNode(INVOKESPECIAL, superName, name, desc, false)) + add(InsnNode(RETURN)) + }) + methods?.add(this) + } + + +private val ClassNode.defaultOnCreate: MethodNode + get() = MethodNode(ACC_PUBLIC, "onCreate", "()V", null, null).apply { + instructions?.add(InsnList().apply { + add(VarInsnNode(ALOAD, 0)) + add(MethodInsnNode(INVOKESPECIAL, superName, name, desc, false)) + add(InsnNode(RETURN)) + }) + maxStack = 1 + methods?.add(this) + } + +const val ACTIVITY_THREAD_HOOKER = "com/didiglobal/booster/instrument/ActivityThreadHooker" \ No newline at end of file diff --git a/booster-transform-activity-thread/src/main/kotlin/com/didiglobal/booster/transform/activitythread/ActivityThreadVarinatProcessor.kt b/booster-transform-activity-thread/src/main/kotlin/com/didiglobal/booster/transform/activitythread/ActivityThreadVarinatProcessor.kt new file mode 100644 index 0000000..ae8b9a6 --- /dev/null +++ b/booster-transform-activity-thread/src/main/kotlin/com/didiglobal/booster/transform/activitythread/ActivityThreadVarinatProcessor.kt @@ -0,0 +1,22 @@ +package com.didiglobal.booster.transform.activitythread + +import com.android.build.gradle.api.BaseVariant +import com.android.build.gradle.api.LibraryVariant +import com.didiglobal.booster.gradle.scope +import com.didiglobal.booster.task.spi.VariantProcessor +import com.didiglobal.booster.transform.activity.thread.Build +import com.google.auto.service.AutoService + +/** + * @author neighbWang + */ +@AutoService(VariantProcessor::class) +class ActivityThreadVarinatProcessor:VariantProcessor { + + override fun process(variant: BaseVariant) { + if (variant !is LibraryVariant) { + variant.scope.globalScope.project.dependencies.add("implementation", "${Build.GROUP}:booster-android-instrument-activity-thread:${Build.VERSION}") + } + } + +} \ No newline at end of file diff --git a/booster-transform-all/build.gradle b/booster-transform-all/build.gradle index 7bcbe2b..9b389c8 100644 --- a/booster-transform-all/build.gradle +++ b/booster-transform-all/build.gradle @@ -1,6 +1,7 @@ dependencies { compile project(':booster-transform-lint') compile project(':booster-transform-logcat') + compile project(':booster-transform-activity-thread') compile project(':booster-transform-finalizer-watchdog-daemon') compile project(':booster-transform-media-player') compile project(':booster-transform-res-check') diff --git a/settings.gradle b/settings.gradle index 79b831b..a2a507b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,7 @@ include ':booster-aapt2' include ':booster-android-api' include ':booster-android-instrument' +include ':booster-android-instrument-activity-thread' include ':booster-android-instrument-finalizer-watchdog-daemon' include ':booster-android-instrument-logcat' include ':booster-android-instrument-media-player' @@ -20,6 +21,7 @@ include ':booster-task-compression' include ':booster-task-dependency' include ':booster-task-permission' include ':booster-task-spi' +include ':booster-transform-activity-thread' include ':booster-transform-all' include ':booster-transform-asm' include ':booster-transform-finalizer-watchdog-daemon' -- GitLab