提交 28b07296 编写于 作者: J jackjintai

android:完成函数调用栈n级调用(用户自定义)

上级 8628b72c
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply from: 'doraemonkit.gradle'
android {
......@@ -73,7 +74,7 @@ dokitExt {
//通用设置
comm {
//地图经纬度开关
gpsSwitch true
gpsSwitch false
//网络开关
networkSwitch true
//大图开关
......@@ -83,14 +84,14 @@ dokitExt {
slowMethod {
//0:默认模式 打印函数调用栈 需添加指定入口 默认为application onCreate 和attachBaseContext
//1:普通模式 运行时打印某个函数的耗时 全局业务代码函数插入
strategy 1
strategy 0
//函数功能开关
methodSwitch true
//调用栈模式配置
stackMethod {
//默认值为 5ms 小于该值的函数在调用栈中不显示
thresholdTime 10
thresholdTime 5
//调用栈函数入口
enterMethods = ["com.didichuxing.doraemondemo.MainDebugActivity.test1"]
}
......
......@@ -5,7 +5,7 @@ if (rootProject.ext.config["applyPlugin"]) {
dependencies {
//外部平台依赖
debugImplementation project(":doraemonkit")
debugImplementation project(":doraemonkit-weex")
//debugImplementation project(":doraemonkit-weex")
// releaseImplementation project(":doraemonkit-no-op")
// debugImplementation project(":doraemonkit-leakcanary")
//新版线上包
......
......@@ -32,8 +32,8 @@ class App : Application() {
// kits.add(DemoKit())
// kits.add(DemoKit())
val mapKits: LinkedHashMap<String, MutableList<AbstractKit>> = linkedMapOf()
mapKits.put("业务专区1", mutableListOf(DemoKit()))
mapKits.put("业务专区2", mutableListOf(DemoKit()))
mapKits["业务专区1"] = mutableListOf<AbstractKit>(DemoKit())
mapKits["业务专区2"] = mutableListOf<AbstractKit>(DemoKit())
DoraemonKit.install(this, mapKits = mapKits, productId = "749a0600b5e48dd77cf8ee680be7b1b7")
val config = ImagePipelineConfig.newBuilder(this)
......
......@@ -25,24 +25,19 @@ import com.baidu.location.BDAbstractLocationListener
import com.baidu.location.BDLocation
import com.baidu.location.LocationClient
import com.baidu.location.LocationClientOption
import com.blankj.utilcode.util.*
import com.blankj.utilcode.util.ConvertUtils
import com.blankj.utilcode.util.ThreadUtils
import com.blankj.utilcode.util.ThreadUtils.SimpleTask
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.CircleCrop
import com.didichuxing.doraemondemo.retrofit.GithubService
import com.didichuxing.doraemonkit.DoraemonKit
import com.didichuxing.doraemonkit.aop.method_stack.MethodInvokNode
import com.didichuxing.doraemonkit.aop.method_stack.MethodStackBean
import com.didichuxing.doraemonkit.aop.method_stack.MethodStackUtil
import com.didichuxing.doraemonkit.okgo.DokitOkGo
import com.didichuxing.doraemonkit.okgo.callback.StringCallback
import com.didichuxing.doraemonkit.okgo.model.Response
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.view.SimpleDraweeView
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonObject
import com.nostra13.universalimageloader.core.ImageLoader
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration
import com.squareup.picasso.MemoryPolicy
......@@ -54,7 +49,6 @@ import com.tencent.map.geolocation.TencentLocationRequest
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_main.*
import okhttp3.*
import org.json.JSONArray
import org.json.JSONObject
import pub.devrel.easypermissions.EasyPermissions
import pub.devrel.easypermissions.PermissionRequest
......@@ -62,9 +56,7 @@ import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.io.*
import java.lang.reflect.Type
import java.net.*
import java.util.ArrayList
/**
* @author jintai
......@@ -198,10 +190,8 @@ class MainDebugActivity : BaseActivity(), View.OnClickListener {
private val mLocationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
if (location != null) {
val string = "lat====>" + location.latitude + " lng====>" + location.longitude
Log.i(TAG, "系统定位====>$string")
}
val string = "lat====>" + location.latitude + " lng====>" + location.longitude
Log.i(TAG, "系统定位====>$string")
}
override fun onProviderDisabled(arg0: String) {}
......@@ -440,7 +430,7 @@ class MainDebugActivity : BaseActivity(), View.OnClickListener {
.url("http://cdn1.lbesec.com/products/history/20131220/privacyspace_rel_2.2.1617.apk")
.build()
}
val call = okHttpClient!!.newCall(request)
val call = okHttpClient!!.newCall(request!!)
val startTime = SystemClock.uptimeMillis()
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
......
......@@ -3,6 +3,7 @@ package com.didichuxing.doraemondemo;
import android.util.Log;
import com.didichuxing.doraemonkit.aop.DokitPluginConfig;
import com.didichuxing.doraemonkit.aop.method_stack.MethodStackUtil;
import com.didichuxing.doraemonkit.aop.urlconnection.HttpUrlConnectionProxyUtil;
import java.io.IOException;
......@@ -25,14 +26,8 @@ public class AopTest {
private static final String TAG = "AopTest";
public void test() {
try {
URL url = new URL("sss");
URLConnection connection = url.openConnection();
connection.setReadTimeout(1000);
connection.connect();
} catch (IOException e) {
e.printStackTrace();
}
//MethodStackUtil.getInstance().recodeObjectMethodCostStart(10, 5, 0, "AopTest", "test", "desc", this);
MethodStackUtil.getInstance().recodeStaticMethodCostStart(10, 5, 7, "AopTest", "test", "desc");
}
......
package com.didichuxing.doraemonkit.plugin
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.InsnNode
import org.objectweb.asm.tree.MethodInsnNode
/**
* ================================================
......@@ -14,8 +16,8 @@ import org.objectweb.asm.tree.InsnNode
* ================================================
*/
fun InsnList.methodExitInsnNode(): InsnNode? {
return this.iterator()?.asSequence()?.filterIsInstance(InsnNode::class.java)?.find {
fun InsnList.getMethodExitInsnNodes(): Sequence<InsnNode>? {
return this.iterator()?.asSequence()?.filterIsInstance(InsnNode::class.java)?.filter {
it.opcode == RETURN ||
it.opcode == IRETURN ||
it.opcode == FRETURN ||
......@@ -24,4 +26,7 @@ fun InsnList.methodExitInsnNode(): InsnNode? {
it.opcode == DRETURN ||
it.opcode == ATHROW
}
}
\ No newline at end of file
}
val MethodInsnNode.ownerClassName: String
get() = owner.replace('/', '.')
\ No newline at end of file
......@@ -3,6 +3,8 @@ 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 com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt.Companion.STRATEGY_NORMAL
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
/**
* ================================================
......@@ -21,7 +23,11 @@ object DoKitExtUtil {
*/
private var mDokitPluginSwitch = true
private var mDokitLogSwitch = false
private var mUsefulInRelease = false
/**
* 默认函数调用为5级
*/
public var mStackMethodLevel = 5
private val applications: MutableSet<String> = mutableSetOf()
var commExt = CommExt()
private set
......@@ -35,11 +41,6 @@ object DoKitExtUtil {
return mDokitLogSwitch
}
fun usefulInRelease(): Boolean {
return mUsefulInRelease
}
/**
* 初始化
*
......@@ -49,7 +50,6 @@ object DoKitExtUtil {
fun init(dokitEx: DoKitExt, applicationId: String) {
mDokitPluginSwitch = dokitEx.dokitPluginSwitch
mDokitLogSwitch = dokitEx.dokitLogSwitch
mUsefulInRelease = dokitEx.usefulInRelease
//设置普通的配置
commExt = dokitEx.comm
slowMethodExt.strategy = dokitEx.slowMethod.strategy
......@@ -97,8 +97,10 @@ object DoKitExtUtil {
/**
* ============慢函数stack策略的配置 end==========
*/
}
fun setApplications(applications: MutableSet<String>) {
if (applications.isEmpty()) {
return
......
......@@ -3,8 +3,11 @@ 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.stack_method.MethodStackNodeUtil
import com.didichuxing.doraemonkit.plugin.transform.*
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.gradle.getAndroid
import com.didiglobal.booster.gradle.getProperty
import com.didiglobal.booster.task.spi.VariantProcessor
import org.gradle.api.Plugin
import org.gradle.api.Project
......@@ -25,6 +28,13 @@ class DoKitPlugin : Plugin<Project> {
//创建指定扩展 并将project 传入构造函数
project.extensions.create("dokitExt", DoKitExt::class.java)
// val debug = project.gradle.startParameter.taskNames.any {
// it.contains("debug") || it.contains("Debug")
// }
// if (!debug) {
// return
// }
project.gradle.addListener(DoKitTransformTaskExecutionListener(project))
//println("project.plugins===>${project.plugins}")
......@@ -68,8 +78,20 @@ class DoKitPlugin : Plugin<Project> {
when {
project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.dynamic-feature") -> {
project.getAndroid<AppExtension>().let { androidExt ->
val methodStackLevel = project.getProperty("DoKit_MethodStack_Level", 5)
DoKitExtUtil.mStackMethodLevel = methodStackLevel
MethodStackNodeUtil.METHOD_STACK_KEYS.clear()
//注册transform
androidExt.registerTransform(DoKitTransform(project))
androidExt.registerTransform(DoKitCommTransform(project))
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(DoKitDependTransform(project, index))
}
}
//项目评估完毕回调
project.afterEvaluate {
this.variantProcessors.let { processors ->
......@@ -85,7 +107,7 @@ class DoKitPlugin : Plugin<Project> {
project.plugins.hasPlugin("com.android.library") -> {
project.getAndroid<LibraryExtension>().let { libraryExt ->
libraryExt.registerTransform(DoKitTransform(project))
libraryExt.registerTransform(DoKitCommTransform(project))
project.afterEvaluate {
this.variantProcessors.let { processors ->
libraryExt.libraryVariants.forEach { variant ->
......
......@@ -13,6 +13,7 @@ import com.android.build.api.transform.Status.REMOVED
import com.android.build.api.transform.TransformInput
import com.android.build.api.transform.TransformInvocation
import com.android.build.api.transform.TransformOutputProvider
import com.didichuxing.doraemonkit.plugin.transform.DoKitBaseTransform
import com.didiglobal.booster.gradle.*
import com.didiglobal.booster.kotlinx.NCPU
import com.didiglobal.booster.transform.AbstractKlassPool
......@@ -30,7 +31,7 @@ import java.util.concurrent.Future
*
* @author johnsonlee
*/
internal class DoKitTransformInvocation(private val delegate: TransformInvocation, internal val transform: DoKitTransform) : TransformInvocation, TransformContext, ArtifactManager {
internal class DoKitTransformInvocation(private val delegate: TransformInvocation, internal val transform: DoKitBaseTransform) : TransformInvocation, TransformContext, ArtifactManager {
private val executor = Executors.newWorkStealingPool(NCPU)
......
package com.didichuxing.doraemonkit.plugin.asmclasstransformer
import com.didiglobal.booster.transform.asm.ClassTransformer
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/21-16:54
* 描 述:
* 修订历史:
* ================================================
*/
open class BaseDoKitClassTransformer : ClassTransformer {
}
\ No newline at end of file
package com.didichuxing.doraemonkit.plugin.asmclasstransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didichuxing.doraemonkit.plugin.ownerClassName
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.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) : BaseDoKitClassTransformer() {
private val thresholdTime = DoKitExtUtil.slowMethodExt.stackMethod.thresholdTime
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
if (!DoKitExtUtil.slowMethodExt.methodSwitch) {
return klass
}
if (DoKitExtUtil.slowMethodExt.strategy == SlowMethodExt.STRATEGY_NORMAL) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
val methodStackKeys: MutableSet<String> = MethodStackNodeUtil.METHOD_STACK_KEYS[level - 1]
klass.methods.forEach { methodNode ->
val key = "${klass.className}&${methodNode.name}&${methodNode.desc}"
if (methodStackKeys.contains(key)) {
println("level===>$level mathched key===>$key")
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
val insnList = InsnList()
if (isStaticMethod) {
insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
insnList.add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
insnList.add(IntInsnNode(BIPUSH, thresholdTime))
insnList.add(IntInsnNode(BIPUSH, level))
insnList.add(LdcInsnNode(className))
insnList.add(LdcInsnNode(methodName))
insnList.add(LdcInsnNode(desc))
insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostStart", "(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
insnList.add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
insnList.add(IntInsnNode(BIPUSH, thresholdTime))
insnList.add(IntInsnNode(BIPUSH, level))
insnList.add(LdcInsnNode(className))
insnList.add(LdcInsnNode(methodName))
insnList.add(LdcInsnNode(desc))
insnList.add(VarInsnNode(ALOAD, 0))
insnList.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))
}
return insnList
}
/**
* 创建慢函数退出时的指令集
*/
private fun createMethodExitInsnList(level: Int, className: String, methodName: String, desc: String, access: Int): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
val insnList = InsnList()
if (isStaticMethod) {
insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
insnList.add(IntInsnNode(BIPUSH, thresholdTime))
insnList.add(IntInsnNode(BIPUSH, level))
insnList.add(LdcInsnNode(className))
insnList.add(LdcInsnNode(methodName))
insnList.add(LdcInsnNode(desc))
insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostEnd", "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
insnList.add(IntInsnNode(BIPUSH, thresholdTime))
insnList.add(IntInsnNode(BIPUSH, level))
insnList.add(LdcInsnNode(className))
insnList.add(LdcInsnNode(methodName))
insnList.add(LdcInsnNode(desc))
insnList.add(VarInsnNode(ALOAD, 0))
insnList.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))
}
return insnList
}
}
package com.didichuxing.doraemonkit.plugin.asmtransformer
import com.didichuxing.doraemonkit.plugin.asmclasstransformer.BaseDoKitClassTransformer
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.Transformer
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
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/21-16:44
* 描 述:
* 修订历史:
* ================================================
*/
open class BaseDoKitAsmTransformer : Transformer {
private val threadMxBean = ManagementFactory.getThreadMXBean()
private val durations = mutableMapOf<BaseDoKitClassTransformer, Long>()
internal val transformers: Collection<BaseDoKitClassTransformer>
/**
* For unit test only
*/
constructor(vararg transformers: BaseDoKitClassTransformer) {
this.transformers = transformers.sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}
}
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: BaseDoKitClassTransformer, 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.didichuxing.doraemonkit.plugin.asmclasstransformer.MethodStackDepTransformer
import com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/21-16:44
* 描 述:
* 修订历史:
* ================================================
*/
class DoKitAsmTransformer(val level: Int) : BaseDoKitAsmTransformer(MethodStackDepTransformer(level)) {
}
package com.didichuxing.doraemonkit.plugin.transform
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.methodExitInsnNode
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
......@@ -25,10 +25,12 @@ import org.objectweb.asm.tree.VarInsnNode
@Priority(2)
@AutoService(ClassTransformer::class)
class BigImgTransformer : ClassTransformer {
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(klass.className == "com.didichuxing.doraemondemo.App"){
// println("===BigImgTransformer====transform===")
// }
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
......@@ -49,8 +51,8 @@ class BigImgTransformer : ClassTransformer {
(methodNode.name == "init" || methodNode.name == "<init>") && methodNode.desc != null
}.let { methodNode ->
//函数结束的地方插入
methodNode?.instructions?.methodExitInsnNode().let {
methodNode?.instructions?.insertBefore(it, createGlideInsnList())
methodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
methodNode.instructions?.insertBefore(it, createGlideInsnList())
}
}
}
......@@ -61,8 +63,8 @@ class BigImgTransformer : ClassTransformer {
methodNode.name == "<init>" && methodNode.desc != null
}.let { methodNode ->
//函数结束的地方插入
methodNode?.instructions?.methodExitInsnNode().let {
methodNode?.instructions?.insertBefore(it, createPicassoInsnList())
methodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
methodNode.instructions?.insertBefore(it, createPicassoInsnList())
}
}
}
......
package com.didichuxing.doraemonkit.plugin.transform
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.methodExitInsnNode
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
......@@ -29,6 +29,9 @@ class CommTransformer : ClassTransformer {
}
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
// if(klass.className == "com.didichuxing.doraemondemo.App"){
// println("===CommTransformer====transform===")
// }
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
......@@ -78,15 +81,17 @@ class CommTransformer : ClassTransformer {
val zeroConsMethodNode = klass.methods?.find {
it.name == "<init>" && it.desc == "()V"
}
val zeroReturnInsnNode = zeroConsMethodNode?.instructions?.methodExitInsnNode()
zeroConsMethodNode?.instructions?.insertBefore(zeroReturnInsnNode, createOkHttpZeroConsInsnList())
zeroConsMethodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
zeroConsMethodNode.instructions.insertBefore(it, createOkHttpZeroConsInsnList())
}
//一个参数的构造方法
val oneConsMethodNode = klass.methods?.find {
it.name == "<init>" && it.desc == "(Lokhttp3/OkHttpClient;)V"
}
val oneReturnInsnNode = oneConsMethodNode?.instructions?.methodExitInsnNode()
oneConsMethodNode?.instructions?.insertBefore(oneReturnInsnNode, createOkHttpOneConsInsnList())
oneConsMethodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
oneConsMethodNode.instructions.insertBefore(it, createOkHttpOneConsInsnList())
}
}
//didi platform
......@@ -95,16 +100,18 @@ class CommTransformer : ClassTransformer {
val zeroConsMethodNode = klass.methods?.find {
it.name == "<init>" && it.desc == "()V"
}
val zeroReturnInsnNode = zeroConsMethodNode?.instructions?.methodExitInsnNode()
zeroConsMethodNode?.instructions?.insertBefore(zeroReturnInsnNode, createDidiHttpZeroConsInsnList())
zeroConsMethodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
zeroConsMethodNode.instructions.insertBefore(it, createDidiHttpZeroConsInsnList())
}
//一个参数的构造方法
val oneConsMethodNode = klass.methods?.find {
it.name == "<init>" && it.desc == "(Ldidihttp/DidiHttpClient;)V"
}
val oneReturnInsnNode = oneConsMethodNode?.instructions?.methodExitInsnNode()
oneConsMethodNode?.instructions?.insertBefore(oneReturnInsnNode, createDidiHttpOneConsInsnList())
oneConsMethodNode?.instructions?.getMethodExitInsnNodes()?.forEach {
oneConsMethodNode.instructions.insertBefore(it, createDidiHttpOneConsInsnList())
}
}
}
......
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didichuxing.doraemonkit.plugin.ownerClassName
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(4)
@AutoService(ClassTransformer::class)
class EnterMethodStackTransformer : ClassTransformer {
private val thresholdTime = DoKitExtUtil.slowMethodExt.stackMethod.thresholdTime
private val level = 0
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
if (!DoKitExtUtil.slowMethodExt.methodSwitch) {
return klass
}
if (DoKitExtUtil.slowMethodExt.strategy == SlowMethodExt.STRATEGY_NORMAL) {
return klass
}
if (DoKitExtUtil.ignorePackageNames(klass.className)) {
return klass
}
//默认为Application onCreate 和attachBaseContext
val enterMethods = DoKitExtUtil.slowMethodExt.stackMethod.enterMethods
//找不到配置的Application
if (enterMethods.isEmpty()) {
val superName = klass.superName
//先判断父类
if (superName.isNotEmpty() && (superName == "android/app/Application" ||
superName == "android/support/multidex/MultiDexApplication" ||
superName == "androidx/multidex/MultiDexApplication")) {
klass.methods.filter { methodNode ->
(methodNode.name == "onCreate" && methodNode.desc == "()V") || (methodNode.name == "attachBaseContext" && methodNode.desc == "(Landroid/content/Context;)V")
}.let { methodNodes ->
//读取全是函数调用的Insn
methodNodes.forEach { methodNode ->
operateMethodInsn(klass, methodNode)
}
}
}
} else {
enterMethods.forEach { enterMethodName ->
klass.methods.forEach { methodNode ->
val allMethodName = "${klass.className}.${methodNode.name}"
if (allMethodName == enterMethodName) {
println("level===>$level mathched enterMethod===>$allMethodName")
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
val insnList = InsnList()
if (isStaticMethod) {
insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
insnList.add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
insnList.add(IntInsnNode(BIPUSH, thresholdTime))
insnList.add(IntInsnNode(BIPUSH, level))
insnList.add(LdcInsnNode(className))
insnList.add(LdcInsnNode(methodName))
insnList.add(LdcInsnNode(desc))
insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostStart", "(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
insnList.add(IntInsnNode(BIPUSH, DoKitExtUtil.mStackMethodLevel))
insnList.add(IntInsnNode(BIPUSH, thresholdTime))
insnList.add(IntInsnNode(BIPUSH, level))
insnList.add(LdcInsnNode(className))
insnList.add(LdcInsnNode(methodName))
insnList.add(LdcInsnNode(desc))
insnList.add(VarInsnNode(ALOAD, 0))
insnList.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))
}
return insnList
}
/**
* 创建慢函数退出时的指令集
*/
private fun createMethodExitInsnList(level: Int, className: String, methodName: String, desc: String, access: Int): InsnList {
val isStaticMethod = access and ACC_STATIC != 0
val insnList = InsnList()
if (isStaticMethod) {
insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
insnList.add(IntInsnNode(BIPUSH, thresholdTime))
insnList.add(IntInsnNode(BIPUSH, level))
insnList.add(LdcInsnNode(className))
insnList.add(LdcInsnNode(methodName))
insnList.add(LdcInsnNode(desc))
insnList.add(MethodInsnNode(INVOKEVIRTUAL, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "recodeStaticMethodCostEnd", "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false))
} else {
insnList.add(MethodInsnNode(INVOKESTATIC, "com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil", "getInstance", "()Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;", false))
insnList.add(IntInsnNode(BIPUSH, thresholdTime))
insnList.add(IntInsnNode(BIPUSH, level))
insnList.add(LdcInsnNode(className))
insnList.add(LdcInsnNode(methodName))
insnList.add(LdcInsnNode(desc))
insnList.add(VarInsnNode(ALOAD, 0))
insnList.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))
}
return insnList
}
}
package com.didichuxing.doraemonkit.plugin.transform
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import com.didichuxing.doraemonkit.plugin.methodExitInsnNode
import com.didichuxing.doraemonkit.plugin.getMethodExitInsnNodes
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
......@@ -17,7 +17,7 @@ import org.objectweb.asm.tree.*
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:全局业务代码 慢函数 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 描 述:全局业务代码慢函数 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
......@@ -25,7 +25,11 @@ import org.objectweb.asm.tree.*
@AutoService(ClassTransformer::class)
class GlobalSlowMethodTransformer : ClassTransformer {
val thresholdTime = DoKitExtUtil.slowMethodExt.normalMethod.thresholdTime
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
// if(klass.className == "com.didichuxing.doraemondemo.App"){
// println("===GlobalSlowMethodTransformer====transform===")
// }
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
......@@ -56,7 +60,7 @@ class GlobalSlowMethodTransformer : ClassTransformer {
//方法入口插入
methodNode.instructions.insert(createMethodEnterInsnList(className, methodNode.name, methodNode.access))
//方法出口插入
methodNode.instructions.methodExitInsnNode().let { methodExitInsnNode ->
methodNode.instructions.getMethodExitInsnNodes()?.forEach { methodExitInsnNode ->
methodNode.instructions.insertBefore(methodExitInsnNode, createMethodExitInsnList(className, methodNode.name, methodNode.access))
}
}
......
package com.didichuxing.doraemonkit.plugin.transform
package com.didichuxing.doraemonkit.plugin.classtransformer
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didiglobal.booster.annotations.Priority
......@@ -28,6 +28,9 @@ class UrlConnectionTransformer : ClassTransformer {
private val DESC = "(Ljava/net/URLConnection;)Ljava/net/URLConnection;"
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
// if(klass.className == "com.didichuxing.doraemondemo.App"){
// println("===UrlConnectionTransformer====transform===")
// }
if (!DoKitExtUtil.dokitPluginSwitchOpen()) {
return klass
}
......
......@@ -7,7 +7,6 @@ import org.gradle.api.Action
*/
open class DoKitExt(var dokitPluginSwitch: Boolean = true,
var dokitLogSwitch: Boolean = true,
var usefulInRelease: Boolean = false,
var comm: CommExt = CommExt(),
var slowMethod: SlowMethodExt = SlowMethodExt()) {
......@@ -21,9 +20,6 @@ open class DoKitExt(var dokitPluginSwitch: Boolean = true,
this.dokitLogSwitch = dokitLogSwitch
}
fun usefulInRelease(usefulInRelease: Boolean) {
this.usefulInRelease = usefulInRelease
}
/**
* 让comm 支持 DSL 语法
......@@ -44,7 +40,7 @@ open class DoKitExt(var dokitPluginSwitch: Boolean = true,
}
override fun toString(): String {
return "DoKitExt(dokitPluginSwitch=$dokitPluginSwitch, dokitLogSwitch=$dokitLogSwitch, usefulInRelease=$usefulInRelease, comm=$comm, slowMethod=$slowMethod)"
return "DoKitExt(dokitPluginSwitch=$dokitPluginSwitch, dokitLogSwitch=$dokitLogSwitch, comm=$comm, slowMethod=$slowMethod)"
}
......
......@@ -56,16 +56,16 @@ open class SlowMethodExt(
this.thresholdTime = thresholdTime
}
fun normalMethod(enterMethods: MutableSet<String>) {
this.enterMethods = enterMethods
}
override fun toString(): String {
return "StackMethodExt{" +
"thresholdTime=" + thresholdTime +
", enterMethods=" + enterMethods +
'}'
return "StackMethodExt(thresholdTime=$thresholdTime, enterMethods=$enterMethods)"
}
}
class NormalMethodExt(
......
......@@ -5,6 +5,7 @@ import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.LibraryVariant
import com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import com.didichuxing.doraemonkit.plugin.transform.*
import com.didiglobal.booster.gradle.getAndroid
import com.didiglobal.booster.gradle.isDynamicFeature
import com.didiglobal.booster.gradle.project
......@@ -14,6 +15,7 @@ import com.didiglobal.booster.transform.ArtifactManager
import com.didiglobal.booster.transform.artifacts
import com.didiglobal.booster.transform.util.ComponentHandler
import com.google.auto.service.AutoService
import org.gradle.api.Project
import javax.xml.parsers.SAXParserFactory
/**
......@@ -37,7 +39,6 @@ class DoKitPluginConfigProcessor : VariantProcessor {
val parser = SAXParserFactory.newInstance().newSAXParser()
val handler = ComponentHandler()
parser.parse(manifest, handler)
//println("handler====>${handler.applications}")
DoKitExtUtil.setApplications(handler.applications)
}
......@@ -47,10 +48,9 @@ class DoKitPluginConfigProcessor : VariantProcessor {
//查找Application路径
val doKitExt = variant.project.extensions.getByType(DoKitExt::class.java)
DoKitExtUtil.init(doKitExt, appExt.defaultConfig.applicationId)
// println("doKitExt====>$doKitExt applicationId===>${appExt.defaultConfig.applicationId}")
}
//println("variant====>${variant.name}")
}
}
\ 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
package com.didichuxing.doraemonkit.plugin.transform
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.Transform
......@@ -6,6 +6,7 @@ import com.android.build.api.transform.TransformInvocation
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.build.gradle.internal.pipeline.TransformManager.SCOPE_FULL_PROJECT
import com.didichuxing.doraemonkit.plugin.DoKitTransformInvocation
import com.didiglobal.booster.gradle.SCOPE_FULL_WITH_FEATURES
import com.didiglobal.booster.gradle.SCOPE_PROJECT
import com.didiglobal.booster.gradle.getAndroid
......@@ -16,15 +17,15 @@ import java.util.ServiceLoader
/**
* Represents the transform base
*
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer
* @author johnsonlee
*/
open class DoKitTransform(val project: Project) : Transform() {
open class DoKitBaseTransform(val project: Project) : Transform() {
/*
* Preload transformers as List to fix NoSuchElementException caused by ServiceLoader in parallel mode
*/
internal val transformers = ServiceLoader.load(Transformer::class.java, javaClass.classLoader).toList()
internal open val transformers = ServiceLoader.load(Transformer::class.java, javaClass.classLoader).toList()
private val androidExt: BaseExtension = project.getAndroid()
......@@ -39,7 +40,7 @@ open class DoKitTransform(val project: Project) : Transform() {
val bootKlassPool: AbstractKlassPool
get() = androidKlassPool
override fun getName() = "booster"
override fun getName() = this.javaClass.simpleName
override fun isIncremental() = true
......
package com.didichuxing.doraemonkit.plugin.transform
import org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer、EnterMethodStackTransformer
* @author johnsonlee
*/
class DoKitCommTransform(androidProject: Project) : DoKitBaseTransform(androidProject) {
}
package com.didichuxing.doraemonkit.plugin.transform
import com.didichuxing.doraemonkit.plugin.asmtransformer.DoKitAsmTransformer
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, val level: Int) : DoKitBaseTransform(androidProject) {
internal override val transformers = mutableListOf<Transformer>(DoKitAsmTransformer(level))
override fun getName(): String {
return "${this.javaClass.simpleName}_$level"
}
}
......@@ -2,15 +2,15 @@ package com.didichuxing.doraemonkit.plugin;
import com.android.build.gradle.AppExtension;
import com.didichuxing.doraemonkit.plugin.extension.DoKitExt;
import com.didichuxing.doraemonkit.plugin.transform.DokitBigImageTransform;
import com.didichuxing.doraemonkit.plugin.transform.DokitCommTransform;
import com.didichuxing.doraemonkit.plugin.transform.DokitMethodStack0Transform;
import com.didichuxing.doraemonkit.plugin.transform.DokitMethodStack1Transform;
import com.didichuxing.doraemonkit.plugin.transform.DokitMethodStack2Transform;
import com.didichuxing.doraemonkit.plugin.transform.DokitMethodStack3Transform;
import com.didichuxing.doraemonkit.plugin.transform.DokitMethodStack4Transform;
import com.didichuxing.doraemonkit.plugin.transform.DokitSlowMethodTransform;
import com.didichuxing.doraemonkit.plugin.transform.DokitUrlConnectionTransform;
import com.didichuxing.doraemonkit.plugin.classtransformer.DokitBigImageTransform;
import com.didichuxing.doraemonkit.plugin.classtransformer.DokitCommTransform;
import com.didichuxing.doraemonkit.plugin.classtransformer.DokitMethodStack0Transform;
import com.didichuxing.doraemonkit.plugin.classtransformer.DokitMethodStack1Transform;
import com.didichuxing.doraemonkit.plugin.classtransformer.DokitMethodStack2Transform;
import com.didichuxing.doraemonkit.plugin.classtransformer.DokitMethodStack3Transform;
import com.didichuxing.doraemonkit.plugin.classtransformer.DokitMethodStack4Transform;
import com.didichuxing.doraemonkit.plugin.classtransformer.DokitSlowMethodTransform;
import com.didichuxing.doraemonkit.plugin.classtransformer.DokitUrlConnectionTransform;
import com.didiglobal.booster.gradle.BaseVariantKt;
import com.didiglobal.booster.gradle.VariantScopeKt;
......
package com.didichuxing.doraemonkit.plugin.transform;
package com.didichuxing.doraemonkit.plugin.classtransformer;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.TransformException;
......
package com.didichuxing.doraemonkit.plugin.transform;
package com.didichuxing.doraemonkit.plugin.classtransformer;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.TransformException;
......
package com.didichuxing.doraemonkit.plugin.transform;
package com.didichuxing.doraemonkit.plugin.classtransformer;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.TransformException;
......
package com.didichuxing.doraemonkit.plugin.transform;
package com.didichuxing.doraemonkit.plugin.classtransformer;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.TransformException;
......
package com.didichuxing.doraemonkit.plugin.transform;
package com.didichuxing.doraemonkit.plugin.classtransformer;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.TransformException;
......
package com.didichuxing.doraemonkit.plugin.transform;
package com.didichuxing.doraemonkit.plugin.classtransformer;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.TransformException;
......
package com.didichuxing.doraemonkit.plugin.transform;
package com.didichuxing.doraemonkit.plugin.classtransformer;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.TransformException;
......
package com.didichuxing.doraemonkit.plugin.transform;
package com.didichuxing.doraemonkit.plugin.classtransformer;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.TransformException;
......
package com.didichuxing.doraemonkit.plugin.transform;
package com.didichuxing.doraemonkit.plugin.classtransformer;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.TransformException;
......
......@@ -67,7 +67,7 @@ dependencies {
implementation rootProject.ext.dependencies["okhttp"]
implementation rootProject.ext.dependencies["okhttp_v2"]
implementation rootProject.ext.dependencies["room_runtime"]
annotationProcessor rootProject.ext.dependencies["room_compile"]
kapt rootProject.ext.dependencies["room_compile"]
//三大图片框架
compileOnly rootProject.ext.dependencies["glide"]
......
......@@ -176,7 +176,7 @@ object DoraemonKitReal {
* 添加内置kit
*/
private fun addInnerKit(application: Application) {
var json = ""
val json: String?
if (FileUtils.isFileExists(DokitConstant.SYSTEM_KITS_BAK_PATH)) {
json = FileIOUtils.readFile2String(DokitConstant.SYSTEM_KITS_BAK_PATH)
} else {
......@@ -353,7 +353,7 @@ object DoraemonKitReal {
return
}
val files = rootFileDir.listFiles()
for (file in files) {
files?.forEach { file ->
if (file.isDirectory) {
//若是目录,则递归打印该目录下的文件
//LogHelper.i(TAG, "文件夹==>" + file.getAbsolutePath());
......@@ -372,6 +372,7 @@ object DoraemonKitReal {
//LogHelper.i(TAG, "文件==>" + file.getAbsolutePath() + " fileName===>" + FileUtils.getFileName(file) + " fileLength===>" + fileLength);
}
}
}
/**
......
......@@ -9,6 +9,8 @@ import com.didichuxing.doraemonkit.aop.MethodCostUtil;
import com.didichuxing.doraemonkit.kit.timecounter.TimeCounterManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
......@@ -26,12 +28,13 @@ public class MethodStackUtil {
/**
* key className&methodName
*/
private ConcurrentHashMap<String, MethodInvokNode> ROOT_METHOD_STACKS = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, MethodInvokNode> LEVEL1_METHOD_STACKS = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, MethodInvokNode> LEVEL2_METHOD_STACKS = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, MethodInvokNode> LEVEL3_METHOD_STACKS = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, MethodInvokNode> LEVEL4_METHOD_STACKS = new ConcurrentHashMap<>();
// private ConcurrentHashMap<String, MethodInvokNode> ROOT_METHOD_STACKS = new ConcurrentHashMap<>();
// private ConcurrentHashMap<String, MethodInvokNode> LEVEL1_METHOD_STACKS = new ConcurrentHashMap<>();
// private ConcurrentHashMap<String, MethodInvokNode> LEVEL2_METHOD_STACKS = new ConcurrentHashMap<>();
// private ConcurrentHashMap<String, MethodInvokNode> LEVEL3_METHOD_STACKS = new ConcurrentHashMap<>();
// private ConcurrentHashMap<String, MethodInvokNode> LEVEL4_METHOD_STACKS = new ConcurrentHashMap<>();
private List<ConcurrentHashMap<String, MethodInvokNode>> METHOD_STACKS = Collections.synchronizedList(new ArrayList<ConcurrentHashMap<String, MethodInvokNode>>());
/**
* 静态内部类单例
......@@ -44,39 +47,37 @@ public class MethodStackUtil {
return MethodStackUtil.Holder.INSTANCE;
}
private void createMethodStackList(int totalLevel) {
if (METHOD_STACKS.size() == totalLevel) {
return;
}
METHOD_STACKS.clear();
for (int index = 0; index < totalLevel; index++) {
METHOD_STACKS.add(index, new ConcurrentHashMap<String, MethodInvokNode>());
}
}
/**
* @param level
* @param currentLevel
* @param methodName
* @param classObj null 代表静态函数
*/
public void recodeObjectMethodCostStart(int thresholdTime, int level, String className, String methodName, String desc, Object classObj) {
public void recodeObjectMethodCostStart(int totalLevel, int thresholdTime, int currentLevel, String className, String methodName, String desc, Object classObj) {
try {
//先创建队列
createMethodStackList(totalLevel);
MethodInvokNode methodInvokNode = new MethodInvokNode();
methodInvokNode.setStartTimeMillis(System.currentTimeMillis());
methodInvokNode.setCurrentThreadName(Thread.currentThread().getName());
methodInvokNode.setClassName(className);
methodInvokNode.setMethodName(methodName);
if (level == 0) {
methodInvokNode.setLevel(0);
ROOT_METHOD_STACKS.put(String.format("%s&%s", className, methodName), methodInvokNode);
} else if (level == 1) {
methodInvokNode.setLevel(1);
LEVEL1_METHOD_STACKS.put(String.format("%s&%s", className, methodName), methodInvokNode);
} else if (level == 2) {
methodInvokNode.setLevel(2);
LEVEL2_METHOD_STACKS.put(String.format("%s&%s", className, methodName), methodInvokNode);
} else if (level == 3) {
methodInvokNode.setLevel(3);
LEVEL3_METHOD_STACKS.put(String.format("%s&%s", className, methodName), methodInvokNode);
} else if (level == 4) {
methodInvokNode.setLevel(4);
LEVEL4_METHOD_STACKS.put(String.format("%s&%s", className, methodName), methodInvokNode);
}
methodInvokNode.setLevel(currentLevel);
METHOD_STACKS.get(currentLevel).put(String.format("%s&%s", className, methodName), methodInvokNode);
//特殊判定
if (level == 0) {
if (currentLevel == 0) {
if (classObj instanceof Application) {
if (methodName.equals("onCreate")) {
TimeCounterManager.get().onAppCreateStart();
......@@ -95,36 +96,25 @@ public class MethodStackUtil {
}
/**
* @param level
* @param currentLevel
* @param className
* @param methodName
* @param desc
* @param classObj null 代表静态函数
*/
public void recodeObjectMethodCostEnd(int thresholdTime, int level, String className, String methodName, String desc, Object classObj) {
public void recodeObjectMethodCostEnd(int thresholdTime, int currentLevel, String className, String methodName, String desc, Object classObj) {
synchronized (MethodCostUtil.class) {
try {
MethodInvokNode methodInvokNode = null;
if (level == 0) {
methodInvokNode = ROOT_METHOD_STACKS.get(String.format("%s&%s", className, methodName));
} else if (level == 1) {
methodInvokNode = LEVEL1_METHOD_STACKS.get(String.format("%s&%s", className, methodName));
} else if (level == 2) {
methodInvokNode = LEVEL2_METHOD_STACKS.get(String.format("%s&%s", className, methodName));
} else if (level == 3) {
methodInvokNode = LEVEL3_METHOD_STACKS.get(String.format("%s&%s", className, methodName));
} else if (level == 4) {
methodInvokNode = LEVEL4_METHOD_STACKS.get(String.format("%s&%s", className, methodName));
}
MethodInvokNode methodInvokNode = METHOD_STACKS.get(currentLevel).get(String.format("%s&%s", className, methodName));
if (methodInvokNode != null) {
methodInvokNode.setEndTimeMillis(System.currentTimeMillis());
bindNode(thresholdTime, level, methodInvokNode);
bindNode(thresholdTime, currentLevel, methodInvokNode);
}
//打印函数调用栈
if (level == 0) {
if (currentLevel == 0) {
if (methodInvokNode != null) {
toStack(classObj instanceof Application, methodInvokNode);
}
......@@ -139,7 +129,7 @@ public class MethodStackUtil {
}
//移除对象
ROOT_METHOD_STACKS.remove(className + "&" + methodName);
METHOD_STACKS.get(0).remove(className + "&" + methodName);
}
} catch (Exception e) {
......@@ -155,18 +145,18 @@ public class MethodStackUtil {
int index = 0;
for (int i = 0; i < stackTraceElements.length; i++) {
StackTraceElement stackTraceElement = stackTraceElements[i];
if (currentClassName.equals(stackTraceElement.getClassName().replaceAll("\\.", "/")) && currentMethodName.equals(stackTraceElement.getMethodName())) {
if (currentClassName.equals(stackTraceElement.getClassName()) && currentMethodName.equals(stackTraceElement.getMethodName())) {
index = i;
break;
}
}
StackTraceElement parentStackTraceElement = stackTraceElements[index + 1];
return String.format("%s&%s", parentStackTraceElement.getClassName().replaceAll("\\.", "/"), parentStackTraceElement.getMethodName());
return String.format("%s&%s", parentStackTraceElement.getClassName(), parentStackTraceElement.getMethodName());
}
private void bindNode(int thresholdTime, int level, MethodInvokNode methodInvokNode) {
private void bindNode(int thresholdTime, int currentLevel, MethodInvokNode methodInvokNode) {
if (methodInvokNode == null) {
return;
}
......@@ -176,55 +166,24 @@ public class MethodStackUtil {
return;
}
MethodInvokNode parentMethodNode;
switch (level) {
case 1:
//设置父node 并将自己添加到父node中
parentMethodNode = ROOT_METHOD_STACKS.get(getParentMethod(methodInvokNode.getClassName(), methodInvokNode.getMethodName()));
if (parentMethodNode != null) {
methodInvokNode.setParent(parentMethodNode);
parentMethodNode.addChild(methodInvokNode);
}
break;
case 2:
//设置父node 并将自己添加到父node中
parentMethodNode = LEVEL1_METHOD_STACKS.get(getParentMethod(methodInvokNode.getClassName(), methodInvokNode.getMethodName()));
if (parentMethodNode != null) {
methodInvokNode.setParent(parentMethodNode);
parentMethodNode.addChild(methodInvokNode);
}
break;
case 3:
//设置父node 并将自己添加到父node中
parentMethodNode = LEVEL2_METHOD_STACKS.get(getParentMethod(methodInvokNode.getClassName(), methodInvokNode.getMethodName()));
if (parentMethodNode != null) {
methodInvokNode.setParent(parentMethodNode);
parentMethodNode.addChild(methodInvokNode);
}
break;
case 4:
//设置父node 并将自己添加到父node中
parentMethodNode = LEVEL3_METHOD_STACKS.get(getParentMethod(methodInvokNode.getClassName(), methodInvokNode.getMethodName()));
if (parentMethodNode != null) {
methodInvokNode.setParent(parentMethodNode);
parentMethodNode.addChild(methodInvokNode);
}
break;
default:
break;
if (currentLevel >= 1) {
MethodInvokNode parentMethodNode = METHOD_STACKS.get(currentLevel - 1).get(getParentMethod(methodInvokNode.getClassName(), methodInvokNode.getMethodName()));
if (parentMethodNode != null) {
methodInvokNode.setParent(parentMethodNode);
parentMethodNode.addChild(methodInvokNode);
}
}
}
public void recodeStaticMethodCostStart(int thresholdTime, int level, String className, String methodName, String desc) {
recodeObjectMethodCostStart(thresholdTime, level, className, methodName, desc, new StaicMethodObject());
public void recodeStaticMethodCostStart(int totalLevel, int thresholdTime, int currentLevel, String className, String methodName, String desc) {
recodeObjectMethodCostStart(totalLevel, thresholdTime, currentLevel, className, methodName, desc, new StaicMethodObject());
}
public void recodeStaticMethodCostEnd(int thresholdTime, int level, String className, String methodName, String desc) {
recodeObjectMethodCostEnd(thresholdTime, level, className, methodName, desc, new StaicMethodObject());
public void recodeStaticMethodCostEnd(int thresholdTime, int currentLevel, String className, String methodName, String desc) {
recodeObjectMethodCostEnd(thresholdTime, currentLevel, className, methodName, desc, new StaicMethodObject());
}
private void jsonTravel(List<MethodStackBean> methodStackBeans, List<MethodInvokNode> methodInvokNodes) {
......@@ -254,7 +213,7 @@ public class MethodStackUtil {
public void toJson() {
List<MethodStackBean> methodStackBeans = new ArrayList<>();
for (MethodInvokNode methodInvokNode : ROOT_METHOD_STACKS.values()) {
for (MethodInvokNode methodInvokNode : METHOD_STACKS.get(0).values()) {
MethodStackBean methodStackBean = new MethodStackBean();
methodStackBean.setCostTime(methodInvokNode.getCostTimeMillis());
methodStackBean.setFunction(methodInvokNode.getClassName() + "&" + methodInvokNode.getMethodName());
......
......@@ -28,7 +28,7 @@ class DokitManagerAdapter(kitViews: MutableList<KitWrapItem>?)
override fun convert(holder: BaseViewHolder, item: KitWrapItem) {
when (item.itemType) {
KitWrapItem.TYPE_TITLE -> {
item.name?.let {
item.name.let {
holder.getView<TextView>(R.id.tv_title_name).text = it
}
}
......
......@@ -343,7 +343,7 @@ class DokitManagerFragment : BaseFragment() {
}
mAdapter.setOnItemClickListener { adapter, view, position ->
mAdapter.setOnItemClickListener { _, _, position ->
if (IS_EDIT) {
val multiKitItem = mKits[position]
if (multiKitItem.itemType == KitWrapItem.TYPE_KIT) {
......
......@@ -18,6 +18,6 @@ class DokitSettingAdapter(datas: MutableList<String>)
: BaseQuickAdapter<String, BaseViewHolder>(R.layout.dk_item_main_setting, datas) {
override fun convert(holder: BaseViewHolder, name: String) {
holder.getView<TextView>(R.id.tv_name).setText(name)
holder.getView<TextView>(R.id.tv_name).text = name
}
}
\ No newline at end of file
......@@ -45,7 +45,7 @@ class DokitSettingFragment : BaseFragment() {
mAdapter = DokitSettingAdapter(mSettings)
setting_list.adapter = mAdapter
setting_list.layoutManager = LinearLayoutManager(activity)
mAdapter.setOnItemClickListener { adapter, view, position ->
mAdapter.setOnItemClickListener { _, _, position ->
when (position) {
0 -> {
......
......@@ -42,7 +42,7 @@ class ToolPanelAdapter(kitViews: MutableList<KitWrapItem>?)
override fun convert(holder: BaseViewHolder, item: KitWrapItem) {
when (item.itemType) {
KitWrapItem.TYPE_TITLE -> {
item.name?.let {
item.name.let {
if (it.equals(DokitUtil.getString(R.string.dk_category_platform))) {
holder.getView<TextView>(R.id.tv_sub_title_name).visibility = View.VISIBLE
holder.getView<TextView>(R.id.tv_sub_title_name).text = "(www.dokit.cn)"
......@@ -64,7 +64,7 @@ class ToolPanelAdapter(kitViews: MutableList<KitWrapItem>?)
val radioGroup = holder.getView<RadioGroup>(R.id.rb_group)
val rbNormal = holder.getView<RadioButton>(R.id.rb_normal)
val rbSystem = holder.getView<RadioButton>(R.id.rb_system)
radioGroup.setOnCheckedChangeListener { group, checkedId ->
radioGroup.setOnCheckedChangeListener { _, checkedId ->
if (checkedId == R.id.rb_normal) {
//选中normal
SharedPrefsUtil.putString(SharedPrefsKey.FLOAT_START_MODE, "normal")
......
......@@ -114,7 +114,7 @@ class ToolPanelDokitView : AbsDokitView() {
4
}
}
mAdapter.setOnItemClickListener { adapter, view, position ->
mAdapter.setOnItemClickListener { _, _, position ->
val multiKitItem = mKits[position]
if (multiKitItem.itemType == KitWrapItem.TYPE_KIT) {
//常规模式下点击常用工具不隐藏工具面板
......
......@@ -16,3 +16,5 @@ android.debug.obsoleteApi=true
android.useAndroidX=true
android.enableJetifier=true
android.injected.testOnly=false
#dokit 函数调用栈层级
DoKit_MethodStack_Level=8
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册