diff --git a/app/matrixTrace/blackMethodList.txt b/app/matrixTrace/blackMethodList.txt index 16fbc0f824..880f2aa415 100644 --- a/app/matrixTrace/blackMethodList.txt +++ b/app/matrixTrace/blackMethodList.txt @@ -8,9 +8,10 @@ -keeppackage com/elegant/log/ -keeppackage org/slf4j/ -keeppackage kotlin/ +-keeppackage androidx/ -keepclass com.mogo.eagle.core.function.main.MainMoGoApplication -keepclass com.mogo.commons.AbsMogoApplication -keepclass com.bytedance.apm.agent.v2.instrumentation.AppAgent --keepclass androidx.core.app.CoreComponentFactory +# -keepclass androidx.core.app.CoreComponentFactory -keepclass com.mogo.eagle.core.utilcode.util.ProcessUtils -keepclass com.mogo.eagle.core.utilcode.mogo.AppLaunchTimeUtils \ No newline at end of file diff --git a/app/src/main/java/com/mogo/launcher/lancet/CrashFix.kt b/app/src/main/java/com/mogo/launcher/lancet/CrashFix.kt index 001c99c9b2..8a26b9311b 100644 --- a/app/src/main/java/com/mogo/launcher/lancet/CrashFix.kt +++ b/app/src/main/java/com/mogo/launcher/lancet/CrashFix.kt @@ -27,6 +27,8 @@ class CrashFix { @TargetMethod(methodName = "startService") @ReplaceInvoke fun fixStartServiceCrash(context: Context, intent: Intent): ComponentName? { + + return try { context.startService(intent) } catch (t: Throwable) { diff --git a/app/src/main/java/com/mogo/launcher/lancet/MainBlockCheck.kt b/app/src/main/java/com/mogo/launcher/lancet/MainBlockCheck.kt new file mode 100644 index 0000000000..31214f5da5 --- /dev/null +++ b/app/src/main/java/com/mogo/launcher/lancet/MainBlockCheck.kt @@ -0,0 +1,574 @@ +package com.mogo.launcher.lancet + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.graphics.Canvas +import android.os.* +import android.util.* +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.* +import androidx.collection.ArrayMap +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.* +import kotlinx.coroutines.Runnable +import java.util.LinkedList +import java.util.concurrent.ConcurrentHashMap +import com.mogo.eagle.core.block.runtime.message.Message as Holder + +@Keep +@Weaver +@Group("main_block_check") +class MainBlockCheck { + + companion object { + + @JvmStatic + val tokens = ArrayMap(128) + + @JvmStatic + val whats by lazy { ConcurrentHashMap>() } + + @JvmStatic + fun getRecorder(): IMessageRecorder? { + val block = CallerDevaToolsManager.block() + if (block == null || !block.hasInit()) { + return null + } + return block.recorder() + } + + @JvmStatic val HANDLER by lazy { + Handler(Looper.getMainLooper()) { msg -> + val enclose = msg.obj as? Holder ?: throw AssertionError() + enclose.what?.also { + whats[it]?.remove(enclose) + } + val h = enclose.handler + val action = enclose.action + val oldMsg = enclose.origin + val oldObj = enclose.obj + val now = now() + if (enclose.enqueueTime <= 0) { + throw AssertionError("illegal: $enclose") + } + val elapsedTime = now - enclose.enqueueTime + try { + if (action != null) { + action.run() + } else { + if (oldMsg == null) { + h?.dispatchMessage(msg) + } else { + oldMsg.obj = oldObj + h?.dispatchMessage(oldMsg) + } + } + } finally { + val duration = now() - now + getRecorder()?.recycle(enclose.id, elapsedTime, duration) + } + true + } + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "post") + @ReplaceInvoke + fun handlerPostProxy(handler: Handler, action: Runnable): Boolean { + if (Looper.getMainLooper() != handler.looper) { + return handler.post(action) + } + val what = ObjectHashCodeUtils.getHashCodeIfNeed(action) + val msg = Message.obtain(handler, action) + msg.what = what + val holder = Holder.acquire(handler, action = action, what = what) + msg.obj = holder + val inserted = HANDLER.sendMessage(msg) + if (inserted) { + getRecorder()?.insert(holder) + whats.getOrPut(what) { LinkedList() }.add(holder) + } + return inserted + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "sendEmptyMessage") + @ReplaceInvoke + fun handlerSendEmptyMessage(handler: Handler, what: Int): Boolean { + if (Looper.getMainLooper() != handler.looper) { + return handler.sendEmptyMessage(what) + } + val msg = Message.obtain(handler, what) + val holder = Holder.acquire(handler, what = what) + msg.obj = holder + val inserted = HANDLER.sendMessage(msg) + if (inserted) { + getRecorder()?.insert(holder) + whats.getOrPut(what) { LinkedList() }.add(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 (Looper.getMainLooper() != handler.looper) { + return handler.sendEmptyMessageAtTime(what, uptimeMillis) + } + val msg = Message.obtain(handler, what) + val holder = Holder.acquire(handler, what = what, delay = uptimeMillis - SystemClock.uptimeMillis()) + msg.obj = holder + val inserted = HANDLER.sendMessageAtTime(msg, uptimeMillis) + if (inserted) { + getRecorder()?.insert(holder) + whats.getOrPut(what) { LinkedList() }.add(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 (Looper.getMainLooper() != handler.looper) { + return handler.sendEmptyMessageDelayed(what, delayMillis) + } + val msg = Message.obtain(handler, what) + val holder = Holder.acquire(handler, what = what, delay = delayMillis) + msg.obj = holder + val inserted = HANDLER.sendMessageDelayed(msg, delayMillis) + if (inserted) { + getRecorder()?.insert(holder) + whats.getOrPut(what) { LinkedList() }.add(holder) + } + return inserted + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "sendMessage") + @ReplaceInvoke + fun handlerSendMessage(handler: Handler, msg: Message): Boolean { + if (Looper.getMainLooper() != handler.looper) { + return handler.sendMessage(msg) + } + val obj: Holder = Holder.acquire(handler, msg, obj = msg.obj, what = msg.what) + val oldObj = msg.obj + msg.obj = obj + if (oldObj != null) { + synchronized(tokens) { + tokens[oldObj] = obj + } + } + val inserted = HANDLER.sendMessage(msg) + if (inserted) { + getRecorder()?.insert(obj) + whats.getOrPut(msg.what) { LinkedList() }.add(obj) + } + return inserted + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "sendMessageAtTime") + @ReplaceInvoke + fun handlerSendMessageAtTime(handler: Handler, msg: Message, uptimeMillis: Long): Boolean { + if (Looper.getMainLooper() != handler.looper) { + return handler.sendMessageAtTime(msg, uptimeMillis) + } + val obj= Holder.acquire(handler, msg, obj = msg.obj, what = msg.what, delay = uptimeMillis - SystemClock.uptimeMillis()) + val oldObj = msg.obj + msg.obj = obj + if (oldObj != null) { + synchronized(tokens) { + tokens[oldObj] = obj + } + } + val inserted = HANDLER.sendMessageAtTime(msg, uptimeMillis) + if (inserted) { + whats.getOrPut(msg.what) { LinkedList() }.add(obj) + getRecorder()?.insert(obj) + } + return inserted + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "sendMessageAtFrontOfQueue") + @ReplaceInvoke + fun handlerSendMessageAtFrontOfQueue(handler: Handler, msg: Message): Boolean { + if (Looper.getMainLooper() != handler.looper) { + return handler.sendMessageAtFrontOfQueue(msg) + } + val obj = Holder.acquire(handler, msg, obj = msg.obj, what = msg.what) + val old = msg.obj + msg.obj = obj + if (old != null) { + synchronized(tokens) { + tokens[old] = obj + } + } + val inserted = HANDLER.sendMessageAtFrontOfQueue(msg) + if (inserted) { + whats.getOrPut(msg.what) { LinkedList() }.add(obj) + getRecorder()?.insert(obj) + } + return inserted + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "postDelayed") + @ReplaceInvoke + fun handlerPostDelayProxy(handler: Handler, action: Runnable, delayMillis: Long): Boolean { + if (Looper.getMainLooper() != handler.looper) { + return handler.postDelayed(action, delayMillis) + } + val what = ObjectHashCodeUtils.getHashCodeIfNeed(action) + val msg = Message.obtain(handler, action) + msg.what = what + val holder = Holder.acquire(handler, action = action, what = what, delay = delayMillis) + msg.obj = holder + val inserted = HANDLER.sendMessageDelayed(msg, delayMillis) + if (inserted) { + getRecorder()?.insert(holder) + whats.getOrPut(what) { LinkedList() }.add(holder) + } + return inserted + } + + @JvmStatic + @TargetClass(value = "android.app.Activity", scope = ALL) + @TargetMethod(methodName = "post") + @ReplaceInvoke + fun runOnUiThreadOfActivityProxy(activity: Activity, action: Runnable) { + val what = ObjectHashCodeUtils.getHashCodeIfNeed(action) + val msg = Message.obtain() + msg.what = what + val holder = Holder.acquire(null, action = action, what = what) + msg.obj = holder + val inserted = HANDLER.sendMessage(msg) + if (inserted) { + getRecorder()?.insert(holder) + whats.getOrPut(what) { LinkedList() }.add(holder) + } + } + + @JvmStatic + @TargetClass(value = "android.view.View", scope = ALL) + @TargetMethod(methodName = "post") + @ReplaceInvoke + fun postOfViewProxy(view: View, action: Runnable): Boolean { + val what = ObjectHashCodeUtils.getHashCodeIfNeed(action) + val msg = Message.obtain() + msg.what = what + val holder = Holder.acquire(null, action = action, what = what) + msg.obj = holder + val inserted = HANDLER.sendMessage(msg) + if (inserted) { + getRecorder()?.insert(holder) + whats.getOrPut(what) { LinkedList() }.add(holder) + } + return inserted + } + + @JvmStatic + @TargetClass(value = "android.view.View", scope = ALL) + @TargetMethod(methodName = "postDelayed") + @ReplaceInvoke + fun postDelayedOfViewProxy(view: View, action: Runnable, delayMillis: Long): Boolean { + val what = ObjectHashCodeUtils.getHashCodeIfNeed(action) + val msg = Message.obtain() + msg.what = what + val holder = Holder.acquire(null, action = action, what = what, delay = delayMillis) + msg.obj = holder + val inserted = HANDLER.sendMessageDelayed(msg, delayMillis) + if (inserted) { + getRecorder()?.insert(holder) + whats.getOrPut(what) { LinkedList() }.add(holder) + } + return inserted + } + + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "removeCallbacks") + @ReplaceInvoke + fun handlerRemoveCallbacks(handler: Handler, action: Runnable) { + if (Looper.getMainLooper() != handler.looper) { + return handler.removeCallbacks(action) + } + val what = ObjectHashCodeUtils.getHashCodeIfNeed(action) + HANDLER.removeMessages(what) + val holders = whats.remove(what) + if (!holders.isNullOrEmpty()) { + getRecorder()?.remove(holders) + } + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "removeCallbacks") + @ReplaceInvoke + fun handlerRemoveCallbacksWithToken(handler: Handler, action: Runnable, token: Any?) { + if (Looper.getMainLooper() != handler.looper) { + return handler.removeCallbacks(action, token) + } + val what = ObjectHashCodeUtils.getHashCodeIfNeed(action) + val holders = whats.remove(what) + if (!holders.isNullOrEmpty()) { + getRecorder()?.remove(holders) + } + HANDLER.removeMessages(what, if (token != null) { synchronized(tokens) { tokens.remove(token) } } else null) + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "removeMessages") + @ReplaceInvoke + fun handlerRemoveMessages(handler: Handler, what: Int) { + if (Looper.getMainLooper() != handler.looper) { + return handler.removeMessages(what) + } + val holders = whats.remove(what) + if (!holders.isNullOrEmpty()) { + getRecorder()?.remove(holders) + } + HANDLER.removeMessages(what) + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "removeMessages") + @ReplaceInvoke + fun handlerRemoveMessagesWithToken(handler: Handler, what: Int, token: Any?) { + if (Looper.getMainLooper() != handler.looper) { + return handler.removeMessages(what, token) + } + val holders = whats.remove(what) + if (!holders.isNullOrEmpty()) { + getRecorder()?.remove(holders) + } + HANDLER.removeMessages(what, if (token != null) synchronized(tokens) { tokens.remove(token) } else null) + } + } + + + @TargetClass(value = "android.view.View", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onMeasure") + fun proxyViewOnMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val now = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - now + getRecorder()?.insert(Holder.acquire(spend, "View[${This.get().javaClass.name}]:onMeasure")) + } + } + + @TargetClass(value = "android.view.View", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onLayout") + fun proxyViewOnLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + val holder = Holder.acquire(handler = null, obj = "View[${This.get().javaClass.name}]:onLayout") + getRecorder()?.insert(holder) + val now = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - now + getRecorder()?.recycle(holder.id, 0, spend) + } + } + + + @TargetClass(value = "android.view.View", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onDraw") + fun proxyViewOnDraw(canvas: Canvas) { + val holder = Holder.acquire(handler = null, obj = "View[${This.get().javaClass.name}]:onDraw") + getRecorder()?.insert(holder) + val now = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - now + getRecorder()?.recycle(holder.id, 0, spend) + } + } + + @TargetClass(value = "android.app.Activity", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onCreate") + fun proxyActivityOnCreate(savedInstanceState: Bundle?) { + val holder = Holder.acquire(handler = null, obj = "Activity[${This.get().javaClass.name}]:onCreate") + getRecorder()?.insert(holder) + val now = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - now + CallerDevaToolsManager.block()?.monitor((This.get() as Activity).window) + getRecorder()?.recycle(holder.id, 0, spend) + } + } + + @TargetClass(value = "android.app.Activity", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onStart") + fun proxyActivityOnStart() { + val holder = Holder.acquire(handler = null, obj = "Activity[${This.get().javaClass.name}]:onStart") + getRecorder()?.insert(holder) + val now = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - now + getRecorder()?.recycle(holder.id, 0, spend) + } + } + + @TargetClass(value = "android.app.Activity", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onResume") + fun proxyActivityOnResume() { + val holder = Holder.acquire(handler = null, obj = "Activity[${This.get().javaClass.name}]:onResume") + getRecorder()?.insert(holder) + val now = now() + CallerDevaToolsManager.block()?.resume((This.get() as Activity).window) + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - now + getRecorder()?.recycle(holder.id, 0, spend) + } + } + + @TargetClass(value = "android.app.Activity", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onPause") + fun proxyActivityOnPause() { + val holder = Holder.acquire(handler = null, obj = "Activity[${This.get().javaClass.name}]:onPause") + getRecorder()?.insert(holder) + CallerDevaToolsManager.block()?.pause((This.get() as Activity).window) + val now = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - now + getRecorder()?.recycle(holder.id, 0, spend) + } + } + + @TargetClass(value = "android.app.Activity", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onDestroy") + fun proxyActivityOnDestroy() { + CallerDevaToolsManager.block()?.pop((This.get() as Activity).window) + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } + } + + @TargetClass(value = "androidx.fragment.app.Fragment", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onCreateView") + fun proxyFragmentOnCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?) { + val holder = Holder.acquire(handler = null, obj = "Fragment[${This.get().javaClass.name}]:onCreateView") + getRecorder()?.insert(holder) + val startTime = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - startTime + getRecorder()?.recycle(holder.id, 0, spend) + } + } + + @TargetClass(value = "androidx.fragment.app.Fragment", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onCreate") + fun proxyFragmentOnCreate(savedInstanceState: Bundle?) { + val holder = Holder.acquire(handler = null, obj = "Fragment[${This.get().javaClass.name}]:onCreate") + getRecorder()?.insert(holder) + val startTime = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - startTime + getRecorder()?.recycle(holder.id, 0, spend) + } + } + + @TargetClass(value = "androidx.fragment.app.Fragment", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onViewCreated") + fun proxyFragmentOnViewCreated(view: View, savedInstanceState: Bundle?) { + val holder = Holder.acquire(handler = null, obj = "Fragment[${This.get().javaClass.name}]:onViewCreated") + getRecorder()?.insert(holder) + val startTime = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - startTime + getRecorder()?.recycle(holder.id, 0, spend) + } + } + + @TargetClass(value = "android.content.BroadcastReceiver", scope = LEAF) + @Insert(mayCreateSuper = true) + @TargetMethod(methodName = "onReceive") + fun proxyBroadcastReceiver(ctx: Context?, intent: Intent?) { + val holder = Holder.acquire(handler = null, obj = "Receiver[${This.get().javaClass.name}]:onReceive") + getRecorder()?.insert(holder) + val startTime = now() + try { + Origin.callVoid() + } catch (t: Throwable) { + throw t + } finally { + val spend = now() - startTime + getRecorder()?.recycle(holder.id, 0, spend) + } + } +} \ No newline at end of file diff --git a/config.gradle b/config.gradle index 79dfcf477f..95a31710b8 100644 --- a/config.gradle +++ b/config.gradle @@ -223,7 +223,10 @@ ext { log_runtime : "com.mogo.eagle.core.log.record:runtime:1.0.6", // 安全证书 - passport_secret : "com.zhidaoauto:sdk-java:1.0.5-SNAPSHOT" + passport_secret : "com.zhidaoauto:sdk-java:1.0.5-SNAPSHOT", + + // 主线程卡顿监测 + block_detector : "com.mogo.eagle.core.block:runtime:10.0.0" ] android = [ launcherApplicationId : "com.mogo.launcher", diff --git a/core/function-impl/mogo-core-function-devatools/build.gradle b/core/function-impl/mogo-core-function-devatools/build.gradle index c98d253059..30d39421ff 100644 --- a/core/function-impl/mogo-core-function-devatools/build.gradle +++ b/core/function-impl/mogo-core-function-devatools/build.gradle @@ -98,6 +98,7 @@ dependencies { implementation group: "com.tencent.matrix", name: "matrix-hooks", version: MATRIX_VERSION, changing: true implementation rootProject.ext.dependencies.weak_network // implementation rootProject.ext.dependencies.btrace + api rootProject.ext.dependencies.block_detector implementation project(':foudations:mogo-commons') implementation project(':core:mogo-core-utils') diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/DevaToolsProvider.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/DevaToolsProvider.kt index 46d186b687..5992f031ec 100644 --- a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/DevaToolsProvider.kt +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/DevaToolsProvider.kt @@ -18,6 +18,7 @@ import com.mogo.eagle.core.data.deva.scene.SceneTAG import com.mogo.eagle.core.data.msgbox.MsgBoxBean import com.mogo.eagle.core.function.api.devatools.IDevaToolsProvider import com.mogo.eagle.core.function.api.devatools.apm.* +import com.mogo.eagle.core.function.api.devatools.block.* import com.mogo.eagle.core.function.api.devatools.strict.* import com.mogo.eagle.core.function.api.devatools.download.* import com.mogo.eagle.core.function.api.devatools.logcat.* @@ -40,6 +41,7 @@ import com.mogo.weak.network.SdtManager import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager import com.zhjt.mogo_core_function_devatools.badcase.consts.BadCaseConfig import com.zhjt.mogo_core_function_devatools.binding.* +import com.zhjt.mogo_core_function_devatools.block.* import com.zhjt.mogo_core_function_devatools.env.EnvChangeManager import com.zhjt.mogo_core_function_devatools.funcconfig.FuncConfigCenter.Companion.bizConfigCenter import com.zhjt.mogo_core_function_devatools.funcconfig.FuncConfigImpl @@ -83,6 +85,8 @@ class DevaToolsProvider : IDevaToolsProvider { private val logRecordProvider by lazy { MoGoLogRecordProviderImpl() } + private val block by lazy { MoGoBlockProviderImpl() } + @Volatile private var mDockerVersion: String? = null @@ -122,6 +126,11 @@ class DevaToolsProvider : IDevaToolsProvider { logRecordProvider.init(it) logRecordProvider.start() } + + mContext?.also { + block.init(it) + block.start() + } } override fun checkMonitorDb() { @@ -377,4 +386,6 @@ class DevaToolsProvider : IDevaToolsProvider { override fun mofang(): IMoGoMoFangProvider = mofangProvider override fun logRecord(): IMoGoLogRecordProvider = logRecordProvider + + override fun block(): IMoGoBlockProvider = block } \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MainBlockLinkedLog.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MainBlockLinkedLog.kt new file mode 100644 index 0000000000..6cbb66cf99 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MainBlockLinkedLog.kt @@ -0,0 +1,23 @@ +package com.zhjt.mogo_core_function_devatools.block + +import com.mogo.eagle.core.data.deva.chain.* +import com.zhjt.service.chain.* + +internal class MainBlockLinkedLog { + + fun record(extra: Map>) { + try { + recordInternal(extra) + } catch (t: Throwable) { + t.printStackTrace() + } + } + + @ChainLog( + linkChainLog = ChainConstant.CHAIN_TYPE_HMI, + linkCode = ChainConstant.CHAIN_SOURCE_HMI, + nodeAliasCode = ChainConstant.CHAIN_CODE_MAIN_BLOCK, + paramIndexes = [0] + ) + private fun recordInternal(extra: Map>) {} +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MoGoBlockProviderImpl.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MoGoBlockProviderImpl.kt new file mode 100644 index 0000000000..53a35aace0 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MoGoBlockProviderImpl.kt @@ -0,0 +1,87 @@ +package com.zhjt.mogo_core_function_devatools.block + +import android.content.* +import android.util.* +import android.view.* +import androidx.metrics.performance.* +import com.mogo.eagle.core.block.runtime.* +import com.mogo.eagle.core.block.runtime.config.* +import com.mogo.eagle.core.block.runtime.config.recorder.* +import com.mogo.eagle.core.block.runtime.config.recorder.IMessageRecorder.OnDumpListener +import com.mogo.eagle.core.block.runtime.listener.* +import com.mogo.eagle.core.block.runtime.report.* +import com.mogo.eagle.core.function.api.devatools.block.* +import java.util.concurrent.TimeUnit.SECONDS + +internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener { + + @Volatile + private var hasInit = false + + private val linkedLog by lazy { MainBlockLinkedLog() } + + override fun init(ctx: Context) { + BlockDetector.init(BlockMetrics.Builder() + .context(ctx) + .multiplier(2.0f) + .isDebug(false) + .period(5, SECONDS) + .junkRateThreshold(0.8f) + .recorder(null, 1000, 512) + .build()) + hasInit = true + } + + override fun hasInit(): Boolean { + return hasInit + } + + override fun start() { + BlockDetector.start() + BlockDetector.addListener(this) + } + + override fun onBlockReport(info: ReportInfo) { + Log.d("BLOCK", "--- onBlockReport ---: ${info.frames.size}") + val map = mutableMapOf>() + map["frames"] = info.frames.map { "$it" } + BlockDetector.recorder().dump(object: OnDumpListener { + override fun OnDumped(data: Map>) { + map["history"] = data[0] ?: emptyList() + map["pending"] = data[1] ?: emptyList() + linkedLog.record(map) + } + }) + } + + override fun monitor(window: Window) { + BlockDetector.monitor(window) + } + + override fun pause(window: Window) { + BlockDetector.pause(window) + } + + override fun addState(window: Window, key: String, status: String) { + val holder = PerformanceMetricsState.getHolderForHierarchy(window.decorView) + holder.state?.putState(key, status) + } + + override fun resume(window: Window) { + BlockDetector.resume(window) + } + + override fun pop(window: Window) { + BlockDetector.pop(window) + } + + override fun stop() { + BlockDetector.removeListener(this) + BlockDetector.stop() + } + + override fun recorder(): IMessageRecorder { + + return BlockDetector.recorder() + } +} \ No newline at end of file diff --git a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/deva/chain/ChainConstant.kt b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/deva/chain/ChainConstant.kt index 8e2a6689c0..cb9c2c9c4d 100644 --- a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/deva/chain/ChainConstant.kt +++ b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/deva/chain/ChainConstant.kt @@ -81,6 +81,7 @@ class ChainConstant { const val CHAIN_CODE_INIT_ON_MAP_INIT = "CHAIN_CODE_INIT_ON_MAP_INIT" const val CHAIN_CODE_UPGRADE_APP = "CHAIN_CODE_UPGRADE_APP" const val CHAIN_CODE_MO_FANG_CONNECT = "CHAIN_CODE_MO_FANG_CONNECT" + const val CHAIN_CODE_MAIN_BLOCK = "CHAIN_CODE_MAIN_BLOCK" const val CHAIN_CODE_CLOUD_INIT = "CHAIN_CODE_CLOUD_INIT" const val CHAIN_CODE_CLOUD_PASSPORT_TOKEN = "CHAIN_CODE_CLOUD_PASSPORT_TOKEN" diff --git a/core/mogo-core-function-api/build.gradle b/core/mogo-core-function-api/build.gradle index f953297e09..3cba230563 100644 --- a/core/mogo-core-function-api/build.gradle +++ b/core/mogo-core-function-api/build.gradle @@ -51,6 +51,8 @@ dependencies { implementation rootProject.ext.dependencies.arouter kapt rootProject.ext.dependencies.aroutercompiler + compileOnly rootProject.ext.dependencies.block_detector + if (Boolean.valueOf(USE_MAVEN_PACKAGE)) { compileOnly rootProject.ext.dependencies.mogo_core_data compileOnly rootProject.ext.dependencies.mogomapapi diff --git a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt index 3105a75a5e..a03bc63dd3 100644 --- a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt +++ b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt @@ -12,6 +12,7 @@ import com.mogo.eagle.core.data.deva.scene.SceneModule import com.mogo.eagle.core.data.deva.scene.SceneTAG import com.mogo.eagle.core.data.msgbox.MsgBoxBean import com.mogo.eagle.core.function.api.devatools.apm.* +import com.mogo.eagle.core.function.api.devatools.block.* import com.mogo.eagle.core.function.api.devatools.strict.* import com.mogo.eagle.core.function.api.devatools.download.* import com.mogo.eagle.core.function.api.devatools.logcat.* @@ -225,4 +226,9 @@ interface IDevaToolsProvider : IProvider { * 日志记录功能 */ fun logRecord(): IMoGoLogRecordProvider + + /** + * 主线程卡顿监控 + */ + fun block(): IMoGoBlockProvider } \ No newline at end of file diff --git a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/block/IMoGoBlockProvider.kt b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/block/IMoGoBlockProvider.kt new file mode 100644 index 0000000000..ebd2cfe599 --- /dev/null +++ b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/block/IMoGoBlockProvider.kt @@ -0,0 +1,28 @@ +package com.mogo.eagle.core.function.api.devatools.block + +import android.content.Context +import android.view.Window +import com.mogo.eagle.core.block.runtime.config.recorder.* + +interface IMoGoBlockProvider { + + fun init(ctx: Context) + + fun hasInit(): Boolean + + fun start() + + fun monitor(window: Window) + + fun pause(window: Window) + + fun resume(window: Window) + + fun pop(window: Window) + + fun addState(window: Window, key: String, status: String) + + fun stop() + + fun recorder(): IMessageRecorder +} \ No newline at end of file diff --git a/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt b/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt index 337a316d60..22386d7a6b 100644 --- a/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt +++ b/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt @@ -14,6 +14,7 @@ import com.mogo.eagle.core.data.deva.scene.SceneTAG import com.mogo.eagle.core.data.msgbox.MsgBoxBean import com.mogo.eagle.core.function.api.devatools.IDevaToolsProvider import com.mogo.eagle.core.function.api.devatools.apm.* +import com.mogo.eagle.core.function.api.devatools.block.* import com.mogo.eagle.core.function.api.devatools.download.* import com.mogo.eagle.core.function.api.devatools.logcat.* import com.mogo.eagle.core.function.api.devatools.mofang.* @@ -264,4 +265,6 @@ object CallerDevaToolsManager { fun mofang(): IMoGoMoFangProvider? = devaToolsProviderApi?.mofang() fun logcat(): IMoGoLogRecordProvider? = devaToolsProviderApi?.logRecord() + + fun block(): IMoGoBlockProvider? = devaToolsProviderApi?.block() } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 893bf27e7d..d81a505d36 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ org.gradle.daemon=true org.gradle.configureondemand=true org.gradle.parallel=true -org.gradle.jvmargs=-Xmx4096m +org.gradle.jvmargs=-Xmx6144m #开启gradle缓存 org.gradle.caching=true android.enableBuildCache=true diff --git a/gradle/bytex/bytex_lancetx.gradle b/gradle/bytex/bytex_lancetx.gradle index 72f1abbbd5..9beadd492d 100644 --- a/gradle/bytex/bytex_lancetx.gradle +++ b/gradle/bytex/bytex_lancetx.gradle @@ -18,6 +18,9 @@ LancetX { window_callback { enable true } + main_block_check { + enable true + } } }