[6.2.0][技术优化] 更改Handler消息记录方式,对编译生成的类方法集合进行过滤

This commit is contained in:
renwj
2023-11-20 15:35:36 +08:00
parent c8efde952b
commit 59ac4d030b
20 changed files with 824 additions and 1067 deletions

View File

@@ -214,9 +214,10 @@ dependencies {
implementation rootProject.ext.dependencies.android_start_up
implementation rootProject.ext.dependencies.lancetx_runtime
implementation rootProject.ext.dependencies.lancetx_compiler_lib
//kapt rootProject.ext.dependencies.lancetx_compiler
implementation rootProject.ext.dependencies.handler_proxy_runtime
kapt rootProject.ext.dependencies.lancetx_compiler
//annotationProcessor rootProject.ext.dependencies.lancetx_compiler
annotationProcessor rootProject.ext.dependencies.lancetx_compiler
annotationProcessor rootProject.ext.dependencies.google_auto_service
kapt rootProject.ext.dependencies.google_auto_service
compileOnly rootProject.ext.dependencies.google_auto_service

View File

@@ -5,6 +5,7 @@
-keeppackage com/knightboost/lancet/api/synchronized_lock/
-keeppackage com/mogo/core/lancetx/compiler/lib/
-keeppackage com/mogo/eagle/core/lancetx/generated/
-keeppackage com/mogo/eagle/core/handler/
-keeppackage com/mogo/launcher/lancet/jank/
-keeppackage com/rousetime/android_startup/
-keeppackage com/mogo/systrace/
@@ -22,4 +23,5 @@
-keepclass com.mogo.commons.AbsMogoApplication
-keepclass com.bytedance.apm.agent.v2.instrumentation.AppAgent
-keepclass com.mogo.eagle.core.utilcode.util.ProcessUtils
-keepclass com.mogo.eagle.core.utilcode.mogo.AppLaunchTimeUtils
-keepclass com.mogo.eagle.core.utilcode.mogo.AppLaunchTimeUtils
-keepclass com.mogo.eagle.core.function.main.ARouterUtils

View File

@@ -1,68 +0,0 @@
package com.mogo.launcher.lancet.jank;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.Keep;
import com.knightboost.lancet.api.Scope;
import com.knightboost.lancet.api.annotations.Group;
import com.knightboost.lancet.api.annotations.ReplaceInvoke;
import com.knightboost.lancet.api.annotations.TargetClass;
import com.knightboost.lancet.api.annotations.TargetMethod;
import com.knightboost.lancet.api.annotations.Weaver;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.CopyOnWriteArraySet;
@Keep
@Weaver
@Group("main_block_check")
public class AsyncHandlerReflectLancet {
@TargetClass(value = "java.lang.reflect.Method",scope = Scope.SELF)
@TargetMethod(methodName = "invoke")
@ReplaceInvoke
public static Object hookMethodInvoke(Method method, Object obj, Object... args) throws InvocationTargetException, IllegalAccessException {
Object ret = method.invoke(obj, args);
if (ret instanceof Handler && "createAsync".equals(method.getName())) {
CopyOnWriteArraySet<WeakReference<Handler>> asyncHandlers = MainBlockCheck.Companion.getAsyncHandlers();
asyncHandlers.add(new WeakReference<>((Handler) ret));
}
return ret;
}
@TargetClass(value = "android.os.Handler",scope = Scope.SELF)
@TargetMethod(methodName = "createAsync")
@ReplaceInvoke(isStatic = true)
public static Handler createAsync(Looper looper) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Handler handler = Handler.createAsync(looper);
CopyOnWriteArraySet<WeakReference<Handler>> asyncHandlers = MainBlockCheck.Companion.getAsyncHandlers();
asyncHandlers.add(new WeakReference<>(handler));
return handler;
} else {
return new Handler(looper);
}
}
@TargetClass(value = "android.os.Handler",scope = Scope.SELF)
@TargetMethod(methodName = "createAsync")
@ReplaceInvoke(isStatic = true)
public static Handler createAsync(Looper looper, Handler.Callback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Handler handler = Handler.createAsync(looper, callback);
CopyOnWriteArraySet<WeakReference<Handler>> asyncHandlers = MainBlockCheck.Companion.getAsyncHandlers();
asyncHandlers.add(new WeakReference<>(handler));
return handler;
} else {
return new Handler(looper, callback);
}
}
}

View File

