未验证 提交 57337616 编写于 作者: 金台 提交者: GitHub

Merge pull request #858 from Leifzhang/feature/compose_building

[feat]  调整compose building  方便后续插件调试 推远端
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
apply from: "../config.gradle"
def archives_type = rootProject.ext.publish_config["archives_type"]
def runType = rootProject.ext.publish_config["run_type"]
def dokitPluginVersion = rootProject.ext.publish_config["version"]
def kotlinVersion = rootProject.ext.android["kotlin_version"]
def gradlePluginVersion
if (archives_type == 2) {
gradlePluginVersion = rootProject.ext.android["jcenter_gradle_plugin_version"]
} else {
gradlePluginVersion = rootProject.ext.android["gradle_plugin_version"]
}
repositories {
maven {
url 'https://maven.aliyun.com/repository/central/'
}
maven {
url "https://oss.sonatype.org/content/groups/public"
}
google()
mavenCentral()
// maven { url 'http://artifactory.intra.xiaojukeji.com/artifactory/public' }
maven {
//本地插件地址
url uri('/Users/didi/project/dokit_repo')
}
}
dependencies {
classpath "com.android.tools.build:gradle:$gradlePluginVersion"
//jcenter
// classpath "com.novoda:bintray-release:0.9.2"
// MavenCentral
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.4.30"
if (runType == 1) {
classpath "io.github.didi.dokit:dokitx-plugin:$dokitPluginVersion"
}
//classpath "com.didiglobal.booster:booster-task-compression-pngquant:${rootProject.ext.android["booster_version"]}"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// classpath "com.didiglobal.booster:booster-gradle-plugin:3.3.1"
// classpath "com.didiglobal.booster:booster-task-analyser:3.3.1"
}
}
allprojects {
repositories {
maven {
url 'https://maven.aliyun.com/repository/central/'
}
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
\ No newline at end of file
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
apply from: '../upload.gradle'
//apply from: '../../upload.gradle'
sourceSets {
main {
......@@ -50,8 +50,8 @@ task deleteSource(type: Delete) {
*/
task copyPluginSource(type: Copy, dependsOn: deleteSource) {
println("===copySource start===")
from './../buildSrc/src/main/kotlin'
into './src/main/kotlin'
// from './../buildSrc/src/main/kotlin'
// into './src/main/kotlin'
println("===copySource end===")
}
......@@ -63,11 +63,15 @@ if (PUBLISH_ARCHIVES_TYPE == 0 || PUBLISH_ARCHIVES_TYPE == 1) {
}
repositories {
google()
// jcenter()
mavenCentral()
}
apply plugin: 'java-gradle-plugin'
gradlePlugin {
plugins {
version {
// 在 app 模块需要通过 id 引用这个插件
id = 'com.didi.dokit.debug'
// 实现这个插件的类的路径
implementationClass = 'com.didichuxing.doraemonkit.plugin.DoKitPlugin'
}
}
}
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
\ No newline at end of file
#Wed Jul 14 22:41:53 CST 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
include ':doraemonkit-plugin'
def runType = rootProject.ext.publish_config["run_type"]
if (runType == 0) {
// 引用插件
apply plugin: 'com.didi.dokit.debug'
apply plugin: 'com.didi.dokit'
// 这里引用正常库
dependencies {
//外部平台依赖
......@@ -9,7 +9,7 @@ if (runType == 0) {
debugImplementation project(":doraemonkit-mc")
debugImplementation project(":doraemonkit-ft")
debugImplementation project(":doraemonkit-weex")
debugImplementation project(":doraemonkit-rpc")
// debugImplementation project(":doraemonkit-rpc")
releaseImplementation project(":doraemonkit-no-op")
}
} else if (runType == 1) {
......
......@@ -13,13 +13,16 @@ buildscript {
}
repositories {
maven {
url 'https://maven.aliyun.com/repository/central/'
}
google()
mavenCentral()
jcenter()
maven {
url "https://oss.sonatype.org/content/groups/public"
}
maven { url 'http://artifactory.intra.xiaojukeji.com/artifactory/public' }
// maven { url 'http://artifactory.intra.xiaojukeji.com/artifactory/public' }
maven {
//本地插件地址
url uri('/Users/didi/project/dokit_repo')
......@@ -42,15 +45,23 @@ buildscript {
}
plugins {
// 这个 id 就是在 versionPlugin 文件夹下 build.gradle 文件内定义的id apply fasle 代表当前build不引入
id "com.didi.dokit.debug" apply false
}
allprojects {
repositories {
maven {
url 'https://maven.aliyun.com/repository/central/'
}
google()
mavenCentral()
jcenter()
maven {
url "https://oss.sonatype.org/content/groups/public"
}
maven { url 'http://artifactory.intra.xiaojukeji.com/artifactory/public' }
// maven { url 'http://artifactory.intra.xiaojukeji.com/artifactory/public' }
maven {
//本地插件地址
url uri('/Users/didi/project/dokit_repo')
......
buildscript {
apply from: "../config.gradle"
repositories {
mavenLocal()
mavenCentral()
google()
jcenter()
maven { url 'https://oss.sonatype.org/content/repositories/public/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
dependencies {
//classpath 'com.android.tools.build:gradle:3.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${rootProject.ext.android["kotlin_version"]}"
}
}
//apply plugin: 'java-library'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
/**
* 由于 buildSrc 的执行时机要早于任何一个 project,因此需要⾃⼰添加仓库
*/
repositories {
mavenLocal()
mavenCentral()
google()
jcenter()
maven { url 'https://oss.sonatype.org/content/repositories/public/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
sourceSets {
main {
java {
srcDirs += []
}
kotlin {
srcDirs += ['src/main/kotlin', 'src/main/java']
}
}
}
compileKotlin {
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8
}
def archives_type = rootProject.ext.publish_config["archives_type"]
def gradlePluginVersion
if (archives_type == 2) {
gradlePluginVersion = rootProject.ext.android["jcenter_gradle_plugin_version"]
} else {
gradlePluginVersion = rootProject.ext.android["gradle_plugin_version"]
}
dependencies {
compileOnly localGroovy()
compileOnly gradleApi()
implementation "com.android.tools.build:gradle:$gradlePluginVersion"
/* 👇👇👇👇 引用这两个模块 👇👇👇👇 */
api "com.didiglobal.booster:booster-api:${rootProject.ext.android["booster_version"]}"
api "com.didiglobal.booster:booster-transform-asm:${rootProject.ext.android["booster_version"]}"
}
\ No newline at end of file
#入口文件
#文件名为插件名 即 apply plugin "xxx"
implementation-class=com.didichuxing.doraemonkit.plugin.DoKitPlugin
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.android.build.gradle.api.BaseVariant
import com.android.dex.DexFormat
import com.android.dx.command.dexer.Main
import com.didiglobal.booster.kotlinx.NCPU
import com.didiglobal.booster.transform.TransformContext
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.*
import java.io.File
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/19-18:00
* 描 述:dokit 对象扩展
* 修订历史:
* ================================================
*/
fun MethodNode.isGetSetMethod(): Boolean {
var ignoreCount = 0
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
}
if (opcode != GETFIELD && opcode != GETSTATIC && opcode != H_GETFIELD && opcode != H_GETSTATIC && opcode != RETURN && opcode != ARETURN && opcode != DRETURN && opcode != FRETURN && opcode != LRETURN && opcode != IRETURN && opcode != PUTFIELD && opcode != PUTSTATIC && opcode != H_PUTFIELD && opcode != H_PUTSTATIC && opcode > SALOAD) {
if (name.equals("<init>") && opcode == INVOKESPECIAL) {
ignoreCount++
if (ignoreCount > 1) {
return false
}
continue
}
return false
}
}
return true
}
fun MethodNode.isSingleMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
if (-1 == opcode) {
continue
} else if (INVOKEVIRTUAL <= opcode && opcode <= INVOKEDYNAMIC) {
return false
}
}
return true
}
fun MethodNode.isEmptyMethod(): Boolean {
val iterator = instructions.iterator()
while (iterator.hasNext()) {
val insnNode = iterator.next()
val opcode = insnNode.opcode
return if (-1 == opcode) {
continue
} else {
false
}
}
return true
}
fun InsnList.getMethodExitInsnNodes(): Sequence<InsnNode>? {
return this.iterator()?.asSequence()?.filterIsInstance(InsnNode::class.java)?.filter {
it.opcode == RETURN ||
it.opcode == IRETURN ||
it.opcode == FRETURN ||
it.opcode == ARETURN ||
it.opcode == LRETURN ||
it.opcode == DRETURN ||
it.opcode == ATHROW
}
}
fun BaseVariant.isRelease(): Boolean {
if (this.name.contains("release") || this.name.contains("Release")) {
return true
}
return false
}
fun TransformContext.isRelease(): Boolean {
if (this.name.contains("release") || this.name.contains("Release")) {
return true
}
return false
}
fun String.println() {
if (DoKitExtUtil.dokitLogSwitchOpen()) {
println("[dokit plugin]===>$this")
}
}
fun File.lastPath(): String {
return this.path.split("/").last()
}
val MethodInsnNode.ownerClassName: String
get() = owner.replace('/', '.')
val ClassNode.formatSuperName: String
get() = superName.replace('/', '.')
internal fun File.dex(output: File, api: Int = DexFormat.API_NO_EXTENDED_OPCODES): Int {
val args = Main.Arguments().apply {
numThreads = NCPU
debug = true
warnings = true
emptyOk = true
multiDex = true
jarOutput = true
optimize = false
minSdkVersion = api
fileNames = arrayOf(output.canonicalPath)
outName = canonicalPath
}
return try {
Main.run(args)
} catch (t: Throwable) {
t.printStackTrace()
-1
}
}
package com.didichuxing.doraemonkit.plugin
import com.didichuxing.doraemonkit.plugin.extension.CommExt
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import org.gradle.api.Project
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/3/24-14:58
* 描 述:
* 修订历史:
* ================================================
*/
object DoKitExtUtil {
/**
* 是否包含dokitx-rpc模块
*/
var HAS_DOKIT_RPC_MODULE = false
//private var mApplicationId: String = ""
/**
* 三方库版本信息
*/
// val THIRD_LIB_INFOS = mutableMapOf<String, ThirdLibInfo>()
val THIRD_LIB_INFOS = mutableListOf<ThirdLibInfo>()
/**
* dokit 插件开关 字段权限必须为public 否则无法进行赋值
*/
var DOKIT_PLUGIN_SWITCH = true
var DOKIT_LOG_SWITCH = false
/**
* 默认函数调用为5级
*/
var STACK_METHOD_LEVEL = 5
/**
* 自定义webview全限定名
*/
var WEBVIEW_CLASS_NAME: String = ""
/**
* 慢函数默认关闭
*/
var SLOW_METHOD_SWITCH = false
/**
* 三方库信息开关
*/
var THIRD_LIBINFO_SWITCH = true
/**
* 慢函数策略 默认为函数调用栈策略
*/
var SLOW_METHOD_STRATEGY = SlowMethodExt.STRATEGY_STACK
private val applications: MutableSet<String> = mutableSetOf()
/**
* app的packageName
*/
private var appPackageName: String = ""
var commExt = CommExt()
private set
val slowMethodExt = SlowMethodExt()
fun dokitPluginSwitchOpen(): Boolean {
return DOKIT_PLUGIN_SWITCH
}
fun dokitLogSwitchOpen(): Boolean {
return DOKIT_LOG_SWITCH
}
fun dokitSlowMethodSwitchOpen(): Boolean {
return SLOW_METHOD_SWITCH
}
/**
* 初始化
*
* @param dokitEx dokitExtension
* @param appExtension appExtension
*/
fun init(dokitEx: DoKitExt) {
//设置普通的配置
commExt = dokitEx.comm
//slowMethodExt.strategy = dokitEx.slowMethod.strategy
//slowMethodExt.methodSwitch = dokitEx.slowMethod.methodSwitch
/**
* ============慢函数普通策略的配置 start==========
*/
slowMethodExt.normalMethod.thresholdTime = dokitEx.slowMethod.normalMethod.thresholdTime
//设置慢函数普通策略插装包名
slowMethodExt.normalMethod.packageNames.clear()
for (packageName in dokitEx.slowMethod.normalMethod.packageNames) {
slowMethodExt.normalMethod.packageNames.add(packageName)
}
//添加默认的包名
if (appPackageName.isNotEmpty()) {
if (slowMethodExt.normalMethod.packageNames.isEmpty()) {
slowMethodExt.normalMethod.packageNames.add(appPackageName)
}
}
//设置慢函数普通策略插装包名黑名单
slowMethodExt.normalMethod.methodBlacklist.clear()
for (blackStr in dokitEx.slowMethod.normalMethod.methodBlacklist) {
slowMethodExt.normalMethod.methodBlacklist.add(blackStr)
}
/**
* ============慢函数普通策略的配置end==========
*/
/**
* ============慢函数stack策略的配置 start==========
*/
slowMethodExt.stackMethod.thresholdTime = dokitEx.slowMethod.stackMethod.thresholdTime
slowMethodExt.stackMethod.enterMethods.clear()
//添加默认的入口函数
for (application in applications) {
val attachBaseContextMethodName = "$application.attachBaseContext"
val onCreateMethodName = "$application.onCreate"
slowMethodExt.stackMethod.enterMethods.add(attachBaseContextMethodName)
slowMethodExt.stackMethod.enterMethods.add(onCreateMethodName)
}
for (methodName in dokitEx.slowMethod.stackMethod.enterMethods) {
slowMethodExt.stackMethod.enterMethods.add(methodName)
}
//设置慢函数调用栈策略插装包名黑名单
slowMethodExt.stackMethod.methodBlacklist.clear()
for (blackStr in dokitEx.slowMethod.stackMethod.methodBlacklist) {
slowMethodExt.stackMethod.methodBlacklist.add(blackStr)
}
/**
* ============慢函数stack策略的配置 end==========
*/
}
fun setApplications(applications: MutableSet<String>) {
if (applications.isEmpty()) {
return
}
this.applications.clear()
for (application in applications) {
this.applications.add(application)
}
}
/**
* 设置packageName
*/
fun setAppPackageName(packageName: String) {
appPackageName = packageName
}
fun ignorePackageNames(className: String): Boolean {
//命中白名单返回false
for (packageName in whitePackageNames) {
if (className.startsWith(packageName, true)) {
return false
}
}
//命中黑名单返回true
for (packageName in blackPackageNames) {
if (className.startsWith(packageName, true)) {
return true
}
}
return false
}
/**
* 白名单
*/
private val whitePackageNames = arrayOf(
"com.didichuxing.doraemonkit.DoraemonKit",
"com.didichuxing.doraemonkit.DoKit",
"com.didichuxing.doraemonkit.DoKitReal"
)
/**
* 黑名单
*/
private val blackPackageNames = arrayOf(
"com.didichuxing.doraemonkit.",
"kotlin.",
"java.",
"android.",
"androidx."
)
fun log(
tag: String,
className: String,
methodName: String,
access: Int,
desc: String,
signature: String,
thresholdTime: Int
) {
if (DOKIT_LOG_SWITCH) {
println("$tag===matched====> className===$className methodName===$methodName access===$access desc===$desc signature===$signature thresholdTime===$thresholdTime")
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.android.build.gradle.AppExtension
import com.android.build.gradle.LibraryExtension
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.processor.DoKitPluginConfigProcessor
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
import com.didichuxing.doraemonkit.plugin.transform.*
import com.didiglobal.booster.gradle.GTE_V3_4
import com.didiglobal.booster.gradle.dependencies
import com.didiglobal.booster.gradle.getAndroid
import com.didiglobal.booster.gradle.getProperty
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.result.ResolvedArtifactResult
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-10:01
* 描 述:
* 修订历史:
* ================================================
*/
class DoKitPlugin : Plugin<Project> {
override fun apply(project: Project) {
//创建指定扩展 并将project 传入构造函数
val doKitExt = project.extensions.create("dokitExt", DoKitExt::class.java)
project.gradle.addListener(DoKitTransformTaskExecutionListener(project))
//println("project.plugins===>${project.plugins}")
/**
* when 也可以用来取代 if-else if链。
* 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:
*/
/**
* 作用域函数:let、run、with、apply 以及 also
* 它们的唯一目的是在对象的上下文中执行代码块
* 由于作用域函数本质上都非常相似,因此了解它们之间的区别很重要。每个作用域函数之间有两个主要区别:
* 引用上下文对象的方式:
* 作为 lambda 表达式的接收者(this)或者作为 lambda 表达式的参数(it)
* run、with 以及 apply 通过关键字 this 引用上下文对象
* let 及 also 将上下文对象作为 lambda 表达式参数
*
* 返回值:
* apply 及 also 返回上下文对象。
* let、run 及 with 返回 lambda 表达式结果.
*/
/**
* 函数 对象引用 返回值 是否是扩展函数
* let it Lambda 表达式结果 是
* run this Lambda 表达式结果 是
* run - Lambda 表达式结果 不是:调用无需上下文对象
* with this Lambda 表达式结果 不是:把上下文对象当做参数
* apply this 上下文对象 是
* also it 上下文对象 是
*/
/**
*对一个非空(non-null)对象执行 lambda 表达式:let
*将表达式作为变量引入为局部作用域中:let
*对象配置:apply
*对象配置并且计算结果:run
*在需要表达式的地方运行语句:非扩展的 run
*附加效果:also
*一个对象的一组函数调用:with
*/
when {
project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.dynamic-feature") -> {
if (!isReleaseTask(project)) {
project.getAndroid<AppExtension>().let { androidExt ->
val pluginSwitch = project.getProperty("DOKIT_PLUGIN_SWITCH", true)
val logSwitch = project.getProperty("DOKIT_LOG_SWITCH", false)
val slowMethodSwitch = project.getProperty("DOKIT_METHOD_SWITCH", false)
val slowMethodStrategy = project.getProperty("DOKIT_METHOD_STRATEGY", 0)
val methodStackLevel = project.getProperty("DOKIT_METHOD_STACK_LEVEL", 5)
val webViewClassName = project.getProperty("DOKIT_WEBVIEW_CLASS_NAME", "")
val thirdLibInfo = project.getProperty("DOKIT_THIRD_LIB_SWITCH", true)
DoKitExtUtil.DOKIT_PLUGIN_SWITCH = pluginSwitch
DoKitExtUtil.DOKIT_LOG_SWITCH = logSwitch
DoKitExtUtil.SLOW_METHOD_SWITCH = slowMethodSwitch
DoKitExtUtil.SLOW_METHOD_STRATEGY = slowMethodStrategy
DoKitExtUtil.STACK_METHOD_LEVEL = methodStackLevel
DoKitExtUtil.WEBVIEW_CLASS_NAME = webViewClassName
DoKitExtUtil.THIRD_LIBINFO_SWITCH = thirdLibInfo
"application module ${project.name} is executing...".println()
MethodStackNodeUtil.METHOD_STACK_KEYS.clear()
if (DoKitExtUtil.DOKIT_PLUGIN_SWITCH) {
//注册transform
androidExt.registerTransform(commNewInstance(project))
if (slowMethodSwitch && slowMethodStrategy == SlowMethodExt.STRATEGY_STACK) {
MethodStackNodeUtil.METHOD_STACK_KEYS.add(0, mutableSetOf<String>())
val methodStackRange = 1 until methodStackLevel
if (methodStackLevel > 1) {
for (index in methodStackRange) {
MethodStackNodeUtil.METHOD_STACK_KEYS.add(
index,
mutableSetOf<String>()
)
androidExt.registerTransform(
dependNewInstance(project, index)
)
}
}
}
}
//项目评估完毕回调
// project.afterEvaluate { project ->
// "===afterEvaluate===".println()
// androidExt.applicationVariants.forEach { variant ->
// DoKitPluginConfigProcessor(project).process(variant)
// }
// }
/**
* 所有项目的build.gradle执行完毕
* wiki:https://juejin.im/post/6844903607679057934
*
* **/
project.gradle.projectsEvaluated {
"===projectsEvaluated===".println()
androidExt.applicationVariants.forEach { variant ->
DoKitPluginConfigProcessor(project).process(variant)
}
}
//task依赖关系图建立完毕
project.gradle.taskGraph.whenReady {
"===taskGraph.whenReady===".println()
}
}
}
}
project.plugins.hasPlugin("com.android.library") -> {
if (!isReleaseTask(project)) {
project.getAndroid<LibraryExtension>().let { libraryExt ->
"library module ${project.name} is executing...".println()
if (DoKitExtUtil.DOKIT_PLUGIN_SWITCH) {
libraryExt.registerTransform(commNewInstance(project))
}
project.afterEvaluate {
libraryExt.libraryVariants.forEach { variant ->
DoKitPluginConfigProcessor(project).process(variant)
}
}
}
}
}
}
}
private fun isReleaseTask(project: Project): Boolean {
return project.gradle.startParameter.taskNames.any {
it.contains("release") || it.contains("Release")
}
}
private fun commNewInstance(project: Project): DoKitBaseTransform = when {
GTE_V3_4 -> DoKitCommTransformV34(project)
else -> DoKitCommTransform(project)
}
private fun dependNewInstance(project: Project, index: Int): DoKitBaseTransform = when {
GTE_V3_4 -> DoKitDependTransformV34(project, index)
else -> DoKitDependTransform(project, index)
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import java.io.File
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/10/20-15:13
* 描 述:
* 修订历史:
* ================================================
*/
object DoKitPluginUtil {
const val BYTE = 1
const val KB = 1024
const val MB = 1048576
const val GB = 1073741824
fun fileSize(file: File, precision: Int): String? {
if (!file.isFile) {
return "0kb"
}
if (file.isDirectory) {
return "0kb"
}
val fileLength = file.length()
return byte2FitMemorySize(fileLength, precision)
}
private fun byte2FitMemorySize(byteSize: Long, precision: Int): String? {
require(precision >= 0) { "precision shouldn't be less than zero!" }
return if (byteSize < 0) {
throw IllegalArgumentException("byteSize shouldn't be less than zero!")
} else if (byteSize < KB) {
String.format("%." + precision + "fB", byteSize.toDouble())
} else if (byteSize < MB) {
String.format("%." + precision + "fKB", byteSize.toDouble() / KB)
} else if (byteSize < GB) {
String.format("%." + precision + "fMB", byteSize.toDouble() / MB)
} else {
String.format("%." + precision + "fGB", byteSize.toDouble() / GB)
}
}
private fun getNextChunk(version: String, n: Int, p: Int): Pair<Int, Int> {
// if pointer is set to the end of string
// return 0
if (p > n - 1) {
return Pair(0, p)
}
// find the end of chunk
var i = 0
var pEnd = p
while (pEnd < n && version[pEnd].equals(".")) {
++pEnd
}
// retrieve the chunk
i = if (pEnd != n - 1) {
version.substring(p, pEnd).toInt()
} else {
version.substring(p, n).toInt()
}
// find the beginning of next chunk
val q = pEnd + 1
return Pair(i, q)
}
/**
* 比较version的大小
*/
fun compareVersion(version1: String, version2: String): Int {
var p1 = 0
var p2 = 0
val n1 = version1.length
val n2 = version2.length
var i1: Int
var i2: Int
var pair: Pair<Int, Int>
while (p1 < n1 || p2 < n2) {
pair = getNextChunk(version1, n1, p1)
i1 = pair.first
p1 = pair.second
pair = getNextChunk(version2, n2, p2)
i2 = pair.first
p2 = pair.second
if (i1 != i2) {
return if (i1 > i2) {
1
} else {
-1
}
}
}
return 0
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.android.build.api.transform.*
import com.android.build.api.transform.Status.*
import com.android.dex.DexFormat
import com.android.dx.command.dexer.Main
import com.didichuxing.doraemonkit.plugin.transform.DoKitBaseTransform
import com.didiglobal.booster.gradle.*
import com.didiglobal.booster.kotlinx.NCPU
import com.didiglobal.booster.kotlinx.file
import com.didiglobal.booster.kotlinx.green
import com.didiglobal.booster.kotlinx.red
import com.didiglobal.booster.transform.AbstractKlassPool
import com.didiglobal.booster.transform.ArtifactManager
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.artifacts
import com.didiglobal.booster.transform.util.transform
import java.io.File
import java.net.URI
import java.util.concurrent.*
/**
* Represents a delegate of TransformInvocation
*
* @author johnsonlee
*/
internal class DoKitTransformInvocation(
private val delegate: TransformInvocation,
internal val transform: DoKitBaseTransform
) : TransformInvocation by delegate, TransformContext, ArtifactManager {
private val project = transform.project
private val outputs = CopyOnWriteArrayList<File>()
override val name: String = delegate.context.variantName
override val projectDir: File = project.projectDir
override val buildDir: File = project.buildDir
override val temporaryDir: File = delegate.context.temporaryDir
override val reportsDir: File = File(buildDir, "reports").also { it.mkdirs() }
override val bootClasspath = delegate.bootClasspath
override val compileClasspath = delegate.compileClasspath
override val runtimeClasspath = delegate.runtimeClasspath
override val artifacts = this
override val dependencies: Collection<String> by lazy {
ResolvedArtifactResults(variant).map {
it.id.displayName
}
}
override val klassPool: AbstractKlassPool =
object : AbstractKlassPool(compileClasspath, transform.bootKlassPool) {}
override val applicationId = delegate.applicationId
override val originalApplicationId = delegate.originalApplicationId
override val isDebuggable = variant.buildType.isDebuggable
override val isDataBindingEnabled = delegate.isDataBindingEnabled
override fun hasProperty(name: String) = project.hasProperty(name)
override fun <T> getProperty(name: String, default: T): T = project.getProperty(name, default)
override fun get(type: String) = variant.artifacts.get(type)
internal fun doFullTransform() = doTransform(this::transformFully)
internal fun doIncrementalTransform() = doTransform(this::transformIncrementally)
private fun onPreTransform() {
transform.transformers.forEach {
it.onPreTransform(this)
}
}
private fun onPostTransform() {
transform.transformers.forEach {
it.onPostTransform(this)
}
}
private fun doTransform(block: (ExecutorService) -> Iterable<Future<*>>) {
this.outputs.clear()
this.onPreTransform()
val executor = Executors.newFixedThreadPool(NCPU)
try {
block(executor).forEach {
it.get()
}
} finally {
executor.shutdown()
executor.awaitTermination(1, TimeUnit.HOURS)
}
this.onPostTransform()
if (transform.verifyEnabled) {
this.doVerify()
}
}
private fun transformFully(executor: ExecutorService) = this.inputs.map {
it.jarInputs + it.directoryInputs
}.flatten().map { input ->
executor.submit {
val format = if (input is DirectoryInput) Format.DIRECTORY else Format.JAR
outputProvider?.let { provider ->
project.logger.info("Transforming ${input.file}")
input.transform(
provider.getContentLocation(
input.name,
input.contentTypes,
input.scopes,
format
)
)
}
}
}
private fun transformIncrementally(executor: ExecutorService) = this.inputs.map { input ->
input.jarInputs.filter { it.status != NOTCHANGED }.map { jarInput ->
executor.submit {
doIncrementalTransform(jarInput)
}
} + input.directoryInputs.filter { it.changedFiles.isNotEmpty() }.map { dirInput ->
val base = dirInput.file.toURI()
executor.submit {
doIncrementalTransform(dirInput, base)
}
}
}.flatten()
@Suppress("NON_EXHAUSTIVE_WHEN")
private fun doIncrementalTransform(jarInput: JarInput) {
when (jarInput.status) {
REMOVED -> jarInput.file.delete()
CHANGED, ADDED -> {
project.logger.info("Transforming ${jarInput.file}")
outputProvider?.let { provider ->
jarInput.transform(
provider.getContentLocation(
jarInput.name,
jarInput.contentTypes,
jarInput.scopes,
Format.JAR
)
)
}
}
}
}
@Suppress("NON_EXHAUSTIVE_WHEN")
private fun doIncrementalTransform(dirInput: DirectoryInput, base: URI) {
dirInput.changedFiles.forEach { (file, status) ->
when (status) {
REMOVED -> {
project.logger.info("Deleting $file")
outputProvider?.let { provider ->
provider.getContentLocation(
dirInput.name,
dirInput.contentTypes,
dirInput.scopes,
Format.DIRECTORY
).parentFile.listFiles()?.asSequence()
?.filter { it.isDirectory }
?.map { File(it, dirInput.file.toURI().relativize(file.toURI()).path) }
?.filter { it.exists() }
?.forEach { it.delete() }
}
file.delete()
}
ADDED, CHANGED -> {
project.logger.info("Transforming $file")
outputProvider?.let { provider ->
val root = provider.getContentLocation(
dirInput.name,
dirInput.contentTypes,
dirInput.scopes,
Format.DIRECTORY
)
val output = File(root, base.relativize(file.toURI()).path)
outputs += output
file.transform(output) { bytecode ->
bytecode.transform()
}
}
}
}
}
}
private fun doVerify() {
outputs.sortedBy(File::nameWithoutExtension).forEach { output ->
val out = temporaryDir.file(output.name)
val rc = out.dex(
output,
variant.extension.defaultConfig.targetSdkVersion?.apiLevel
?: DexFormat.API_NO_EXTENDED_OPCODES
)
println("${if (rc != 0) red("") else green("")} $output")
out.deleteRecursively()
}
}
private fun QualifiedContent.transform(output: File) {
outputs += output
this.file.transform(output) { bytecode ->
bytecode.transform()
}
}
private fun ByteArray.transform(): ByteArray {
return transform.transformers.fold(this) { bytes, transformer ->
transformer.transform(this@DoKitTransformInvocation, bytes)
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.android.build.gradle.internal.pipeline.TransformTask
import com.didiglobal.booster.kotlinx.call
import com.didiglobal.booster.kotlinx.get
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.execution.TaskExecutionAdapter
/**
* @author neighbWang
*/
class DoKitTransformTaskExecutionListener(private val project: Project) : TaskExecutionAdapter() {
override fun beforeExecute(task: Task) {
task.takeIf {
it.project == project && it is TransformTask && it.transform.scopes.isNotEmpty()
}?.run {
task["outputStream"]?.call<Unit>("init")
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
import com.didiglobal.booster.task.spi.VariantProcessor
import com.didiglobal.booster.transform.Transformer
import org.gradle.api.Project
import java.net.URL
import java.nio.charset.StandardCharsets
import java.util.ServiceConfigurationError
//internal interface ServiceLoader<T> {
// fun load(vararg args: Any): List<T>
//}
//internal class ServiceLoaderFactory<T>(private val classLoader: ClassLoader, private val service: Class<T>) {
//
// fun newServiceLoader(vararg types: Class<*>) = object : ServiceLoader<T> {
//
// @Suppress("UNCHECKED_CAST")
// override fun load(vararg args: Any) = classLoader.getResources("META-INF/services/${service.name}")?.asSequence()?.map(::parse)?.flatten()?.toSet()?.map { provider ->
// try {
// val providerClass = Class.forName(provider, false, classLoader)
// if (!service.isAssignableFrom(providerClass)) {
// throw ServiceConfigurationError("Provider $provider not a subtype")
// }
//
// try {
// providerClass.getConstructor(*types).newInstance(*args) as T
// } catch (e: NoSuchMethodException) {
// providerClass.newInstance() as T
// }
// } catch (e: ClassNotFoundException) {
// throw ServiceConfigurationError("Provider $provider not found")
// }
// } ?: emptyList()
//
// }
//
//}
//
//internal inline fun <reified T> newServiceLoader(classLoader: ClassLoader, vararg types: Class<*>) = ServiceLoaderFactory(classLoader, T::class.java).newServiceLoader(*types)
/**
* Load [Transformer]s with the specified [classLoader]
*/
//@Throws(ServiceConfigurationError::class)
//internal fun loadTransformers(classLoader: ClassLoader) = newServiceLoader<Transformer>(classLoader, ClassLoader::class.java).load(classLoader)
/**
* Load [VariantProcessor]s with the specified [classLoader]
*/
//@Throws(ServiceConfigurationError::class)
//internal fun loadVariantProcessors(project: Project) = newServiceLoader<VariantProcessor>(project.buildscript.classLoader, Project::class.java).load(project)
@Throws(ServiceConfigurationError::class)
private fun parse(u: URL) = try {
u.openStream().bufferedReader(StandardCharsets.UTF_8).readLines().filter {
it.isNotEmpty() && it.isNotBlank() && !it.startsWith('#')
}.map(String::trim).filter(::isJavaClassName)
} catch (e: Throwable) {
emptyList<String>()
}
private fun isJavaClassName(text: String): Boolean {
if (!Character.isJavaIdentifierStart(text[0])) {
throw ServiceConfigurationError("Illegal provider-class name: $text")
}
for (i in 1 until text.length) {
val cp = text.codePointAt(i)
if (!Character.isJavaIdentifierPart(cp) && cp != '.'.toInt()) {
throw ServiceConfigurationError("Illegal provider-class name: $text")
}
}
return true
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/10/20-14:50
* 描 述:
* 修订历史:
* ================================================
*/
//data class ThirdLibInfo(
// val groupId: String,
// val artifactId: String,
// val version: String,
// val fileName: String,
// val fileSize: Long,
// val variant: String
//)
data class ThirdLibInfo(val variant: String, val fileSize: Long)
package com.didichuxing.doraemonkit.plugin.asmtransformer
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.Transformer
import com.didiglobal.booster.transform.asm.ClassTransformer
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.tree.ClassNode
import java.lang.management.ManagementFactory
import java.lang.management.ThreadMXBean
import java.util.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/21-16:44
* 描 述:
* 修订历史:
* ================================================
*/
open class BaseDoKitAsmTransformer : Transformer {
private val threadMxBean = ManagementFactory.getThreadMXBean()
private val durations = mutableMapOf<ClassTransformer, Long>()
private val classLoader: ClassLoader
internal val transformers: Iterable<ClassTransformer>
constructor() : this(Thread.currentThread().contextClassLoader)
constructor(classLoader: ClassLoader = Thread.currentThread().contextClassLoader) : this(
ServiceLoader.load(ClassTransformer::class.java, classLoader).sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}, classLoader
)
constructor(
transformers: Iterable<ClassTransformer>,
classLoader: ClassLoader = Thread.currentThread().contextClassLoader
) {
this.classLoader = classLoader
this.transformers = transformers
}
override fun onPreTransform(context: TransformContext) {
this.transformers.forEach { transformer ->
this.threadMxBean.sumCpuTime(transformer) {
transformer.onPreTransform(context)
}
}
}
override fun transform(context: TransformContext, bytecode: ByteArray): ByteArray {
return ClassWriter(ClassWriter.COMPUTE_MAXS).also { writer ->
this.transformers.fold(ClassNode().also { klass ->
ClassReader(bytecode).accept(klass, 0)
}) { klass, transformer ->
this.threadMxBean.sumCpuTime(transformer) {
transformer.transform(context, klass)
}
}.accept(writer)
}.toByteArray()
}
override fun onPostTransform(context: TransformContext) {
this.transformers.forEach { transformer ->
this.threadMxBean.sumCpuTime(transformer) {
transformer.onPostTransform(context)
}
}
val w1 = this.durations.keys.map {
it.javaClass.name.length
}.max() ?: 20
this.durations.forEach { (transformer, ns) ->
println("${transformer.javaClass.name.padEnd(w1 + 1)}: ${ns / 1000000} ms")
}
}
private fun <R> ThreadMXBean.sumCpuTime(transformer: ClassTransformer, action: () -> R): R {
val ct0 = this.currentThreadCpuTime
val result = action()
val ct1 = this.currentThreadCpuTime
durations[transformer] = durations.getOrDefault(transformer, 0) + (ct1 - ct0)
return result
}
}
package com.didichuxing.doraemonkit.plugin.asmtransformer
import com.didiglobal.booster.transform.asm.ClassTransformer
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/21-16:44
* 描 述:
* 修订历史:
* ================================================
*/
class DoKitAsmTransformer(transformers: Iterable<ClassTransformer>) :
BaseDoKitAsmTransformer(transformers)
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didichuxing.doraemonkit.plugin.println
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.asm.className
import org.objectweb.asm.tree.ClassNode
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2021/5/12-18:06
* 描 述:
* 修订历史:
* ================================================
*/
open class AbsClassTransformer : ClassTransformer {
fun onCommInterceptor(context: TransformContext, klass: ClassNode): Boolean {
if (context.isRelease()) {
return true
}
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return true
}
//过滤kotlin module-info
if (klass.className == "module-info") {
return true
}
return false
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.*
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.asm.className
import com.google.auto.service.AutoService
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.VarInsnNode
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
//@Priority(1)
//@AutoService(ClassTransformer::class)
class BigImgTransformer : AbsClassTransformer() {
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (onCommInterceptor(context, klass)) {
return klass
}
if (!DoKitExtUtil.commExt.bigImgSwitch) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
val className = klass.className
//glide
if (className == "com.bumptech.glide.request.SingleRequest") {
klass.methods.find { methodNode ->
(methodNode.name == "init" || methodNode.name == "<init>") && methodNode.desc != null
}.let { methodNode ->
//函数结束的地方插入
methodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
"${context.projectDir.lastPath()}->hook glide succeed: ${className}_${methodNode.name}_${methodNode.desc}".println()
methodNode.instructions?.insertBefore(it, createGlideInsnList())
}
}
}
//picasso
if (className == "com.squareup.picasso.Request") {
klass.methods.find { methodNode ->
methodNode.name == "<init>" && methodNode.desc != null
}.let { methodNode ->
//函数结束的地方插入
methodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
"${context.projectDir.lastPath()}->hook picasso succeed: ${className}_${methodNode.name}_${methodNode.desc}".println()
methodNode.instructions?.insertBefore(it, createPicassoInsnList())
}
}
}
//Fresco
if (className == "com.facebook.imagepipeline.request.ImageRequest") {
klass.methods.find { methodNode ->
methodNode.name == "<init>" && methodNode.desc != null
}.let { methodNode ->
"${context.projectDir.lastPath()}->hook Fresco succeed: ${className}_${methodNode?.name}_${methodNode?.desc}".println()
//函数开始的地方插入
methodNode?.instructions?.insert(createFrescoInsnList())
}
}
//ImageLoader
if (className == "com.nostra13.universalimageloader.core.ImageLoadingInfo") {
klass.methods.find { methodNode ->
methodNode.name == "<init>" && methodNode.desc != null
}.let { methodNode ->
"${context.projectDir.lastPath()}->hook ImageLoader succeed: ${className}_${methodNode?.name}_${methodNode?.desc}".println()
methodNode?.instructions?.insert(createImageLoaderInsnList())
}
}
return klass
}
/**
* 创建Glide Aop代码指令
*/
private fun createGlideInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 0))
add(
MethodInsnNode(
INVOKESTATIC,
"com/didichuxing/doraemonkit/aop/bigimg/glide/GlideHook",
"proxy",
"(Ljava/lang/Object;)V",
false
)
)
this
}
}
/**
* 创建Picasso Aop代码指令
*/
private fun createPicassoInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 0))
add(
MethodInsnNode(
INVOKESTATIC,
"com/didichuxing/doraemonkit/aop/bigimg/picasso/PicassoHook",
"proxy",
"(Ljava/lang/Object;)V",
false
)
)
this
}
}
/**
* 创建Fresco Aop代码指令
*/
private fun createFrescoInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 1))
add(VarInsnNode(ALOAD, 1))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/facebook/imagepipeline/request/ImageRequestBuilder",
"getSourceUri",
"()Landroid/net/Uri;",
false
)
)
add(VarInsnNode(ALOAD, 1))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/facebook/imagepipeline/request/ImageRequestBuilder",
"getPostprocessor",
"()Lcom/facebook/imagepipeline/request/Postprocessor;",
false
)
)
add(
MethodInsnNode(
INVOKESTATIC,
"com/didichuxing/doraemonkit/aop/bigimg/fresco/FrescoHook",
"proxy",
"(Landroid/net/Uri;Lcom/facebook/imagepipeline/request/Postprocessor;)Lcom/facebook/imagepipeline/request/Postprocessor;",
false
)
)
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/facebook/imagepipeline/request/ImageRequestBuilder",
"setPostprocessor",
"(Lcom/facebook/imagepipeline/request/Postprocessor;)Lcom/facebook/imagepipeline/request/ImageRequestBuilder;",
false
)
)
this
}
}
/**
* 创建ImageLoader Aop代码指令
*/
private fun createImageLoaderInsnList(): InsnList {
return with(InsnList()) {
add(VarInsnNode(ALOAD, 6))
add(
MethodInsnNode(
INVOKESTATIC,
"com/didichuxing/doraemonkit/aop/bigimg/imageloader/ImageLoaderHook",
"proxy",
"(Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;)Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;",
false
)
)
add(VarInsnNode(ASTORE, 6))
this
}
}
}
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNode
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.asm.asIterable
import com.didiglobal.booster.transform.asm.className
import com.google.auto.service.AutoService
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:入口函数 慢函数调用栈 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:不要指定自动注入 需要手动在DoKitAsmTransformer中通过配置创建
* 原理:transform()方法的调用是无序的 原因:哪一个class会先被transformer执行是不确定的 但是每一个class被transformer执行顺序是遵循transformer的Priority规则的
* ================================================
*/
//@Priority(3)
//@AutoService(ClassTransformer::class)
class EnterMethodStackTransformer : AbsClassTransformer() {
private val thresholdTime = DoKitExtUtil.slowMethodExt.stackMethod.thresholdTime
private val level = 0
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (onCommInterceptor(context, klass)) {
return klass
}
if (!DoKitExtUtil.dokitSlowMethodSwitchOpen()) {
return klass
}
if (DoKitExtUtil.SLOW_METHOD_STRATEGY == SlowMethodExt.STRATEGY_NORMAL) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
//默认为Application onCreate 和attachBaseContext
val enterMethods = DoKitExtUtil.slowMethodExt.stackMethod.enterMethods
if (enterMethods.isNotEmpty()) {
enterMethods.forEach { enterMethodName ->
klass.methods.forEach { methodNode ->
val allMethodName = "${klass.className}.${methodNode.name}"
if (allMethodName == enterMethodName) {
"${context.projectDir.lastPath()}->level-->$level mathched enterMethod===>$allMethodName".println()
operateMethodInsn(klass, methodNode)
}
}
}
}
return klass
}
private fun operateMethodInsn(klass: ClassNode, methodNode: MethodNode) {
//读取全是函数调用的指令
methodNode.instructions.asIterable().filterIsInstance(MethodInsnNode::class.java)
.filter { methodInsnNode ->
methodInsnNode.name != "<init>"
}.forEach { methodInsnNode ->
val methodStackNode = MethodStackNode(
level,
methodInsnNode.ownerClassName,
methodInsnNode.name,
methodInsnNode.desc,
klass.className,
methodNode.name,
methodNode.desc
)
MethodStackNodeUtil.addMethodStackNode(level, methodStackNode)
}
//函数出入口插入耗时统计代码
//方法入口插入
methodNode.instructions.insert(
createMethodEnterInsnList(
level,
klass.className,
methodNode.name,
methodNode.desc,
methodNode.access
)
)
//方法出口插入
methodNode.instructions.getMethodExitInsnNodes()?.forEach { methodExitInsnNode ->
methodNode.instructions.insertBefore(
methodExitInsnNode,
createMethodExitInsnList(
level,
klass.className,
methodNode.name,
methodNode.desc,
methodNode.access
)
)
}
}
/**
* 创建慢函数入口指令集
*/
private fun createMethodEnterInsnList(
level: Int,
className: String,
methodName: String,
desc: String,
access: Int
): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
)
)
add(IntInsnNode(BIPUSH, DoKitExtUtil.STACK_METHOD_LEVEL))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"recodeStaticMethodCostStart",
"(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
false
)
)
} else {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
)
)
add(IntInsnNode(BIPUSH, DoKitExtUtil.STACK_METHOD_LEVEL))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(VarInsnNode(ALOAD, 0))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"recodeObjectMethodCostStart",
"(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V",
false
)
)
}
this
}
}
/**
* 创建慢函数退出时的指令集
*/
private fun createMethodExitInsnList(
level: Int,
className: String,
methodName: String,
desc: String,
access: Int
): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
)
)
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"recodeStaticMethodCostEnd",
"(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
false
)
)
} else {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
)
)
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(VarInsnNode(ALOAD, 0))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"recodeObjectMethodCostEnd",
"(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V",
false
)
)
}
this
}
}
}
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.asm.asIterable
import com.didiglobal.booster.transform.asm.className
import com.google.auto.service.AutoService
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:全局业务代码慢函数 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
//@Priority(2)
//@AutoService(ClassTransformer::class)
class GlobalSlowMethodTransformer : AbsClassTransformer() {
val thresholdTime = DoKitExtUtil.slowMethodExt.normalMethod.thresholdTime
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (onCommInterceptor(context, klass)) {
return klass
}
if (!DoKitExtUtil.dokitSlowMethodSwitchOpen()) {
return klass
}
if (DoKitExtUtil.SLOW_METHOD_STRATEGY == SlowMethodExt.STRATEGY_STACK) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
val className = klass.className
//没有自定义设置插装包名 默认是以packageName为包名 即全局业务代码插桩
DoKitExtUtil.slowMethodExt.normalMethod.packageNames.forEach { packageName ->
//包含在白名单中且不在黑名单中
if (className.contains(packageName) && notMatchedBlackList(className)) {
klass.methods.filter { methodNode ->
methodNode.name != "<init>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.forEach { methodNode ->
methodNode.instructions.asIterable()
.filterIsInstance(MethodInsnNode::class.java).let { methodInsnNodes ->
if (methodInsnNodes.isNotEmpty()) {
//方法入口插入
methodNode.instructions.insert(
createMethodEnterInsnList(
className,
methodNode.name,
methodNode.access
)
)
//方法出口插入
methodNode.instructions.getMethodExitInsnNodes()
?.forEach { methodExitInsnNode ->
methodNode.instructions.insertBefore(
methodExitInsnNode,
createMethodExitInsnList(
className,
methodNode.name,
methodNode.access
)
)
}
}
}
}
}
}
return klass
}
private fun notMatchedBlackList(className: String): Boolean {
for (strBlack in DoKitExtUtil.slowMethodExt.normalMethod.methodBlacklist) {
if (className.contains(strBlack)) {
return false
}
}
return true
}
/**
* 创建慢函数入口指令集
*/
private fun createMethodEnterInsnList(
className: String,
methodName: String,
access: Int
): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/MethodCostUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"
)
)
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/MethodCostUtil",
"recodeStaticMethodCostStart",
"(ILjava/lang/String;)V",
false
)
)
} else {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/MethodCostUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"
)
)
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(VarInsnNode(ALOAD, 0))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/MethodCostUtil",
"recodeObjectMethodCostStart",
"(ILjava/lang/String;Ljava/lang/Object;)V",
false
)
)
}
this
}
}
/**
* 创建慢函数退出时的指令集
*/
private fun createMethodExitInsnList(
className: String,
methodName: String,
access: Int
): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/MethodCostUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"
)
)
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/MethodCostUtil",
"recodeStaticMethodCostEnd",
"(ILjava/lang/String;)V",
false
)
)
} else {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/MethodCostUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"
)
)
add(IntInsnNode(SIPUSH, thresholdTime))
add(LdcInsnNode("$className&$methodName"))
add(VarInsnNode(ALOAD, 0))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/MethodCostUtil",
"recodeObjectMethodCostEnd",
"(ILjava/lang/String;Ljava/lang/Object;)V",
false
)
)
}
this
}
}
}
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNode
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.asm.asIterable
import com.didiglobal.booster.transform.asm.className
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:入口函数 慢函数调用栈 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:不要指定自动注入 需要手动在DoKitAsmTransformer中通过配置创建
* 原理:transform()方法的调用是无序的 原因:哪一个class会先被transformer执行是不确定的 但是每一个class被transformer执行顺序是遵循transformer的Priority规则的
* ================================================
*/
class MethodStackDepTransformer(private val level: Int = 1) : AbsClassTransformer() {
private val thresholdTime = DoKitExtUtil.slowMethodExt.stackMethod.thresholdTime
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (onCommInterceptor(context, klass)) {
return klass
}
if (!DoKitExtUtil.dokitSlowMethodSwitchOpen()) {
return klass
}
if (DoKitExtUtil.SLOW_METHOD_STRATEGY == SlowMethodExt.STRATEGY_NORMAL) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
//命中黑名单则不插桩
if (!notMatchedBlackList(klass.className)) {
return klass
}
val methodStackKeys: MutableSet<String> = MethodStackNodeUtil.METHOD_STACK_KEYS[level - 1]
klass.methods.filter { methodNode ->
methodNode.name != "<init>" &&
!methodNode.isEmptyMethod() &&
!methodNode.isSingleMethod() &&
!methodNode.isGetSetMethod()
}.forEach { methodNode ->
val key = "${klass.className}&${methodNode.name}&${methodNode.desc}"
if (methodStackKeys.contains(key)) {
"${context.projectDir.lastPath()}->level-->$level mathched key===>$key".println()
operateMethodInsn(klass, methodNode)
}
}
return klass
}
private fun operateMethodInsn(klass: ClassNode, methodNode: MethodNode) {
//读取全是函数调用的指令
methodNode.instructions.asIterable().filterIsInstance(MethodInsnNode::class.java)
.filter { methodInsnNode ->
methodInsnNode.name != "<init>"
}.forEach { methodInsnNode ->
val methodStackNode = MethodStackNode(
level,
methodInsnNode.ownerClassName,
methodInsnNode.name,
methodInsnNode.desc,
klass.className,
methodNode.name,
methodNode.desc
)
MethodStackNodeUtil.addMethodStackNode(level, methodStackNode)
}
//函数出入口插入耗时统计代码
//方法入口插入
methodNode.instructions.insert(
createMethodEnterInsnList(
level,
klass.className,
methodNode.name,
methodNode.desc,
methodNode.access
)
)
//方法出口插入
methodNode.instructions.getMethodExitInsnNodes()?.forEach { methodExitInsnNode ->
methodNode.instructions.insertBefore(
methodExitInsnNode,
createMethodExitInsnList(
level,
klass.className,
methodNode.name,
methodNode.desc,
methodNode.access
)
)
}
}
/**
* 创建慢函数入口指令集
*/
private fun createMethodEnterInsnList(
level: Int,
className: String,
methodName: String,
desc: String,
access: Int
): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
)
)
add(IntInsnNode(BIPUSH, DoKitExtUtil.STACK_METHOD_LEVEL))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"recodeStaticMethodCostStart",
"(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
false
)
)
} else {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
)
)
add(IntInsnNode(BIPUSH, DoKitExtUtil.STACK_METHOD_LEVEL))
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(VarInsnNode(ALOAD, 0))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"recodeObjectMethodCostStart",
"(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V",
false
)
)
}
this
}
}
/**
* 创建慢函数退出时的指令集
*/
private fun createMethodExitInsnList(
level: Int,
className: String,
methodName: String,
desc: String,
access: Int
): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
return with(InsnList()) {
if (isStaticMethod) {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
)
)
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"recodeStaticMethodCostEnd",
"(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
false
)
)
} else {
add(
FieldInsnNode(
GETSTATIC,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"INSTANCE",
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
)
)
add(IntInsnNode(BIPUSH, thresholdTime))
add(IntInsnNode(BIPUSH, level))
add(LdcInsnNode(className))
add(LdcInsnNode(methodName))
add(LdcInsnNode(desc))
add(VarInsnNode(ALOAD, 0))
add(
MethodInsnNode(
INVOKEVIRTUAL,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil",
"recodeObjectMethodCostEnd",
"(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V",
false
)
)
}
this
}
}
private fun notMatchedBlackList(className: String): Boolean {
for (strBlack in DoKitExtUtil.slowMethodExt.stackMethod.methodBlacklist) {
if (className.contains(strBlack)) {
return false
}
}
return true
}
}
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.isRelease
import com.didichuxing.doraemonkit.plugin.lastPath
import com.didichuxing.doraemonkit.plugin.println
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.kotlinx.asIterable
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.asm.className
import com.google.auto.service.AutoService
import org.objectweb.asm.Opcodes.INVOKESTATIC
import org.objectweb.asm.Opcodes.INVOKEVIRTUAL
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.MethodInsnNode
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
//@Priority(1)
//@AutoService(ClassTransformer::class)
class UrlConnectionTransformer : AbsClassTransformer() {
private val SHADOW_URL =
"com/didichuxing/doraemonkit/aop/urlconnection/HttpUrlConnectionProxyUtil"
private val DESC = "(Ljava/net/URLConnection;)Ljava/net/URLConnection;"
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (onCommInterceptor(context, klass)) {
return klass
}
if (!DoKitExtUtil.commExt.networkSwitch) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
klass.methods.forEach { method ->
method.instructions?.iterator()?.asIterable()
?.filterIsInstance(MethodInsnNode::class.java)?.filter {
it.opcode == INVOKEVIRTUAL &&
it.owner == "java/net/URL" &&
it.name == "openConnection" &&
it.desc == "()Ljava/net/URLConnection;"
}?.forEach {
"${context.projectDir.lastPath()}-> hook urlconnection succeed:${klass.name}_${it.name}_${it.desc}".println()
method.instructions.insert(
it,
MethodInsnNode(INVOKESTATIC, SHADOW_URL, "proxy", DESC, false)
)
}
}
return klass
}
}
package com.didichuxing.doraemonkit.plugin.extension
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/28-14:56
* 描 述:
* 修订历史:
* ================================================
*/
open class CommExt(
var gpsSwitch: Boolean = true,
var networkSwitch: Boolean = true,
var bigImgSwitch: Boolean = true,
var webViewSwitch: Boolean = true,
var didinetSwitch: Boolean = true
) {
fun gpsSwitch(gpsSwitch: Boolean) {
this.gpsSwitch = gpsSwitch
}
fun networkSwitch(networkSwitch: Boolean) {
this.networkSwitch = networkSwitch
}
fun didinetSwitch(didinetSwitch: Boolean) {
this.didinetSwitch = didinetSwitch
}
fun bigImgSwitch(bigImgSwitch: Boolean) {
this.bigImgSwitch = bigImgSwitch
}
fun webViewSwitch(webViewSwitch: Boolean) {
this.webViewSwitch = webViewSwitch
}
override fun toString(): String {
return "CommExt(gpsSwitch=$gpsSwitch, networkSwitch=$networkSwitch,didinetSwitch=$didinetSwitch, bigImgSwitch=$bigImgSwitch, webviewSwitch=$webViewSwitch)"
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.extension
import org.gradle.api.Action
/**
* Created by jint on 07/10/2018.
*/
open class DoKitExt(
//var dokitPluginSwitch: Boolean = true,
var comm: CommExt = CommExt(),
var slowMethod: SlowMethodExt = SlowMethodExt()) {
//方法名必须和插件配置一直才能进行反射注入
// fun dokitPluginSwitch(dokitPluginSwitch: Boolean) {
// this.dokitPluginSwitch = dokitPluginSwitch
// }
/**
* 让comm 支持 DSL 语法
*
* @param action
*/
fun comm(action: Action<CommExt>) {
action.execute(comm)
}
/**
* 让slowMethod 支持 DSL 语法
*
* @param action
*/
fun slowMethod(action: Action<SlowMethodExt>) {
action.execute(slowMethod)
}
override fun toString(): String {
return "DoKitExt(comm=$comm, slowMethod=$slowMethod)"
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.extension
import groovy.lang.Closure
import org.gradle.util.ConfigureUtil
import java.util.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/28-14:56
* 描 述:
* 修订历史:
* ================================================
*/
open class SlowMethodExt(
//0:打印函数调用栈 1:普通模式 运行时打印某个函数的耗时 全局业务代码函数插入
@Deprecated("已弃用,请在项目根目录的gradle.properties中通过DOKIT_METHOD_STRATEGY=0|1 来控制")
var strategy: Int = STRATEGY_STACK,
//函数功能开关
@Deprecated("已弃用,请在项目根目录的gradle.properties中通过DoKit_METHOD_SWITCH=true|false 来控制")
var methodSwitch: Boolean = false,
//函数调用栈模式
var stackMethod: StackMethodExt = StackMethodExt(),
//普通模式
var normalMethod: NormalMethodExt = NormalMethodExt()
) {
/**
* 函数功能开关
*/
fun strategy(strategy: Int) {
this.strategy = strategy
}
fun methodSwitch(methodSwitch: Boolean) {
this.methodSwitch = methodSwitch
}
fun stackMethod(closure: Closure<StackMethodExt?>?) {
ConfigureUtil.configure(closure, stackMethod)
}
fun normalMethod(closure: Closure<NormalMethodExt?>?) {
ConfigureUtil.configure(closure, normalMethod)
}
class StackMethodExt(
//默认阈值为5ms
var thresholdTime: Int = 5,
//入口函集合
var enterMethods: MutableSet<String> = mutableSetOf(),
//插桩黑名单
var methodBlacklist: MutableSet<String> = mutableSetOf()
) {
/**
* 默认值为5ms
*/
fun thresholdTime(thresholdTime: Int) {
this.thresholdTime = thresholdTime
}
fun enterMethods(enterMethods: MutableSet<String>) {
this.enterMethods = enterMethods
}
fun methodBlacklist(methodBlacklist: MutableSet<String>) {
this.methodBlacklist = methodBlacklist
}
override fun toString(): String {
return "StackMethodExt(thresholdTime=$thresholdTime, enterMethods=$enterMethods, methodBlacklist=$methodBlacklist)"
}
}
class NormalMethodExt(
//默认阈值为500ms
var thresholdTime: Int = 500,
//普通函数的插装包名集合
var packageNames: MutableSet<String> = mutableSetOf(),
//插桩黑名单
var methodBlacklist: MutableSet<String> = mutableSetOf()
) {
/**
* 默认值为500ms
*/
fun thresholdTime(thresholdTime: Int) {
this.thresholdTime = thresholdTime
}
fun packageNames(packageNames: MutableSet<String>) {
this.packageNames = packageNames
}
fun methodBlacklist(methodBlacklist: MutableSet<String>) {
this.methodBlacklist = methodBlacklist
}
override fun toString(): String {
return "NormalMethodExt{" +
"thresholdTime=" + thresholdTime +
", packageNames=" + packageNames +
", methodBlacklist=" + methodBlacklist +
'}'
}
}
override fun toString(): String {
return "SlowMethodExt{" +
"strategy=" + strategy +
", methodSwitch=" + methodSwitch +
", stackMethod=" + stackMethod +
", normalMethod=" + normalMethod +
'}'
}
companion object {
const val STRATEGY_STACK = 0
const val STRATEGY_NORMAL = 1
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.processor
import com.didichuxing.doraemonkit.plugin.println
import org.xml.sax.Attributes
import org.xml.sax.helpers.DefaultHandler
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:3/2/21-17:26
* 描 述:
* 修订历史:
* ================================================
*/
const val ATTR_NAME = "android:name"
const val MANIFEST_ATTR_NAME = "package"
class DoKitComponentHandler : DefaultHandler() {
var appPackageName: String = ""
val applications = mutableSetOf<String>()
val activities = mutableSetOf<String>()
val services = mutableSetOf<String>()
val providers = mutableSetOf<String>()
val receivers = mutableSetOf<String>()
override fun startElement(
uri: String,
localName: String,
qName: String,
attributes: Attributes
) {
val name: String = attributes.getValue(ATTR_NAME) ?: ""
val packageName: String = attributes.getValue(MANIFEST_ATTR_NAME) ?: ""
when (qName) {
"manifest" -> {
appPackageName = packageName
}
"application" -> {
applications.add(name)
}
"activity" -> {
activities.add(name)
}
"service" -> {
services.add(name)
}
"provider" -> {
providers.add(name)
}
"receiver" -> {
receivers.add(name)
}
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.processor
import com.android.build.gradle.AppExtension
import com.android.build.gradle.api.ApplicationVariant
import com.android.build.gradle.api.BaseVariant
import com.didichuxing.doraemonkit.plugin.*
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import com.didiglobal.booster.gradle.dependencies
import com.didiglobal.booster.gradle.getAndroid
import com.didiglobal.booster.gradle.project
import com.didiglobal.booster.task.spi.VariantProcessor
import com.didiglobal.booster.transform.ArtifactManager
import com.didiglobal.booster.transform.artifacts
import com.didiglobal.booster.transform.util.ComponentHandler
import org.gradle.api.Project
import org.gradle.api.artifacts.result.ResolvedArtifactResult
import java.lang.NullPointerException
import javax.xml.parsers.SAXParserFactory
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/15-11:28
* 描 述:
* 修订历史:
* ================================================
*/
class DoKitPluginConfigProcessor(val project: Project) : VariantProcessor {
override fun process(variant: BaseVariant) {
if (!DoKitExtUtil.DOKIT_PLUGIN_SWITCH) {
return
}
if (variant.isRelease()) {
return
}
//统计三方库信息
if (DoKitExtUtil.THIRD_LIBINFO_SWITCH) {
//遍历三方库
val dependencies = variant.dependencies
DoKitExtUtil.THIRD_LIB_INFOS.clear()
for (artifactResult: ResolvedArtifactResult in dependencies) {
//println("三方库信息===>${artifactResult.variant.displayName}____${artifactResult.file.toString()}")
///Users/didi/project/android/dokit_github/DoraemonKit/Android/java/app/libs/BaiduLBS_Android.jar
///Users/didi/.gradle/caches/modules-2/files-2.1/androidx.activity/activity-ktx/1.2.0/c16aac66e6c4617b01118ab2509f009bb7919b3b/activity-ktx-1.2.0.aar
//println("三方库信息===>${artifactResult.variant.displayName}____${artifactResult.file.toString()}")
// "artifactResult===>${artifactResult.file}|${artifactResult.variant}|${artifactResult.id}|${artifactResult.type}".println()
//"artifactResult===>${artifactResult.variant.owner}|${artifactResult.variant.attributes}|${artifactResult.variant.displayName}|${artifactResult.variant.capabilities}|${artifactResult.variant.externalVariant}".println()
//"artifactResult===>${artifactResult.variant.displayName}".println()
val variants = artifactResult.variant.displayName.split(" ")
var thirdLibInfo: ThirdLibInfo? = null
if (variants.size == 3) {
thirdLibInfo = ThirdLibInfo(
variants[0],
artifactResult.file.length()
)
if (thirdLibInfo.variant.contains("dokitx-rpc")) {
DoKitExtUtil.HAS_DOKIT_RPC_MODULE = true
}
// "thirdLibInfo.variant===>${thirdLibInfo.variant}".println()
DoKitExtUtil.THIRD_LIB_INFOS.add(thirdLibInfo)
} else if (variants.size == 4) {
thirdLibInfo = ThirdLibInfo(
"porject ${variants[1]}",
artifactResult.file.length()
)
if (thirdLibInfo.variant.contains("doraemonkit-rpc")) {
DoKitExtUtil.HAS_DOKIT_RPC_MODULE = true
}
// "thirdLibInfo.variant===>${thirdLibInfo.variant}".println()
DoKitExtUtil.THIRD_LIB_INFOS.add(thirdLibInfo)
}
// val paths = artifactResult.file.toString().split("/")
// var fileName: String = ""
// var groupId: String = ""
// var artifactId: String = ""
// var version: String = ""
// if (artifactResult.file.toString().contains(".gradle/caches")) {
// if (paths.size >= 5) {
// groupId = paths[paths.size - 5]
// artifactId = paths[paths.size - 4]
// version = paths[paths.size - 3]
// fileName =
// "$groupId:$artifactId:$version"
// } else {
// fileName = paths[paths.size - 1]
// }
// } else {
// fileName = paths[paths.size - 1]
// }
//
// val thirdLibInfo =
// ThirdLibInfo(
// groupId,
// artifactId,
// version,
// fileName,
// artifactResult.file.length(),
// artifactResult.variant.displayName
// )
// val key = "$groupId:$artifactId"
// if (DoKitExtUtil.THIRD_LIB_INFOS[key] == null) {
// DoKitExtUtil.THIRD_LIB_INFOS[key] = thirdLibInfo
// } else {
// val libInfo = DoKitExtUtil.THIRD_LIB_INFOS[key]
// if (DoKitPluginUtil.compareVersion(thirdLibInfo.version, libInfo!!.version) > 0) {
// DoKitExtUtil.THIRD_LIB_INFOS[key] = thirdLibInfo
// }
// }
}
}
//查找application module下的配置
if (variant is ApplicationVariant) {
// if (DoKitExtUtil.OKHTTP_VERSION == "V3") {
// variant.project.dependencies.add(
// "implementation",
// "com.didichuxing.doraemonkit:dokit-okhttp_v3:3.2.0"
// )
// } else if (DoKitExtUtil.OKHTTP_VERSION == "V4") {
// variant.project.dependencies.add(
// "implementation",
// "com.didichuxing.doraemonkit:dokit-okhttp_v4:3.2.0"
// )
// }
//查找AndroidManifest.xml 文件路径
variant.artifacts.get(ArtifactManager.MERGED_MANIFESTS).forEach { manifest ->
val parser = SAXParserFactory.newInstance().newSAXParser()
val handler = DoKitComponentHandler()
"App Manifest path====>$manifest".println()
parser.parse(manifest, handler)
"App PackageName is====>${handler.appPackageName}".println()
"App Application path====>${handler.applications}".println()
DoKitExtUtil.setAppPackageName(handler.appPackageName)
DoKitExtUtil.setApplications(handler.applications)
}
//读取插件配置
variant.project.getAndroid<AppExtension>().let { appExt ->
//查找Application路径
val doKitExt = variant.project.extensions.getByType(DoKitExt::class.java)
DoKitExtUtil.init(doKitExt)
// "App ApplicationId is====>${appExt.defaultConfig.applicationId}".println()
// appExt.defaultConfig.applicationId?.let {
// DoKitExtUtil.init(doKitExt)
// } ?: throw NullPointerException("applicationId is null,applicationId暂不支持动态配置的方式读取")
}
} else {
"${variant.project.name}-不建议在Library Module下引入dokit插件".println()
}
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.stack_method
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/20-16:50
* 描 述:
* 修订历史:
* ================================================
*/
data class MethodStackNode(var level: Int,
var className: String,
var methodName: String,
var desc: String,
var parentClassName: String,
var parentMethodName: String,
var parentDesc: String)
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.stack_method
import org.gradle.internal.impldep.org.apache.commons.lang.mutable.Mutable
import java.util.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/20-16:58
* 描 述:
* 修订历史:
* ================================================
*/
object MethodStackNodeUtil {
val METHOD_STACK_KEYS: MutableList<MutableSet<String>> by lazy {
Collections.synchronizedList(mutableListOf<MutableSet<String>>())
}
fun addMethodStackNode(level: Int, methodStackNode: MethodStackNode) {
val key = "${methodStackNode.className}&${methodStackNode.methodName}&${methodStackNode.desc}"
METHOD_STACK_KEYS[level].add(key)
}
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.transform
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.Transform
import com.android.build.api.transform.TransformInvocation
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.pipeline.TransformManager
import com.didichuxing.doraemonkit.plugin.DoKitTransformInvocation
import com.didichuxing.doraemonkit.plugin.println
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.gradle.*
import com.didiglobal.booster.transform.AbstractKlassPool
import com.didiglobal.booster.transform.Transformer
import org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer
* @author johnsonlee
*/
open class DoKitBaseTransform(val project: Project) : Transform() {
/*transformers
* Preload transformers as List to fix NoSuchElementException caused by ServiceLoader in parallel mode
* booster 的默认出炉逻辑 DoKit已重写自处理
*/
internal open val transformers = listOf<Transformer>()
internal val verifyEnabled = project.getProperty(OPT_TRANSFORM_VERIFY, false)
private val android: BaseExtension = project.getAndroid()
private lateinit var androidKlassPool: AbstractKlassPool
init {
project.afterEvaluate {
androidKlassPool = object : AbstractKlassPool(android.bootClasspath) {}
}
}
val bootKlassPool: AbstractKlassPool
get() = androidKlassPool
override fun getName() = this.javaClass.simpleName
override fun isIncremental() = !verifyEnabled
override fun isCacheable() = !verifyEnabled
override fun getInputTypes(): MutableSet<QualifiedContent.ContentType> =
TransformManager.CONTENT_CLASS
override fun getScopes(): MutableSet<in QualifiedContent.Scope> = when {
transformers.isEmpty() -> mutableSetOf()
project.plugins.hasPlugin("com.android.library") -> SCOPE_PROJECT
project.plugins.hasPlugin("com.android.application") -> SCOPE_FULL_PROJECT
project.plugins.hasPlugin("com.android.dynamic-feature") -> SCOPE_FULL_WITH_FEATURES
else -> TODO("Not an Android project")
}
override fun getReferencedScopes(): MutableSet<in QualifiedContent.Scope> = when {
transformers.isEmpty() -> when {
project.plugins.hasPlugin("com.android.library") -> SCOPE_PROJECT
project.plugins.hasPlugin("com.android.application") -> SCOPE_FULL_PROJECT
project.plugins.hasPlugin("com.android.dynamic-feature") -> SCOPE_FULL_WITH_FEATURES
else -> TODO("Not an Android project")
}
else -> super.getReferencedScopes()
}
final override fun transform(invocation: TransformInvocation) {
transformers.forEach {
"transform====>${this::class.simpleName}====>${it}}".println()
}
DoKitTransformInvocation(invocation, this).apply {
if (isIncremental) {
doIncrementalTransform()
} else {
outputProvider?.deleteAll()
doFullTransform()
}
}
}
}
/**
* The option for transform outputs verifying, default is false
*/
private const val OPT_TRANSFORM_VERIFY = "dokit.transform.verify"
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.transform
import com.didichuxing.doraemonkit.plugin.asmtransformer.DoKitAsmTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.BigImgTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.CommTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.EnterMethodStackTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.GlobalSlowMethodTransformer
import com.didiglobal.booster.transform.Transformer
import org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer、EnterMethodStackTransformer
* @author johnsonlee
*/
class DoKitCommTransform(androidProject: Project) : DoKitBaseTransform(androidProject) {
override val transformers = listOf<Transformer>(
DoKitAsmTransformer(
listOf(
CommTransformer(),
BigImgTransformer(),
GlobalSlowMethodTransformer(),
EnterMethodStackTransformer()
)
)
)
}
package com.didichuxing.doraemonkit.plugin.transform
import com.android.build.api.variant.VariantInfo
import com.didichuxing.doraemonkit.plugin.asmtransformer.DoKitAsmTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.BigImgTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.CommTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.EnterMethodStackTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.GlobalSlowMethodTransformer
import com.didiglobal.booster.transform.Transformer
import org.gradle.api.Project
internal class DoKitCommTransformV34(project: Project) : DoKitBaseTransform(project) {
override val transformers = listOf<Transformer>(
DoKitAsmTransformer(
listOf(
CommTransformer(),
BigImgTransformer(),
GlobalSlowMethodTransformer(),
EnterMethodStackTransformer()
)
)
)
@Suppress("UnstableApiUsage")
override fun applyToVariant(variant: VariantInfo): Boolean {
return variant.buildTypeEnabled || (variant.flavorNames.isNotEmpty() && variant.fullVariantEnabled)
}
@Suppress("UnstableApiUsage")
private val VariantInfo.fullVariantEnabled: Boolean
get() = project.findProperty("booster.transform.${fullVariantName}.enabled")?.toString()?.toBoolean() ?: true
@Suppress("UnstableApiUsage")
private val VariantInfo.buildTypeEnabled: Boolean
get() = project.findProperty("booster.transform.${buildTypeName}.enabled")?.toString()?.toBoolean() ?: true
}
package com.didichuxing.doraemonkit.plugin.transform
import com.didichuxing.doraemonkit.plugin.asmtransformer.DoKitAsmTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.MethodStackDepTransformer
import com.didiglobal.booster.transform.Transformer
import org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer
* @author johnsonlee
*/
open class DoKitDependTransform(androidProject: Project, private val level: Int) :
DoKitBaseTransform(androidProject) {
override val transformers = listOf<Transformer>(DoKitAsmTransformer(listOf(MethodStackDepTransformer(level))))
override fun getName(): String {
return "${this.javaClass.simpleName}_$level"
}
}
package com.didichuxing.doraemonkit.plugin.transform
import com.android.build.api.variant.VariantInfo
import com.didichuxing.doraemonkit.plugin.asmtransformer.DoKitAsmTransformer
import com.didichuxing.doraemonkit.plugin.classtransformer.MethodStackDepTransformer
import com.didiglobal.booster.transform.Transformer
import org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer
* @author johnsonlee
*/
open class DoKitDependTransformV34(androidProject: Project, private val level: Int) :
DoKitBaseTransform(androidProject) {
override val transformers = listOf<Transformer>(DoKitAsmTransformer(listOf(MethodStackDepTransformer(level))))
override fun getName(): String {
return "${this.javaClass.simpleName}_$level"
}
@Suppress("UnstableApiUsage")
override fun applyToVariant(variant: VariantInfo): Boolean {
return variant.buildTypeEnabled || (variant.flavorNames.isNotEmpty() && variant.fullVariantEnabled)
}
@Suppress("UnstableApiUsage")
private val VariantInfo.fullVariantEnabled: Boolean
get() = project.findProperty("booster.transform.${fullVariantName}.enabled")?.toString()
?.toBoolean() ?: true
@Suppress("UnstableApiUsage")
private val VariantInfo.buildTypeEnabled: Boolean
get() = project.findProperty("booster.transform.${buildTypeName}.enabled")?.toString()
?.toBoolean() ?: true
}
......@@ -3,12 +3,12 @@ include ':doraemonkit-util'
include ':doraemonkit-ft'
include ':doraemonkit'
include ':doraemonkit-mc'
include ':doraemonkit-plugin'
include ':doraemonkit-okhttp-v3'
include ':doraemonkit-okhttp-v4'
include ':doraemonkit-okhttp-api'
include ':doraemonkit-weex'
include ':doraemonkit-no-op'
include ':doraemonkit-rpc'
include ':doraemonkit-rpc-mc'
//include ':doraemonkit-rpc'
//include ':doraemonkit-rpc-mc'
includeBuild("./Plugin")
//include ':doraemonkit-leakcanary'
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册