@@ -1,705 +0,0 @@
package com.mogo.launcher.lancet.jank
import android.app.*
import android.content.*
import android.graphics.*
import android.os.*
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.Handler.Callback
import android.util.*
import android.view.*
import androidx.annotation.*
import androidx.core.os.HandlerCompat
import androidx.core.util.Pools
import com.knightboost.lancet.api.*
import com.knightboost.lancet.api.Scope.ALL
import com.knightboost.lancet.api.Scope.LEAF
import com.knightboost.lancet.api.annotations.*
import com.knightboost.lancet.api.annotations.Weaver
import com.mogo.eagle.core.block.runtime.config.recorder.*
import com.mogo.eagle.core.block.runtime.utils.TimeUtils.Companion.now
import com.mogo.eagle.core.function.call.devatools.*
import com.mogo.eagle.core.utilcode.util.*
import kotlinx.coroutines.Runnable
import java.lang.ref.WeakReference
import java.util.concurrent.*
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.withLock
import com.mogo.eagle.core.block.runtime.message.Message as Holder
@Keep
@Weaver
@Group("main_block_check")
class MainBlockCheck {
companion object {
private const val TAG = "MAIN_BLOCK_CHECK"
private val handlerTokens by lazy { ConcurrentHashMap<String, CopyOnWriteArrayList<Holder>>() }
private val pool by lazy { Pools.SimplePool<ActionWrapper>(50) }
private val actions by lazy { ConcurrentHashMap<Runnable, ActionWrapper>() }
private val lock by lazy { ReentrantReadWriteLock() }
val asyncHandlers by lazy { CopyOnWriteArraySet<WeakReference<Handler>>() }
private val handlers by lazy { ConcurrentHashMap<Int, Boolean>() }
private val handlerWhats by lazy { ConcurrentHashMap<Int, CopyOnWriteArraySet<Int>>() }
private val main by lazy { Looper.getMainLooper() }
private fun acquireAction(msg: Holder, action: Runnable): ActionWrapper {
return lock.readLock().withLock {
val ret = pool.acquire() ?: ActionWrapper(msg, action)
ret.msg = msg
ret.action = action
ret
}
}
private fun releaseAction(wrapper: ActionWrapper) {
lock.writeLock().withLock { pool.release(wrapper) }
}
private data class ActionWrapper(var msg: Holder, var action: Runnable) : Runnable {
override fun run() {
var elapsedTime: Long = 0
var startTime: Long = 0
try {
val expect = msg.enqueueTime + msg.delay
val now = now()
elapsedTime = now - expect
startTime = now()
action.run()
} catch (t: Throwable) {
Log.e(TAG, "msg -> $msg", t)
throw t
} finally {
getRecorder()?.recycle(msg.id, elapsedTime, now() - startTime)
releaseAction(this)
actions.remove(action)
CallerDevaToolsManager.usage()?.updateMainThreadTime(Debug.threadCpuTimeNanos())
}
}
override fun toString(): String {
return action.toString()
}
}
@JvmStatic
fun getRecorder(): IMessageRecorder? {
val block = CallerDevaToolsManager.block()
if (block == null || !block.hasInit()) {
return null
}
return block.recorder()
}
@JvmStatic val callback by lazy {
Callback { msg ->
val enclose = msg.obj as? Holder
if (enclose == null) {
Log.w(TAG, "--- handleMessage --- 1 ---")
return@Callback true
}
if (!enclose.isValid()) {
Log.w(TAG, "--- handleMessage --- 2 ---:$enclose")
return@Callback true
}
val h = enclose.handler
val action = enclose.action
val oldMsg = enclose.origin
val oldObj = enclose.obj
val start = now()
try {
if (action != null) {
action.run()
} else {
if (oldMsg == null) {
h?.dispatchMessage(msg)
} else {
oldMsg.obj = oldObj
h?.dispatchMessage(oldMsg)
}
if (h != null) {
val key = generateKey(h, msg.what)
handlerTokens[key]?.remove(enclose)
}
}
} catch (t: Throwable) {
Log.e(TAG, "msg: $enclose", t)
throw t
} finally {
val now = now()
val duration = now - start
val expect = enclose.enqueueTime + enclose.delay
val elapsed = now - expect
getRecorder()?.recycle(enclose.id, elapsed, duration)
CallerDevaToolsManager.usage()?.updateMainThreadTime(Debug.threadCpuTimeNanos())
}
true
}
}
@JvmStatic val ASYNC_HANDLER by lazy {
HandlerCompat.createAsync(main, callback)
}
@JvmStatic val HANDLER by lazy {
Handler(main, callback)
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "post")
@ReplaceInvoke
fun handlerPost(handler: Handler, action: Runnable): Boolean {
if (main != handler.looper) {
return handler.post(action)
}
val holder = Holder.acquire(handler, action = action, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
val inserted = handler.post(acquireAction(holder, action).also { actions[action] = it })
if (inserted) {
getRecorder()?.insert(holder)
} else {
actions.remove(action)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "postDelayed")
@ReplaceInvoke
fun handlerPostDelay(handler: Handler, action: Runnable, delayMillis: Long): Boolean {
if (main != handler.looper) {
return handler.postDelayed(action, delayMillis)
}
val holder = Holder.acquire(handler, action = action, delay = delayMillis, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
val inserted = handler.postDelayed(acquireAction(holder, action).also { actions[action] = it }, delayMillis)
if (inserted) {
getRecorder()?.insert(holder)
} else {
actions.remove(action)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "postDelayed")
@ReplaceInvoke
fun handlerPostDelayWithToken(handler: Handler, action: Runnable, token: Any?, delayMillis: Long): Boolean {
if (main != handler.looper) {
return HandlerCompat.postDelayed(handler, action, token, delayMillis)
}
val holder = Holder.acquire(handler, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }, action = action, obj = token, delay = delayMillis)
val inserted = HandlerCompat.postDelayed(handler, acquireAction(holder, action).also { actions[action] = it }, token, delayMillis)
if (inserted) {
getRecorder()?.insert(holder)
} else {
actions.remove(action)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "postAtFrontOfQueue")
@ReplaceInvoke
fun handlerPostAtFrontOfQueue(handler: Handler, action: Runnable): Boolean {
if (main != handler.looper) {
return handler.postAtFrontOfQueue(action)
}
val holder = Holder.acquire(handler, action = action, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
val inserted = handler.postAtFrontOfQueue(acquireAction(holder, action).also { actions[action] = it })
if (inserted) {
getRecorder()?.insert(holder)
} else {
actions.remove(action)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "postAtTime")
@ReplaceInvoke
fun handlerPostAtTime(handler: Handler, action: Runnable, uptimeMillis: Long): Boolean {
if (main != handler.looper) {
return handler.postAtTime(action, uptimeMillis)
}
val delta = uptimeMillis - SystemClock.uptimeMillis()
val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
val inserted = handler.postAtTime(acquireAction(holder, action), uptimeMillis)
if (inserted) {
getRecorder()?.insert(holder)
} else {
actions.remove(action)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "postAtTime")
@ReplaceInvoke
fun handlerPostAtTimeWithToken(handler: Handler, action: Runnable, token: Any?, uptimeMillis: Long): Boolean {
if (main != handler.looper) {
return handler.postAtTime(action, uptimeMillis)
}
val delta = uptimeMillis - SystemClock.uptimeMillis()
val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0, obj = token, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
val inserted = handler.postAtTime(acquireAction(holder, action).also { actions[action] = it }, token, uptimeMillis)
if (inserted) {
getRecorder()?.insert(holder)
} else {
actions.remove(action)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "sendEmptyMessage")
@ReplaceInvoke
fun handlerSendEmptyMessage(handler: Handler, what: Int): Boolean {
if (main != handler.looper) {
return handler.sendEmptyMessage(what)
}
val key = handler.hashCode()
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
val newHandler = getHandler(isAsync)
val msg = Message.obtain(newHandler, what)
val holder = Holder.acquire(handler, what = what, isAsync = isAsync)
msg.obj = holder
val tokenKey = generateKey(handler, what)
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
val inserted = newHandler.sendMessage(msg)
if (inserted) {
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what)
getRecorder()?.insert(holder)
} else {
handlerTokens[tokenKey]?.remove(holder)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "sendEmptyMessageAtTime")
@ReplaceInvoke
fun handlerSendEmptyMessageAtTime(handler: Handler, what: Int, uptimeMillis: Long): Boolean {
if (main != handler.looper) {
return handler.sendEmptyMessageAtTime(what, uptimeMillis)
}
val key = handler.hashCode()
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
val newHandler = getHandler(isAsync)
val msg = Message.obtain(newHandler, what)
val delta = uptimeMillis - SystemClock.uptimeMillis()
val holder = Holder.acquire(handler, what = what, delay = if (delta > 0) delta else 0, isAsync = isAsync)
msg.obj = holder
val tokenKey = generateKey(handler, what)
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
val inserted = newHandler.sendMessageAtTime(msg, uptimeMillis)
if (inserted) {
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what)
getRecorder()?.insert(holder)
} else {
handlerTokens[tokenKey]?.remove(holder)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "sendEmptyMessageDelayed")
@ReplaceInvoke
fun handlerSendEmptyMessageDelayed(handler: Handler, what: Int, delayMillis: Long): Boolean {
if (main != handler.looper) {
return handler.sendEmptyMessageDelayed(what, delayMillis)
}
val key = handler.hashCode()
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
val newHandler = getHandler(isAsync)
val msg = Message.obtain(newHandler, what)
val holder = Holder.acquire(handler, what = what, delay = delayMillis, isAsync = isAsync)
msg.obj = holder
val tokenKey = generateKey(handler, what)
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
val inserted = newHandler.sendMessageDelayed(msg, delayMillis)
if (inserted) {
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what)
getRecorder()?.insert(holder)
} else {
handlerTokens[tokenKey]?.remove(holder)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "sendMessage")
@ReplaceInvoke
fun handlerSendMessage(handler: Handler, msg: Message): Boolean {
if (main != handler.looper) {
return handler.sendMessage(msg)
}
val tokenKey = generateKey(handler, msg.what)
val key = handler.hashCode()
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
val action = msg.callback
val holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync)
val inserted: Boolean
if (action != null) {
inserted = handler.post(acquireAction(holder, action).also { actions[action] = it })
} else {
msg.obj = holder
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
val newHandler = getHandler(isAsync)
inserted = newHandler.sendMessage(msg)
}
if (inserted) {
if (action == null) {
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(holder.what)
}
getRecorder()?.insert(holder)
} else {
if (action == null) {
handlerTokens[tokenKey]?.remove(holder)
} else {
actions.remove(action)
}
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "sendMessageAtTime")
@ReplaceInvoke
fun handlerSendMessageAtTime(handler: Handler, msg: Message, uptimeMillis: Long): Boolean {
if (main != handler.looper) {
return handler.sendMessageAtTime(msg, uptimeMillis)
}
val tokenKey = generateKey(handler, msg.what)
val key = handler.hashCode()
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
val action = msg.callback
val delta = uptimeMillis - SystemClock.uptimeMillis()
val holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync, delay = if (delta > 0) delta else 0)
val inserted: Boolean
if (action != null) {
inserted = handler.postAtTime(acquireAction(holder, action).also { actions[action] = it }, uptimeMillis)
} else {
msg.obj = holder
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
val newHandler = getHandler(isAsync)
inserted = newHandler.sendMessageAtTime(msg, uptimeMillis)
}
if (inserted) {
if (action == null) {
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(holder.what)
}
getRecorder()?.insert(holder)
} else {
if (action == null) {
handlerTokens[tokenKey]?.remove(holder)
} else {
actions.remove(action)
}
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "sendMessageAtFrontOfQueue")
@ReplaceInvoke
fun handlerSendMessageAtFrontOfQueue(handler: Handler, msg: Message): Boolean {
if (main != handler.looper) {
return handler.sendMessageAtFrontOfQueue(msg)
}
val tokenKey = generateKey(handler, msg.what)
val key = handler.hashCode()
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
val action = msg.callback
val holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync)
val inserted: Boolean
if (action != null) {
inserted = handler.postAtFrontOfQueue(acquireAction(holder, action).also { actions[action] = it })
} else {
msg.obj = holder
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
val newHandler = getHandler(isAsync)
inserted = newHandler.sendMessageAtFrontOfQueue(msg)
}
if (inserted) {
if (action == null) {
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(holder.what)
}
getRecorder()?.insert(holder)
} else {
if (action == null) {
handlerTokens[tokenKey]?.remove(holder)
} else {
actions.remove(action)
}
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "hasCallbacks")
@ReplaceInvoke
fun handlerHasCallbacks(handler: Handler, action: Runnable): Boolean {
if (main != handler.looper) {
return if (VERSION.SDK_INT >= VERSION_CODES.Q) {
handler.hasCallbacks(action)
} else {
HandlerCompat.hasCallbacks(handler, action)
}
}
val existed = actions[action]
if (existed != null) {
return HandlerCompat.hasCallbacks(handler, existed)
}
return false
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "hasMessages")
@ReplaceInvoke
fun handlerHasMessages(handler: Handler, what: Int): Boolean {
if (main != handler.looper) {
return handler.hasMessages(what)
}
return getHandler(computeIsAsyncHandler(handler)).hasMessages(what)
}
@JvmStatic
@TargetClass(value = "android.app.Activity", scope = ALL)
@TargetMethod(methodName = "post")
@ReplaceInvoke
fun runOnUiThreadOfActivity(activity: Activity, action: Runnable) {
val holder = Holder.acquire(null, action = action)
try {
activity.runOnUiThread(acquireAction(holder, action))
} finally {
getRecorder()?.insert(holder)
}
}
@JvmStatic
@TargetClass(value = "android.view.View", scope = ALL)
@TargetMethod(methodName = "post")
@ReplaceInvoke
fun postOfView(view: View, action: Runnable): Boolean {
val holder = Holder.acquire(null, action = action)
val inserted = view.post(acquireAction(holder, action).also { actions[action] = it })
if (inserted) {
getRecorder()?.insert(holder)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.view.View", scope = ALL)
@TargetMethod(methodName = "postDelayed")
@ReplaceInvoke
fun postDelayedOfView(view: View, action: Runnable, delayMillis: Long): Boolean {
val holder = Holder.acquire(null, action = action, delay = delayMillis)
val inserted = view.postDelayed(acquireAction(holder, action).also { actions[action] = it }, delayMillis)
if (inserted) {
getRecorder()?.insert(holder)
}
return inserted
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "removeCallbacks")
@ReplaceInvoke
fun handlerRemoveCallbacks(handler: Handler, action: Runnable) {
if (main != handler.looper) {
return handler.removeCallbacks(action)
}
val existed = actions.remove(action)
if (existed != null) {
try {
handler.removeCallbacks(existed)
} finally {
getRecorder()?.remove(existed.msg)
}
}
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "removeCallbacks")
@ReplaceInvoke
fun handlerRemoveCallbacksWithToken(handler: Handler, action: Runnable, token: Any?) {
if (main != handler.looper) {
return handler.removeCallbacks(action, token)
}
val existed = actions.remove(action)
if (existed != null) {
try {
handler.removeCallbacks(existed, token)
} finally {
getRecorder()?.remove(existed.msg)
}
}
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "removeMessages")
@ReplaceInvoke
fun handlerRemoveMessages(handler: Handler, what: Int) {
if (main != handler.looper) {
return handler.removeMessages(what)
}
val newHandler = getHandler(handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
val tokens = handlerTokens.remove(generateKey(handler, what))
if (!tokens.isNullOrEmpty()) {
val snapshot = ArrayList<Holder>(tokens)
snapshot.forEach {
newHandler.removeMessages(what, it)
}
getRecorder()?.remove(snapshot)
}
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "removeCallbacksAndMessages")
@ReplaceInvoke
fun handlerRemoveCallbacksAndMessages(handler: Handler, token: Any?) {
if (main != handler.looper) {
return handler.removeCallbacksAndMessages(token)
}
handler.removeCallbacksAndMessages(token)
val existed = handlerWhats[handler.hashCode()]
if (!existed.isNullOrEmpty()) {
existed.forEach {what ->
handlerTokens.remove(generateKey(handler, what))?.forEach { msg ->
getHandler(handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }).removeMessages(what, msg)
}
}
}
}
@JvmStatic
@TargetClass(value = "android.view.View", scope = ALL)
@TargetMethod(methodName = "removeCallbacks")
@ReplaceInvoke
fun viewRemoveCallback(view: View, action: Runnable): Boolean {
val exist = actions.remove(action)
if (exist != null) {
try {
return view.removeCallbacks(exist)
} finally {
getRecorder()?.remove(exist.msg)
}
}
return false
}
@JvmStatic
@TargetClass(value = "android.os.Handler", scope = ALL)
@TargetMethod(methodName = "removeMessages")
@ReplaceInvoke
fun handlerRemoveMessagesWithToken(handler: Handler, what: Int, token: Any?) {
if (main != handler.looper) {
return handler.removeMessages(what, token)
}
val newHandler = getHandler(handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
val tokens = handlerTokens.remove(generateKey(handler, what))
if (!tokens.isNullOrEmpty()) {
val snapshot = ArrayList<Holder>(tokens)
snapshot.forEach {
newHandler.removeMessages(what, it)
}
getRecorder()?.remove(snapshot)
}
}
private fun generateKey(handler: Handler, what: Int) : String {
return "${handler.hashCode()}_$what"
}
private fun computeIsAsyncHandler(handler: Handler): Boolean {
return asyncHandlers.find { it.get() === handler } != null
}
private fun getHandler(isAsync: Boolean): Handler {
return if (isAsync) {
ASYNC_HANDLER
} else {
HANDLER
}
}
}
@TargetClass(value = "android.app.Activity", scope = LEAF)
@Insert(mayCreateSuper = true)
@TargetMethod(methodName = "onCreate")
fun proxyActivityOnCreate(savedInstanceState: Bundle?) {
try {
Origin.callVoid()
} finally {
CallerDevaToolsManager.block()?.monitor((This.get() as Activity).window)
}
}
@TargetClass(value = "android.app.Activity", scope = LEAF)
@Insert(mayCreateSuper = true)
@TargetMethod(methodName = "onResume")
fun proxyActivityOnResume() {
try {
Origin.callVoid()
} finally {
CallerDevaToolsManager.block()?.resume((This.get() as Activity).window)
}
}
@TargetClass(value = "android.app.Activity", scope = LEAF)
@Insert(mayCreateSuper = true)
@TargetMethod(methodName = "onPause")
fun proxyActivityOnPause() {
try {
Origin.callVoid()
} finally {
CallerDevaToolsManager.block()?.pause((This.get() as Activity).window)
}
}
@TargetClass(value = "android.app.Activity", scope = LEAF)
@Insert(mayCreateSuper = true)
@TargetMethod(methodName = "onDestroy")
fun proxyActivityOnDestroy() {
try {
Origin.callVoid()
} finally {
CallerDevaToolsManager.block()?.pop((This.get() as Activity).window)
}
}
}

View File

@@ -8,6 +8,7 @@ import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import android.content.pm.PackageManager;
import android.opengl.GLSurfaceView;
import android.os.SystemClock;
import android.view.View;
import android.view.ViewManager;
@@ -29,18 +30,14 @@ class JankPointAutoGenerator {
@LancetXGenerator(
group = "main_block_check",
type = Type.IPC,
hookAllPublicMethods = true,
excludeHookMethodNames = { "toString", "hashCode" },
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
hookAllPublicMethods = true
)
private ActivityManager am;
@LancetXGenerator(
group = "main_block_check",
type = Type.IPC,
hookAllPublicMethods = true,
excludeHookMethodNames = { "toString", "hashCode" },
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
hookAllPublicMethods = true
)
private PackageManager pm;
@@ -51,8 +48,28 @@ class JankPointAutoGenerator {
hookAllPublicMethods = true,
scope = Scope.LEAF,
hookType = HookType.AROUND,
excludeHookMethodNames = { "runOnUiThread", "toString", "hashCode", "onPictureInPictureUiStateChanged", "enterPictureInPictureMode", "setPictureInPictureParams" },
excludeHookMethodDescs = { "(Ljava/lang/Runnable;)V", "()Ljava/lang/String;", "()I", "(Landroid/app/PictureInPictureUiState;)V", "(Landroid/app/PictureInPictureParams;)Z", "(Landroid/app/PictureInPictureParams;)V" }
onlyHookMethodNames = {
"onCreate",
"onStart",
"onResume",
"onPause",
"onStop",
"onDestroy",
"onWindowFocusChanged",
"dispatchKeyEvent",
"dispatchTouchEvent"
},
onlyHookMethodDescs = {
"(Landroid/os/Bundle;)V",
"()V",
"()V",
"()V",
"()V",
"()V",
"(Z)V",
"(Landroid/view/KeyEvent;)Z",
"(Landroid/view/MotionEvent;)Z"
}
)
private Activity activity;
@@ -63,8 +80,12 @@ class JankPointAutoGenerator {
hookType = HookType.AROUND,
hookAllPublicMethods = true,
scope = Scope.LEAF,
excludeHookMethodNames = { "toString", "hashCode" },
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
onlyHookMethodNames = {
"onReceive"
},
onlyHookMethodDescs = {
"(Landroid/content/Context;Landroid/content/Intent;)V"
}
)
private BroadcastReceiver receiver;
@@ -75,8 +96,26 @@ class JankPointAutoGenerator {
hookType = HookType.AROUND,
hookAllPublicMethods = true,
scope = Scope.LEAF,
excludeHookMethodNames = { "toString", "hashCode" },
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
onlyHookMethodNames = {
"attachInfo",
"onCreate",
"query",
"getType",
"insert",
"update",
"delete",
"openFile"
},
onlyHookMethodDescs = {
"(Landroid/content/Context;Landroid/content/pm/ProviderInfo;)V",
"()Z",
"(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;",
"(Landroid/net/Uri;)Ljava/lang/String;",
"(Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;",
"(Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I",
"(Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I",
"(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;"
}
)
private ContentProvider provider;
@@ -87,8 +126,32 @@ class JankPointAutoGenerator {
mayCreateSuper = true,
hookType = HookType.AROUND,
scope = Scope.LEAF,
excludeHookMethodNames = { "toString", "hashCode" },
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
onlyHookMethodNames = {
"onCreate",
"onStart",
"onStartCommand",
"onDestroy",
"onConfigurationChanged",
"onLowMemory",
"onTrimMemory",
"onBind",
"onUnbind",
"onRebind",
"onTaskRemoved"
},
onlyHookMethodDescs = {
"()V",
"(Landroid/content/Intent;I)V",
"(Landroid/content/Intent;II)I",
"()V",
"(Landroid/content/res/Configuration;)V",
"()V",
"(I)V",
"(Landroid/content/Intent;)Landroid/os/IBinder;",
"(Landroid/content/Intent;)Z",
"(Landroid/content/Intent;)V",
"(Landroid/content/Intent;)V",
}
)
private Service service;
@@ -106,8 +169,24 @@ class JankPointAutoGenerator {
group = "main_block_check",
type = Type.SLEEP,
scope = Scope.ALL,
onlyHookMethodNames = { "sleep", "sleep"},
onlyHookMethodDescs = { "(J)V", "(JI)V"}
onlyHookMethodNames = {
"sleep",
"sleep",
"join",
"join",
"join",
"getStackTrace",
"getAllStackTraces"
},
onlyHookMethodDescs = {
"(J)V",
"(JI)V",
"(J)V",
"(JI)V",
"()V",
"()[Ljava/lang/StackTraceElement;",
"()Ljava/util/Map;"
}
)
private Thread thread;
@@ -118,9 +197,7 @@ class JankPointAutoGenerator {
hookType = HookType.AROUND,
scope = Scope.LEAF,
mayCreateSuper = true,
hookAllPublicMethods = true,
excludeHookMethodNames = { "toString", "hashCode" },
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
hookAllPublicMethods = true
)
private ISynchronizedLockHooker synchronizedLock;
@@ -129,9 +206,7 @@ class JankPointAutoGenerator {
group = "main_block_check",
type = Type.PARK,
scope = Scope.ALL,
hookAllPublicMethods = true,
excludeHookMethodNames = { "unpark", "getBlocker", "toString", "hashCode" },
excludeHookMethodDescs = { "(Ljava/lang/Thread;)V", "(Ljava/lang/Thread;)Ljava/lang/Object;", "()Ljava/lang/String;", "()I"}
hookAllPublicMethods = true
)
private LockSupport park;
@@ -165,9 +240,44 @@ class JankPointAutoGenerator {
hookType = HookType.AROUND,
mayCreateSuper = true,
hookAllPublicMethods = true,
excludeHookMethodNames = { "toString", "hashCode" },
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
onlyHookMethodNames = {
"onAttach",
"onCreate",
"onCreateView",
"onViewCreated",
"onActivityCreated",
"onViewStateRestored",
"onStart",
"onResume",
"onSaveInstanceState",
"onConfigurationChanged",
"onPause",
"onStop",
"onLowMemory",
"onTrimMemory",
"onDestroyView",
"onDestroy",
"onDetach",
},
onlyHookMethodDescs = {
"(Landroid/content/Context;)V",
"(Landroid/os/Bundle;)",
"(Landroid/view/LayoutInflater;Landroid/view/ViewGroup;Landroid/os/Bundle;)Landroid/view/View;",
"(Landroid/view/View;Landroid/os/Bundle;)V",
"(Landroid/os/Bundle;)V",
"(Landroid/os/Bundle;)V",
"()V",
"()V",
"(Landroid/os/Bundle;)V",
"(Landroid/content/res/Configuration;)V",
"()V",
"()V",
"()V",
"(I)V",
"()V",
"()V",
"()V",
}
)
private Fragment systemFragment;
@@ -178,8 +288,42 @@ class JankPointAutoGenerator {
hookType = HookType.AROUND,
mayCreateSuper = true,
hookAllPublicMethods = true,
excludeHookMethodNames = { "toString", "hashCode" },
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
onlyHookMethodNames = {
"onAttach",
"onCreate",
"onCreateView",
"onViewCreated",
"onActivityCreated",
"onViewStateRestored",
"onStart",
"onResume",
"onSaveInstanceState",
"onConfigurationChanged",
"onPause",
"onStop",
"onLowMemory",
"onDestroyView",
"onDestroy",
"onDetach",
},
onlyHookMethodDescs = {
"(Landroid/content/Context;)V",
"(Landroid/os/Bundle;)",
"(Landroid/view/LayoutInflater;Landroid/view/ViewGroup;Landroid/os/Bundle;)Landroid/view/View;",
"(Landroid/view/View;Landroid/os/Bundle;)V",
"(Landroid/os/Bundle;)V",
"(Landroid/os/Bundle;)V",
"()V",
"()V",
"(Landroid/os/Bundle;)V",
"(Landroid/content/res/Configuration;)V",
"()V",
"()V",
"()V",
"()V",
"()V",
"()V",
}
)
private androidx.fragment.app.Fragment androidxFragment;
@@ -234,4 +378,20 @@ class JankPointAutoGenerator {
}
)
private ViewManager manager;
@LancetXGenerator(
group = "main_block_check",
type = Type.OTHER,
scope = Scope.LEAF,
mayCreateSuper = true,
hookType = HookType.AROUND,
onlyHookMethodNames = {
"onDrawFrame"
},
onlyHookMethodDescs = {
"(Ljavax/microedition/khronos/opengles/GL10;)V"
}
)
private GLSurfaceView.Renderer renderer;
}

View File

@@ -0,0 +1,146 @@
package com.mogo.launcher.lancet.jank.spi
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.os.SystemClock
import com.google.auto.service.AutoService
import com.mogo.eagle.core.block.runtime.config.recorder.IMessageRecorder
import com.mogo.eagle.core.block.runtime.utils.TimeUtils.Companion.now
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager.block
import com.mogo.eagle.core.function.main.ARouterUtils
import com.mogo.eagle.core.handler.loader.IHandlerHooker
import com.mogo.eagle.core.block.runtime.message.Message as Msg
@AutoService(IHandlerHooker::class)
class HandlerHookerImpl : IHandlerHooker {
private val mainLooper = Looper.getMainLooper()
private var recorder: IMessageRecorder? = null
private var usage: IMoGoCpuUsageProvider? = null
private var dispatchTime = 0L
override fun onMessageDispatchAfter(msg: Message) {
if (msg.target.looper != mainLooper) {
return
}
if (dispatchTime <= 0) {
return
}
val duration = now() - dispatchTime
checkIfNeed(msg.target)?.recycle(Msg.acquire(msg.target, msg = msg, duration = duration, elapsed = SystemClock.uptimeMillis() - msg.`when`, enqueue = msg.`when`))
}
override fun onMessageDispatchBefore(msg: Message) {
if (msg.target.looper != mainLooper) {
return
}
dispatchTime = now()
}
override fun onRemoveCallbacks(handler: Handler, action: Runnable) {
checkIfNeed(handler)?.remove(Msg.acquire(handler, action = action))
}
override fun onRemoveCallbacksAndMessages(handler: Handler, token: Any?) {
checkIfNeed(handler)?.remove(Msg.acquire(handler, obj = token))
}
override fun onRemoveCallbacksWithToken(handler: Handler, action: Runnable, token: Any?) {
checkIfNeed(handler)?.remove(Msg.acquire(handler, action = action, obj = token))
}
override fun onRemoveMessages(handler: Handler, what: Int) {
checkIfNeed(handler)?.remove(Msg.acquire(handler, what = what))
}
override fun onRemoveMessages(handler: Handler, what: Int, obj: Any?) {
checkIfNeed(handler)?.remove(Msg.acquire(handler, what = what, obj = obj))
}
override fun onPost(handler: Handler, action: Runnable) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action))
}
override fun onPostAtFrontQueue(handler: Handler, action: Runnable) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action))
}
override fun onPostAtTime(handler: Handler, action: Runnable, token: Any?, uptimeMillis: Long) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action, obj = token, delay = uptimeMillis - SystemClock.uptimeMillis()))
}
override fun onPostAtTime(handler: Handler, action: Runnable, uptimeMillis: Long) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action, delay = uptimeMillis - SystemClock.uptimeMillis()))
}
override fun onPostDelayed(handler: Handler, action: Runnable, token: Any?, delayMillis: Long) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action, obj = token, delay = delayMillis))
}
override fun onPostDelayed(handler: Handler, action: Runnable, delayMillis: Long) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action, delay = delayMillis))
}
override fun onSendEmptyMessage(handler: Handler, what: Int) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, what = what))
}
override fun onSendEmptyMessageAtTime(handler: Handler, what: Int, uptimeMillis: Long) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, what = what, delay = uptimeMillis - SystemClock.uptimeMillis()))
}
override fun onSendEmptyMessageDelayed(handler: Handler, what: Int, delayMillis: Long) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, what = what, delay = delayMillis))
}
override fun onSendMessage(handler: Handler, msg: Message) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, msg, what = msg.what))
}
override fun onSendMessageAtFrontOfQueue(handler: Handler, msg: Message) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, msg, what = msg.what))
}
override fun onSendMessageAtTime(handler: Handler, msg: Message, uptimeMillis: Long) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, msg = msg, delay = uptimeMillis - SystemClock.uptimeMillis()))
}
override fun onSendMessageDelayed(handler: Handler, msg: Message, delayMillis: Long) {
checkIfNeed(handler)?.insert(Msg.acquire(handler, msg = msg, delay = delayMillis))
}
private fun checkIfNeed(handler: Handler?): IMessageRecorder? {
var usage = this.usage
if (usage == null && ARouterUtils.isInit.get()) {
usage = CallerDevaToolsManager.usage()
this.usage = usage
}
if (handler != null) {
if (handler.looper != mainLooper) {
usage?.updateOtherThreadTime()
} else {
usage?.updateMainThreadTime()
}
}
if (handler != null && handler.looper != mainLooper) {
return null
}
if (recorder == null) {
val hasInit = ARouterUtils.isInit.get()
if (hasInit && recorder == null) {
val block = block()
if (block != null && block.hasInit()) {
recorder = block.recorder()
}
}
}
return recorder
}
}

View File

@@ -1,196 +0,0 @@
package com.mogo.launcher.lancet.jank.spi;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.auto.service.AutoService;
import com.mogo.core.lancetx.compiler.lib.generator.Type;
import com.mogo.core.lancetx.compiler.lib.hook.IHookInvoker;
import com.mogo.eagle.core.data.deva.chain.ChainConstant;
import com.zhjt.service.chain.ChainLog;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@AutoService(IHookInvoker.class)
public class HookHandler implements IHookInvoker {
private final Looper mainLooper = Looper.getMainLooper();
private final AtomicLong startTime = new AtomicLong(0L);
private final AtomicLong syncLockStartTime = new AtomicLong(0L);
private final ThreadLocal<StringBuilder> message = new ThreadLocal<>();
private final ThreadLocal<Object> monitor = new ThreadLocal<>();
private final AtomicReference<Thread> holder = new AtomicReference<>();
private String holderDesc = null;
@Override
public void i(@NonNull Type type, Object caller, @NonNull String methodName, @NonNull Object... objects) {
if (Looper.myLooper() == mainLooper) {
if (type == Type.SYNCHRONIZED_LOCK) {
if ("onMonitorBefore".equals(methodName)) {
syncLockStartTime.set(SystemClock.elapsedRealtime());
if (objects.length > 0) {
this.monitor.set(objects[0]);
}
Thread holder = this.holder.get();
if (holder != null) {
holderDesc = holder.toString();
}
}
if ("onMonitorEnter".equals(methodName)) {
long elapsedTime = SystemClock.elapsedRealtime() - syncLockStartTime.get();
if (elapsedTime >= 10) {
StringBuilder builder = message.get();
if (builder == null) {
builder = new StringBuilder();
message.set(builder);
}
if (builder.length() > 0) {
builder.setLength(0);
}
builder
.append(type)
.append("#")
.append(caller == null ? "caller is null" : caller.getClass().getSimpleName())
.append("#")
.append(methodName);
if (objects.length > 0) {
builder.append("#(");
}
for (Object o : objects) {
if (o == null) {
continue;
}
builder.append(o.getClass().getSimpleName())
.append(",");
}
if (objects.length > 0) {
builder.setLength(builder.length() - 1);
builder.append(")");
}
builder.append("#").append(elapsedTime);
Object monitor = this.monitor.get();
if (monitor != null) {
builder.append("#").append(monitor);
}
if (holderDesc != null) {
builder.append("#").append(holderDesc);
}
builder.append("\n");
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement trace: stackTrace) {
builder.append(trace.getClassName()).append("#").append(trace.getMethodName()).append("#").append(trace.getLineNumber()).append("\n");
}
builder.setLength(builder.length() - 1);
Log.w("HookHandler", "Jank detected:" + builder);
linkedLog(type, builder.toString());
}
}
if ("onMonitorExit".equals(methodName)) {
this.monitor.remove();
holderDesc = null;
}
} else {
startTime.set(SystemClock.elapsedRealtime());
}
} else {
if (type == Type.SYNCHRONIZED_LOCK) {
if ("onMonitorBefore".equals(methodName)) {
if (objects.length > 0) {
this.monitor.set(objects[0]);
}
}
if ("onMonitorEnter".equals(methodName)) {
holder.set(Thread.currentThread());
}
if ("onMonitorExit".equals(methodName)) {
this.monitor.remove();
holder.set(null);
}
}
}
}
@Override
public void o(@NonNull Type type, Object caller, @NonNull String methodName, @NonNull Object... objects) {
if (type != Type.SYNCHRONIZED_LOCK && Looper.myLooper() == mainLooper) {
long startTime = this.startTime.get();
if (startTime > 0) {
long delta = SystemClock.elapsedRealtime() - startTime;
if (delta >= 10) {
StringBuilder builder = message.get();
if (builder == null) {
builder = new StringBuilder();
message.set(builder);
}
if (builder.length() > 0) {
builder.setLength(0);
}
builder
.append(type)
.append("#")
.append(caller == null ? "caller is null" : caller.getClass().getSimpleName())
.append("#")
.append(methodName);
if (objects.length > 0) {
builder.append("#(");
}
for (Object o : objects) {
if (o == null) {
continue;
}
builder.append(o.getClass().getSimpleName())
.append(",");
}
if (objects.length > 0) {
builder.setLength(builder.length() - 1);
builder.append(")");
}
builder.append("#").append(delta);
if (type == Type.AQS_LOCK) {
builder.append("\n");
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement trace: stackTrace) {
builder.append(trace.getClassName()).append("#").append(trace.getMethodName()).append("#").append(trace.getLineNumber()).append("\n");
}
builder.setLength(builder.length() - 1);
}
Log.w("HookHandler", "Jank Detected:" + builder);
linkedLog(type, builder.toString());
}
}
}
}
private void linkedLog(Type type, String msg) {
try {
linkedLogInternal(type,msg);
} catch (Throwable t) {
t.printStackTrace();
}
}
@ChainLog(
linkChainLog = ChainConstant.CHAIN_TYPE_ANR_LEAK,
linkCode = ChainConstant.CHAIN_SOURCE_HMI,
nodeAliasCode = ChainConstant.CHAIN_CODE_MAIN_BLOCK,
paramIndexes = { 0, 1 }
)
private void linkedLogInternal(Type type, String msg) {}
}

View File

@@ -0,0 +1,277 @@
package com.mogo.launcher.lancet.jank.spi;
import android.app.Activity;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import com.google.auto.service.AutoService;
import com.mogo.core.lancetx.compiler.lib.generator.Type;
import com.mogo.core.lancetx.compiler.lib.hook.IHookInvoker;
import com.mogo.eagle.core.data.deva.chain.ChainConstant;
import com.mogo.eagle.core.function.api.devatools.block.IMoGoBlockProvider;
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider;
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
import com.mogo.eagle.core.function.main.ARouterUtils;
import com.zhjt.service.chain.ChainLog;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@AutoService(IHookInvoker.class)
public class HookInvokerImpl implements IHookInvoker {
private final Looper mainLooper = Looper.getMainLooper();
private final ThreadLocal<Long> startTime = new ThreadLocal<>();
private final AtomicLong syncLockStartTime = new AtomicLong(0L);
private final ThreadLocal<StringBuilder> message = new ThreadLocal<>();
private final ThreadLocal<Object> monitor = new ThreadLocal<>();
private final AtomicReference<Thread> holder = new AtomicReference<>();
private String holderDesc = null;
private volatile IMoGoCpuUsageProvider provider;
private volatile boolean getProviderRequested = false;
private final long logThreshold = 5; //日志打印阈值
private final long dumpStackThreshold = 20; // dump堆栈阈值
// 切记: 请勿在此方法中调用其它模块类中的api,可能会出现StackOverFlowException
@Override
public void i(Type type, Object caller,String methodName, Object... objects) {
startTime.set(SystemClock.elapsedRealtime());
boolean isMainThread = false;
if (mainLooper == Looper.myLooper()) {
isMainThread = true;
if (type == Type.SYNCHRONIZED_LOCK) {
handleSynchronizedLock(Looper.myLooper() == mainLooper, caller, methodName, objects);
}
}
if (!getProviderRequested && provider == null && mainLooper != Looper.myLooper() && ARouterUtils.isInit.get()) {
getProviderRequested = true;
new Thread(() -> {
try {
provider = CallerDevaToolsManager.INSTANCE.usage();
} finally {
getProviderRequested = false;
}
}, "getProvider").start();
}
if (provider != null) {
if (isMainThread) {
provider.updateMainThreadTime();
} else {
provider.updateOtherThreadTime();
}
}
}
// 切记: 请勿在此方法中调用其它模块类中的api,可能会出现StackOverFlowException
@Override
public void o(Type type, Object caller,String methodName, Object... objects) {
long now = SystemClock.elapsedRealtime();
Long old = startTime.get();
long cost = now - (old == null ? now : old);
if (mainLooper == Looper.myLooper() && type != Type.SYNCHRONIZED_LOCK) {
handleCostTimeRecord(type, caller, methodName, cost, objects);
}
if (mainLooper == Looper.myLooper() && type == Type.ACTIVITY) {
handleActivity((Activity) caller, methodName);
}
if (provider != null) {
if (mainLooper == Looper.myLooper()) {
provider.updateMainThreadTime();
} else {
provider.updateOtherThreadTime();
}
}
}
private void handleCostTimeRecord(Type type, Object caller, String methodName, long cost, Object... args) {
if (cost >= logThreshold) {
StringBuilder builder = message.get();
if (builder == null) {
builder = new StringBuilder();
message.set(builder);
}
if (builder.length() > 0) {
builder.setLength(0);
}
builder
.append(type)
.append("#")
.append(caller == null ? "caller is null" : caller.getClass().getSimpleName())
.append("#")
.append(methodName);
if (args.length > 0) {
builder.append("#(");
}
for (Object o : args) {
if (o == null) {
continue;
}
builder.append(o.getClass().getSimpleName())
.append(",");
}
if (args.length > 0) {
builder.setLength(builder.length() - 1);
builder.append(")");
}
builder.append("#").append(cost);
if (cost >= dumpStackThreshold) {
builder.append("\n");
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement trace: stackTrace) {
builder.append(trace.getClassName()).append("#").append(trace.getMethodName()).append("#").append(trace.getLineNumber()).append("\n");
}
builder.setLength(builder.length() - 1);
}
Log.w("HookHandler", "Jank Detected:" + builder);
linkedLog(type, builder.toString());
}
}
private void handleActivity(Activity caller, String methodName) {
if ("onCreate".equals(methodName)) {
IMoGoBlockProvider block = CallerDevaToolsManager.INSTANCE.block();
if (block != null && block.hasInit()) {
block.monitor(caller.getWindow());
}
}
if ("onResume".equals(methodName)) {
IMoGoBlockProvider block = CallerDevaToolsManager.INSTANCE.block();
if (block != null && block.hasInit()) {
block.resume(caller.getWindow());
}
}
if ("onPause".equals(methodName)) {
IMoGoBlockProvider block = CallerDevaToolsManager.INSTANCE.block();
if (block != null && block.hasInit()) {
block.pause(caller.getWindow());
}
}
if ("onDestroy".equals(methodName)) {
IMoGoBlockProvider block = CallerDevaToolsManager.INSTANCE.block();
if (block != null && block.hasInit()) {
block.pop(caller.getWindow());
}
}
}
private void handleSynchronizedLock(boolean isMainThread,Object caller, String methodName, Object [] objects) {
if (isMainThread) {
if ("onMonitorBefore".equals(methodName)) {
syncLockStartTime.set(SystemClock.elapsedRealtime());
if (objects.length > 0) {
this.monitor.set(objects[0]);
}
Thread holder = this.holder.get();
if (holder != null) {
holderDesc = holder.toString();
}
}
if ("onMonitorEnter".equals(methodName)) {
long elapsedTime = SystemClock.elapsedRealtime() - syncLockStartTime.get();
if (elapsedTime >= logThreshold) {
StringBuilder builder = message.get();
if (builder == null) {
builder = new StringBuilder();
message.set(builder);
}
if (builder.length() > 0) {
builder.setLength(0);
}
builder
.append(Type.SYNCHRONIZED_LOCK)
.append("#")
.append(caller == null ? "caller is null" : caller.getClass().getSimpleName())
.append("#")
.append(methodName);
if (objects.length > 0) {
builder.append("#(");
}
for (Object o : objects) {
if (o == null) {
continue;
}
builder.append(o.getClass().getSimpleName())
.append(",");
}
if (objects.length > 0) {
builder.setLength(builder.length() - 1);
builder.append(")");
}
builder.append("#").append(elapsedTime);
Object monitor = this.monitor.get();
if (monitor != null) {
builder.append("#").append(monitor);
}
if (holderDesc != null) {
builder.append("#").append(holderDesc);
}
if (elapsedTime >= dumpStackThreshold) {
builder.append("\n");
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement trace: stackTrace) {
builder.append(trace.getClassName()).append("#").append(trace.getMethodName()).append("#").append(trace.getLineNumber()).append("\n");
}
builder.setLength(builder.length() - 1);
}
Log.w("HookHandler", "Jank detected:" + builder);
linkedLog(Type.SYNCHRONIZED_LOCK, builder.toString());
}
}
if ("onMonitorExit".equals(methodName)) {
this.monitor.remove();
holderDesc = null;
}
} else {
if ("onMonitorBefore".equals(methodName)) {
if (objects.length > 0) {
this.monitor.set(objects[0]);
}
}
if ("onMonitorEnter".equals(methodName)) {
holder.set(Thread.currentThread());
}
if ("onMonitorExit".equals(methodName)) {
this.monitor.remove();
holder.set(null);
}
}
}
private void linkedLog(Type type, String msg) {
try {
linkedLogInternal(type,msg);
} catch (Throwable t) {
t.printStackTrace();
}
}
@ChainLog(
linkChainLog = ChainConstant.CHAIN_TYPE_ANR_LEAK,
linkCode = ChainConstant.CHAIN_SOURCE_HMI,
nodeAliasCode = ChainConstant.CHAIN_CODE_MAIN_BLOCK,
paramIndexes = { 0, 1 }
)
private void linkedLogInternal(Type type, String msg) {}
}

View File

@@ -0,0 +1,97 @@
package com.mogo.launcher.lancet.jank.ui
import android.app.Activity
import android.view.View
import com.knightboost.lancet.api.Scope
import com.knightboost.lancet.api.annotations.Group
import com.knightboost.lancet.api.annotations.ReplaceInvoke
import com.knightboost.lancet.api.annotations.TargetClass
import com.knightboost.lancet.api.annotations.TargetMethod
import com.knightboost.lancet.api.annotations.Weaver
import com.mogo.eagle.core.block.runtime.message.Message
import com.mogo.eagle.core.block.runtime.utils.TimeUtils.Companion.now
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
import java.util.concurrent.ConcurrentHashMap
@Weaver
@Group("main_block_check")
class UiPostLancet {
internal data class ActionWrapper(var action: Runnable): Runnable {
override fun run() {
val start = now()
try {
action.run()
} finally {
CallerDevaToolsManager.block()?.takeIf {
it.hasInit()
}?.recorder()?.recycle(Message.acquire(handler = null, action = action, duration = now() - start))
}
}
}
companion object {
private val map by lazy { ConcurrentHashMap<Runnable, ActionWrapper>() }
@JvmStatic
@TargetClass(value = "android.app.Activity", scope = Scope.ALL)
@TargetMethod(methodName = "runOnUiThread")
@ReplaceInvoke
fun runOnUiThreadOfActivity(activity: Activity, action: Runnable) {
try {
activity.runOnUiThread(ActionWrapper(action).also { map[action] = it })
} finally {
CallerDevaToolsManager.block()?.takeIf {
it.hasInit()
}?.recorder()?.insert(Message.acquire(handler = null, action = action, extra = mapOf(activity.javaClass.name to "runOnUiThread")))
}
}
@JvmStatic
@TargetClass(value = "android.view.View", scope = Scope.ALL)
@TargetMethod(methodName = "post")
@ReplaceInvoke
fun post(view: View, action: Runnable): Boolean {
return try {
view.post(ActionWrapper(action).also { map[action] = it })
} finally {
CallerDevaToolsManager.block()?.takeIf {
it.hasInit()
}?.recorder()?.insert(Message.acquire(handler = null, action = action, extra = mapOf(view.javaClass.name to "post")))
}
}
@JvmStatic
@TargetClass(value = "android.view.View", scope = Scope.ALL)
@TargetMethod(methodName = "postDelayed")
@ReplaceInvoke
fun postDelayed(view: View, action: Runnable, delayMillis: Long): Boolean {
return try {
view.postDelayed(action, delayMillis)
} finally {
CallerDevaToolsManager.block()?.takeIf {
it.hasInit()
}?.recorder()?.insert(Message.acquire(handler = null, action = action, extra = mapOf(view.javaClass.name to "post")))
}
}
@JvmStatic
@TargetClass(value = "android.view.View", scope = Scope.ALL)
@TargetMethod(methodName = "removeCallbacks")
@ReplaceInvoke
fun removeCallbacks(view: View, action: Runnable): Boolean {
return try {
view.removeCallbacks(map.remove(action))
} finally {
CallerDevaToolsManager.block()?.takeIf {
it.hasInit()
}?.recorder()?.remove(Message.acquire(handler = null, action = action, extra = mapOf(view.javaClass.name to "removeCallbacks")))
}
}
}
}

View File

@@ -3,12 +3,14 @@ package com.mogo.launcher.startup
import android.app.Application
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.function.main.threadopt.ThreadOptInitializer
import com.mogo.eagle.core.function.main.ARouterUtils
import com.mogo.eagle.core.utilcode.util.AppUtils
import com.mogo.eagle.core.utilcode.util.CleanUtils
import java.lang.Exception
object ARouterStartUp {
@JvmStatic
fun init(app: Application) {
try {
@@ -18,7 +20,7 @@ object ARouterStartUp {
}
// 初始化 arouter
ARouter.init(app)
ThreadOptInitializer.aRouterInit.set(true)
ARouterUtils.isInit.set(true)
} catch (e: Exception) {
e.printStackTrace()
// 由于ARouter会在SP_AROUTER_CACHE.xml缓存路由表如果出现了被删除的情况会报错这里清除下就好了

View File

@@ -34,10 +34,11 @@ buildscript {
classpath "com.mogo.cloud:systrace:${plugin_version}"
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18'
classpath "com.mogo.sticky:service:${plugin_version}"
classpath "io.github.knight-zxw:lancet-plugin:10.50.0"
classpath "io.github.knight-zxw:lancet-plugin:10.60.0"
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.15.0"
classpath 'com.mogo.cloud:matrix:1.0.2'
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513'
classpath 'com.mogo.eagle.core.handler.proxy:plugin:1.0.0'
// classpath 'com.bytedance.btrace:rhea-gradle-plugin:2.0.0'
}
// 遇无法更新依赖情况针对Snapshot无法刷新然后sync project即可刷新完成注释该代码

View File

@@ -209,9 +209,9 @@ ext {
//========================= LancetX ===================
lancetx_runtime : "io.github.knight-zxw:lancet-runtime:10.50.0",
lancetx_compiler : "com.mogo.eagle.core.lancetx:compiler:1.0.0",
lancetx_compiler : "com.mogo.eagle.core.lancetx:compiler:1.0.5",
lancetx_compiler_lib : "com.mogo.eagle.core.lancetx:runtime:1.0.0",
lancetx_compiler_lib : "com.mogo.eagle.core.lancetx:runtime:1.0.5",
//========================= autosize ======================
androidautoSize : 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1',
@@ -228,10 +228,13 @@ ext {
passport_secret : "com.zhidaoauto:sdk-java:1.0.5-SNAPSHOT",
// 主线程卡顿监测
block_detector : "com.mogo.eagle.core.block:runtime:10.50.0",
block_detector : "com.mogo.eagle.core.block:runtime:10.60.0",
//======================== google auto-service ===============
google_auto_service : "com.google.auto.service:auto-service:1.0-rc7"
google_auto_service : "com.google.auto.service:auto-service:1.0-rc7",
//======================== handler-proxy-runtime ==============
handler_proxy_runtime : "com.mogo.eagle.core.handler.proxy:runtime:1.0.0"
]
android = [
fLauncherApplicationId : "com.mogo.launcher.f",

View File

@@ -52,13 +52,13 @@ internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener {
map["pending"] = data[1] ?: emptyList()
val cpu = CallerDevaToolsManager.usage()?.dump()
if (!cpu.isNullOrEmpty()) {
val mainThreadCpuUsage = cpu.remove("MainThreadCpuUsage")
val processLaunchedTime = cpu.remove("ProcessLaunchedTime")
if (mainThreadCpuUsage != null && processLaunchedTime != null) {
linkedLog.recordCpuUsage(LinkedHashMap<String, String>().also {
it["MainThread"] = "${ mainThreadCpuUsage * 1.0f * 100 / processLaunchedTime }%"
val mainThreadUsage = cpu.remove("MainThreadUsage")
val processUsage = cpu.remove("ProcessUsage")
if (mainThreadUsage != null && processUsage != null) {
linkedLog.recordCpuUsage(LinkedHashMap<String, String>().also { itx ->
itx["MainThread"] = "${ "%.2f".format(mainThreadUsage * 1.0f * 100 / processUsage) }% ($mainThreadUsage, $processUsage)"
cpu.entries.sortedByDescending { it.value }.forEach { sorted ->
it[sorted.key] = "${ sorted.value * 1.0f * 100 / processLaunchedTime }%"
itx[sorted.key] = "${ "%.2f".format(sorted.value * 1.0f * 100 / processUsage) }% (${sorted.value}, $processUsage)"
}
})
}

View File

@@ -1,5 +1,7 @@
package com.zhjt.mogo_core_function_devatools.perf
import android.os.Debug
import android.os.Looper
import android.os.SystemClock
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider
import java.util.concurrent.locks.ReentrantLock
@@ -11,53 +13,67 @@ internal class MoGoCpuUsageProviderImpl: IMoGoCpuUsageProvider {
private var mainThreadStartTime : Long = 0L
private var mainThreadLastTime: Long = 0L
private var mainThreadCpuUsage: Long = 0L
private val maxSize = 500
private val map by lazy { LinkedHashMap<String, Long>(0, 0.75f, true) }
private val map = LinkedHashMap<String, Long>(0, 0.75f, true)
private val lock by lazy { ReentrantLock() }
private val lock = ReentrantLock()
private val builder by lazy { ThreadLocal<StringBuilder>() }
private val builder = ThreadLocal<StringBuilder>()
override fun onProcessLaunched(time: Long) {
processLaunchTime = time
private val otherThreadLastTime = ThreadLocal<Long>()
private val mainLooper = Looper.getMainLooper()
override fun onProcessLaunched() {
processLaunchTime = SystemClock.elapsedRealtime()
}
override fun startMainThreadTime(time: Long) {
mainThreadStartTime = time
override fun onMainThreadLaunched() {
mainThreadStartTime = Debug.threadCpuTimeNanos() / 1000_000
}
override fun updateMainThreadTime(time: Long) {
if (mainThreadLastTime == 0L) {
mainThreadLastTime = mainThreadStartTime
override fun updateMainThreadTime() {
if (Looper.myLooper() != mainLooper) {
return
}
val delta = time - mainThreadLastTime
if (delta > 0) {
mainThreadCpuUsage += delta
if (mainThreadStartTime > 0) {
mainThreadCpuUsage = (Debug.threadCpuTimeNanos() / 1000_000 - mainThreadStartTime)
}
mainThreadLastTime = time
}
override fun incrementOtherThreadUsage(group: String, t: Thread, usage: Long) {
var builder = builder.get()
if (builder == null) {
builder = StringBuilder(128)
this.builder.set(builder)
}
if (builder.isNotEmpty()) {
builder.setLength(0)
}
builder.append(group).append("@@").append(t.name)
lock.withLock {
while (map.size > maxSize) {
val toEvict = map.entries.iterator().next()
map.remove(toEvict.key)
override fun updateOtherThreadTime() {
var last = otherThreadLastTime.get()
val now = Debug.threadCpuTimeNanos() / 1000_000
try {
if (last == null) {
last = now
otherThreadLastTime.set(last)
}
map[builder.toString()] = map.getOrPut(builder.toString()) { 0 } + usage
val duration = now - last
if (duration < 1) {
return
}
var builder = builder.get()
if (builder == null) {
builder = StringBuilder(128)
this.builder.set(builder)
}
if (builder.isNotEmpty()) {
builder.setLength(0)
}
builder.append(Thread.currentThread().name)
lock.withLock {
while (map.size > maxSize) {
val toEvict = map.entries.iterator().next()
map.remove(toEvict.key)
}
map[builder.toString()] = map.getOrPut(builder.toString()) { 0 } + duration
}
} finally {
otherThreadLastTime.set(now)
}
}
@@ -66,8 +82,8 @@ internal class MoGoCpuUsageProviderImpl: IMoGoCpuUsageProvider {
if (map.isNotEmpty()) {
val iterator = map.entries.iterator()
val rst = LinkedHashMap<String, Long>()
rst["MainThreadCpuUsage"] = mainThreadCpuUsage
rst["ProcessLaunchedTime"] = SystemClock.elapsedRealtimeNanos() - processLaunchTime
rst["MainThreadUsage"] = mainThreadCpuUsage
rst["ProcessUsage"] = SystemClock.elapsedRealtime() - processLaunchTime
while (iterator.hasNext()) {
val next = iterator.next()
rst[next.key] = next.value

View File

@@ -0,0 +1,12 @@
package com.mogo.eagle.core.function.main
import java.util.concurrent.atomic.AtomicBoolean
class ARouterUtils {
companion object {
@JvmField
var isInit = AtomicBoolean(false)
}
}

View File

@@ -1,26 +1,23 @@
package com.mogo.eagle.core.function.main.threadopt;
import android.os.Debug;
import android.os.Looper;
import android.os.SystemClock;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.os.HandlerCompat;
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider;
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
import com.mogo.eagle.core.function.main.ARouterUtils;
import com.mogo.thread.opt.core.ThreadManager;
import com.mogo.thread.opt.core.annotation.Keep;
import com.mogo.thread.opt.core.config.ThreadConfig;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@Keep
public class ThreadOptInitializer {
public static final AtomicBoolean aRouterInit = new AtomicBoolean(false);
private static final ThreadConfig.TaskExecuteListener listener = new ThreadConfig.TaskExecuteListener() {
@@ -38,29 +35,29 @@ public class ThreadOptInitializer {
@Override
public void onExecutorBefore(@NonNull ThreadConfig.TaskType taskType, @Nullable Object task, @Nullable String desc) {
if (!recorded && aRouterInit.get()) {
if (!recorded && ARouterUtils.isInit.get()) {
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
if (usage != null) {
recorded = true;
HandlerCompat.createAsync(Looper.getMainLooper()).post(() -> usage.startMainThreadTime(Debug.threadCpuTimeNanos()));
usage.onProcessLaunched(SystemClock.elapsedRealtimeNanos());
HandlerCompat.createAsync(Looper.getMainLooper()).post(usage::onMainThreadLaunched);
usage.onProcessLaunched();
}
}
if (recorded) {
start.set(Debug.threadCpuTimeNanos());
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
if (usage != null) {
usage.updateOtherThreadTime();
}
}
}
@Override
public void onExecutorAfter(@NonNull ThreadConfig.TaskType taskType, @Nullable Object task, @Nullable String desc) {
if (recorded) {
Long start = this.start.get();
if (start != null && start > 0) {
long delta = Debug.threadCpuTimeNanos() - start;
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
if (usage != null && desc != null) {
usage.incrementOtherThreadUsage(desc, Thread.currentThread(), delta);
}
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
if (usage != null) {
usage.updateOtherThreadTime();
}
}
}

View File

@@ -2,13 +2,13 @@ package com.mogo.eagle.core.function.api.devatools.perf
interface IMoGoCpuUsageProvider {
fun onProcessLaunched(time: Long)
fun onProcessLaunched()
fun startMainThreadTime(time: Long)
fun onMainThreadLaunched()
fun updateMainThreadTime(time: Long)
fun updateMainThreadTime()
fun incrementOtherThreadUsage(group: String, t: Thread, usage: Long)
fun updateOtherThreadTime()
fun dump(): LinkedHashMap<String, Long>
}

View File

@@ -17,5 +17,6 @@ if (!isAndroidTest) {
apply from: rootProject.file('gradle/bytex/bytex_apm.gradle')
apply from: rootProject.file('gradle/bytex/bytex_systrace.gradle')
apply from: rootProject.file('gradle/bytex/bytex_matrix.gradle')
apply from: rootProject.file('gradle/bytex/bytex_handler_proxy.gradle')
// apply from: rootProject.file('gradle/bytex/bytex_btrace.gradle')
}

View File

@@ -0,0 +1,6 @@
apply plugin: 'bytex.handler_proxy'
handler_proxy {
enable true
enableInDebug true
}

View File

@@ -2,8 +2,13 @@ apply plugin: 'LancetX'
LancetX {
enable true
enableInDebug true
blackList = [
"com.mogo.launcher.lancet.jank",
"com.mogo.thread.opt.core",
"com.zhjt.mogo_core_function_devatools.perf"
]
synchronizedLock {
enabled false
enabled true
blackList = [
"okio.AsyncTimeout\$Watchdog"
]
@@ -23,7 +28,7 @@ LancetX {
enable true
}
main_block_check {
enable false
enable true
}
}
}