[6.2.0][技术优化] 打开监测开关
This commit is contained in:
@@ -4,6 +4,8 @@ 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.*
|
||||
@@ -21,7 +23,6 @@ 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.LinkedList
|
||||
import java.util.concurrent.*
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.withLock
|
||||
@@ -36,65 +37,61 @@ class MainBlockCheck {
|
||||
|
||||
private const val TAG = "MAIN_BLOCK_CHECK"
|
||||
|
||||
@JvmStatic
|
||||
private val tokens = ConcurrentHashMap<Int, WeakReference<Holder>>(128)
|
||||
private val handlerTokens by lazy { ConcurrentHashMap<String, CopyOnWriteArrayList<Holder>>() }
|
||||
|
||||
private val pool by lazy { Executors.newFixedThreadPool(2) }
|
||||
private val pool by lazy { Pools.SimplePool<ActionWrapper>(50) }
|
||||
|
||||
val asyncHandlers by lazy { CopyOnWriteArraySet<WeakReference<Handler>>() }
|
||||
|
||||
private val handlersMap by lazy { ConcurrentHashMap<String, Boolean>() }
|
||||
|
||||
private val whats by lazy { ConcurrentHashMap<Int, LinkedList<WeakReference<Holder>>>() }
|
||||
|
||||
private val actionPool by lazy { Pools.SimplePool<PoolAction>(500) }
|
||||
private val actions by lazy { ConcurrentHashMap<Runnable, ActionWrapper>() }
|
||||
|
||||
private val lock by lazy { ReentrantReadWriteLock() }
|
||||
|
||||
private data class PoolAction(var type: Int, var what: Int, var holder: Holder?, var elapsedTime: Long = -1, var duration:Long = -1): Runnable {
|
||||
val asyncHandlers by lazy { CopyOnWriteArraySet<WeakReference<Handler>>() }
|
||||
|
||||
override fun run() {
|
||||
try {
|
||||
val h = holder
|
||||
if (type == 0 && h != null) {
|
||||
whats.getOrPut(what) { LinkedList<WeakReference<Holder>>() }.add(
|
||||
WeakReference(h)
|
||||
)
|
||||
getRecorder()?.insert(h)
|
||||
}
|
||||
if (type == 1) {
|
||||
val holders = whats.remove(what)?.mapNotNull { it.get() }
|
||||
if (!holders.isNullOrEmpty()) {
|
||||
getRecorder()?.remove(holders)
|
||||
}
|
||||
}
|
||||
if (type == 2 && h != null) {
|
||||
val what = h.what
|
||||
if (what != null) {
|
||||
whats.remove(what)?.removeAll { it.get() == h }
|
||||
}
|
||||
getRecorder()?.recycle(h.id, elapsedTime, duration)
|
||||
}
|
||||
} finally {
|
||||
reset()
|
||||
lock.writeLock().withLock {
|
||||
actionPool.release(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
private val handlers by lazy { ConcurrentHashMap<Int, Boolean>() }
|
||||
|
||||
fun reset() {
|
||||
type = -1
|
||||
what = Int.MIN_VALUE
|
||||
holder = null
|
||||
elapsedTime = -1
|
||||
duration = -1
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAction(): PoolAction = lock.readLock().withLock { actionPool.acquire() ?: PoolAction(-1, 0, null) }
|
||||
|
||||
@JvmStatic
|
||||
fun getRecorder(): IMessageRecorder? {
|
||||
@@ -121,8 +118,7 @@ class MainBlockCheck {
|
||||
val action = enclose.action
|
||||
val oldMsg = enclose.origin
|
||||
val oldObj = enclose.obj
|
||||
val now = now()
|
||||
val elapsedTime = now - enclose.enqueueTime
|
||||
val start = now()
|
||||
try {
|
||||
if (action != null) {
|
||||
action.run()
|
||||
@@ -133,44 +129,48 @@ class MainBlockCheck {
|
||||
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, "handler: $h -> ${handlersMap[h.toString()]} -> msg: $enclose", t)
|
||||
Log.e(TAG, "msg: $enclose", t)
|
||||
throw t
|
||||
} finally {
|
||||
val duration = now() - now
|
||||
recycle(enclose, elapsedTime, duration)
|
||||
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(Looper.getMainLooper(), callback)
|
||||
HandlerCompat.createAsync(main, callback)
|
||||
}
|
||||
|
||||
@JvmStatic val HANDLER by lazy {
|
||||
Handler(Looper.getMainLooper(), callback)
|
||||
Handler(main, callback)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
||||
@TargetMethod(methodName = "post")
|
||||
@ReplaceInvoke
|
||||
fun handlerPostProxy(handler: Handler, action: Runnable): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
fun handlerPost(handler: Handler, action: Runnable): Boolean {
|
||||
if (main != handler.looper) {
|
||||
return handler.post(action)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
val newHandler = getHandler(isAsync)
|
||||
val msg = Message.obtain(newHandler)
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(newHandler, action)
|
||||
msg.what = what
|
||||
val holder = Holder.acquire(newHandler, action = action, isAsync = isAsync)
|
||||
msg.obj = holder
|
||||
val inserted = newHandler.sendMessage(msg)
|
||||
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) {
|
||||
insert(what, holder)
|
||||
getRecorder()?.insert(holder)
|
||||
} else {
|
||||
actions.remove(action)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -179,47 +179,34 @@ class MainBlockCheck {
|
||||
@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) {
|
||||
fun handlerPostDelay(handler: Handler, action: Runnable, delayMillis: Long): Boolean {
|
||||
if (main != handler.looper) {
|
||||
return handler.postDelayed(action, delayMillis)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
val newHandler = getHandler(isAsync)
|
||||
val msg = Message.obtain(newHandler)
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
msg.what = what
|
||||
val holder = Holder.acquire(handler, action = action, delay = delayMillis, isAsync = isAsync)
|
||||
msg.obj = holder
|
||||
val inserted = newHandler.sendMessageDelayed(msg, 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) {
|
||||
insert(what, holder)
|
||||
getRecorder()?.insert(holder)
|
||||
} else {
|
||||
actions.remove(action)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
||||
@TargetMethod(methodName = "postAtTime")
|
||||
@TargetMethod(methodName = "postDelayed")
|
||||
@ReplaceInvoke
|
||||
fun handlerPostDelayProxy2(handler: Handler, action: Runnable, token: Any?, delayMillis: Long): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
fun handlerPostDelayWithToken(handler: Handler, action: Runnable, token: Any?, delayMillis: Long): Boolean {
|
||||
if (main != handler.looper) {
|
||||
return HandlerCompat.postDelayed(handler, action, token, delayMillis)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
val newHandler = getHandler(isAsync)
|
||||
val msg = Message.obtain(newHandler)
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
msg.what = what
|
||||
val holder = Holder.acquire(handler, action = action, delay = delayMillis, obj = token)
|
||||
msg.obj = holder
|
||||
if (token != null) {
|
||||
synchronized(tokens) {
|
||||
tokens[token.hashCode()] = WeakReference(holder)
|
||||
}
|
||||
}
|
||||
val inserted = HandlerCompat.postDelayed(newHandler, 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) {
|
||||
insert(what, holder)
|
||||
getRecorder()?.insert(holder)
|
||||
} else {
|
||||
actions.remove(action)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -228,20 +215,16 @@ class MainBlockCheck {
|
||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
||||
@TargetMethod(methodName = "postAtFrontOfQueue")
|
||||
@ReplaceInvoke
|
||||
fun handlerPostAtFrontOfQueueProxy(handler: Handler, action: Runnable): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
fun handlerPostAtFrontOfQueue(handler: Handler, action: Runnable): Boolean {
|
||||
if (main != handler.looper) {
|
||||
return handler.postAtFrontOfQueue(action)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
val newHandler = getHandler(isAsync)
|
||||
val msg = Message.obtain(newHandler)
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
msg.what = what
|
||||
val holder = Holder.acquire(handler, action = action)
|
||||
msg.obj = holder
|
||||
val inserted = newHandler.sendMessageAtFrontOfQueue(msg)
|
||||
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) {
|
||||
insert(what, holder)
|
||||
getRecorder()?.insert(holder)
|
||||
} else {
|
||||
actions.remove(action)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -250,21 +233,17 @@ class MainBlockCheck {
|
||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
||||
@TargetMethod(methodName = "postAtTime")
|
||||
@ReplaceInvoke
|
||||
fun handlerPostAtTimeProxy(handler: Handler, action: Runnable, uptimeMillis: Long): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
fun handlerPostAtTime(handler: Handler, action: Runnable, uptimeMillis: Long): Boolean {
|
||||
if (main != handler.looper) {
|
||||
return handler.postAtTime(action, uptimeMillis)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
val newHandler = getHandler(isAsync)
|
||||
val msg = Message.obtain(newHandler)
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
msg.what = what
|
||||
val delta = uptimeMillis - SystemClock.uptimeMillis()
|
||||
val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0)
|
||||
msg.obj = holder
|
||||
val inserted = newHandler.sendMessageAtTime(msg, 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) {
|
||||
insert(what, holder)
|
||||
getRecorder()?.insert(holder)
|
||||
} else {
|
||||
actions.remove(action)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -273,26 +252,17 @@ class MainBlockCheck {
|
||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
||||
@TargetMethod(methodName = "postAtTime")
|
||||
@ReplaceInvoke
|
||||
fun handlerPostAtTimeProxy2(handler: Handler, action: Runnable, token: Any?, uptimeMillis: Long): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
fun handlerPostAtTimeWithToken(handler: Handler, action: Runnable, token: Any?, uptimeMillis: Long): Boolean {
|
||||
if (main != handler.looper) {
|
||||
return handler.postAtTime(action, uptimeMillis)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString() ) { computeIsAsyncHandler(handler) }
|
||||
val newHandler = getHandler(isAsync)
|
||||
val msg = Message.obtain(newHandler)
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
msg.what = what
|
||||
val delta = uptimeMillis - SystemClock.uptimeMillis()
|
||||
val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0, obj = token)
|
||||
msg.obj = holder
|
||||
if (token != null) {
|
||||
synchronized(tokens) {
|
||||
tokens[token.hashCode()] = WeakReference(holder)
|
||||
}
|
||||
}
|
||||
val inserted = newHandler.sendMessageAtTime(msg, 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) {
|
||||
insert(what, holder)
|
||||
getRecorder()?.insert(holder)
|
||||
} else {
|
||||
actions.remove(action)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -302,17 +272,23 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "sendEmptyMessage")
|
||||
@ReplaceInvoke
|
||||
fun handlerSendEmptyMessage(handler: Handler, what: Int): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.sendEmptyMessage(what)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
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) {
|
||||
insert(what, holder)
|
||||
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what)
|
||||
getRecorder()?.insert(holder)
|
||||
} else {
|
||||
handlerTokens[tokenKey]?.remove(holder)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -322,18 +298,24 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "sendEmptyMessageAtTime")
|
||||
@ReplaceInvoke
|
||||
fun handlerSendEmptyMessageAtTime(handler: Handler, what: Int, uptimeMillis: Long): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.sendEmptyMessageAtTime(what, uptimeMillis)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
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) {
|
||||
insert(what, holder)
|
||||
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what)
|
||||
getRecorder()?.insert(holder)
|
||||
} else {
|
||||
handlerTokens[tokenKey]?.remove(holder)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -343,17 +325,23 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "sendEmptyMessageDelayed")
|
||||
@ReplaceInvoke
|
||||
fun handlerSendEmptyMessageDelayed(handler: Handler, what: Int, delayMillis: Long): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.sendEmptyMessageDelayed(what, delayMillis)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
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) {
|
||||
insert(what, holder)
|
||||
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what)
|
||||
getRecorder()?.insert(holder)
|
||||
} else {
|
||||
handlerTokens[tokenKey]?.remove(holder)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -363,28 +351,34 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "sendMessage")
|
||||
@ReplaceInvoke
|
||||
fun handlerSendMessage(handler: Handler, msg: Message): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.sendMessage(msg)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
val newHandler = getHandler(isAsync)
|
||||
var newMsg = 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) {
|
||||
newMsg = Message.obtain(newHandler)
|
||||
newMsg.what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
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)
|
||||
}
|
||||
val obj: Holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync)
|
||||
val oldObj = msg.obj
|
||||
newMsg.obj = obj
|
||||
if (oldObj != null) {
|
||||
synchronized(tokens) {
|
||||
tokens[oldObj.hashCode()] = WeakReference(obj)
|
||||
}
|
||||
}
|
||||
val inserted = newHandler.sendMessage(newMsg)
|
||||
if (inserted) {
|
||||
insert(newMsg.what, obj)
|
||||
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
|
||||
}
|
||||
@@ -394,29 +388,35 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "sendMessageAtTime")
|
||||
@ReplaceInvoke
|
||||
fun handlerSendMessageAtTime(handler: Handler, msg: Message, uptimeMillis: Long): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.sendMessageAtTime(msg, uptimeMillis)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
val newHandler = getHandler(isAsync)
|
||||
var newMsg = msg
|
||||
val tokenKey = generateKey(handler, msg.what)
|
||||
val key = handler.hashCode()
|
||||
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
|
||||
val action = msg.callback
|
||||
if (action != null) {
|
||||
newMsg = Message.obtain(newHandler)
|
||||
newMsg.what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
}
|
||||
val delta = uptimeMillis - SystemClock.uptimeMillis()
|
||||
val obj= Holder.acquire(handler, msg, obj = msg.obj, action = msg.callback, what = msg.what, delay = if (delta > 0) delta else 0, isAsync = isAsync)
|
||||
val oldObj = msg.obj
|
||||
newMsg.obj = obj
|
||||
if (oldObj != null) {
|
||||
synchronized(tokens) {
|
||||
tokens[oldObj.hashCode()] = WeakReference(obj)
|
||||
}
|
||||
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)
|
||||
}
|
||||
val inserted = newHandler.sendMessageAtTime(newMsg, uptimeMillis)
|
||||
if (inserted) {
|
||||
insert(newMsg.what, obj)
|
||||
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
|
||||
}
|
||||
@@ -426,46 +426,81 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "sendMessageAtFrontOfQueue")
|
||||
@ReplaceInvoke
|
||||
fun handlerSendMessageAtFrontOfQueue(handler: Handler, msg: Message): Boolean {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.sendMessageAtFrontOfQueue(msg)
|
||||
}
|
||||
val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }
|
||||
val newHandler = getHandler(isAsync)
|
||||
var newMsg = 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) {
|
||||
newMsg = Message.obtain(newHandler)
|
||||
newMsg.what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
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)
|
||||
}
|
||||
val obj = Holder.acquire(handler, msg, obj = msg.obj, action = msg.callback, what = msg.what, isAsync = isAsync)
|
||||
val old = msg.obj
|
||||
newMsg.obj = obj
|
||||
if (old != null) {
|
||||
synchronized(tokens) {
|
||||
tokens[old.hashCode()] = WeakReference(obj)
|
||||
}
|
||||
}
|
||||
val inserted = newHandler.sendMessageAtFrontOfQueue(newMsg)
|
||||
if (inserted) {
|
||||
insert(newMsg.what, obj)
|
||||
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 runOnUiThreadOfActivityProxy(activity: Activity, action: Runnable) {
|
||||
val msg = Message.obtain()
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(activity, action)
|
||||
msg.what = what
|
||||
val holder = Holder.acquire(null, action = action, what = msg.what, extra = mutableMapOf("runOnUiThread" to "${activity.javaClass.simpleName}#${action.javaClass.name}"))
|
||||
msg.obj = holder
|
||||
val inserted = HANDLER.sendMessage(msg)
|
||||
if (inserted) {
|
||||
insert(what, holder)
|
||||
fun runOnUiThreadOfActivity(activity: Activity, action: Runnable) {
|
||||
val holder = Holder.acquire(null, action = action)
|
||||
try {
|
||||
activity.runOnUiThread(acquireAction(holder, action))
|
||||
} finally {
|
||||
getRecorder()?.insert(holder)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,15 +508,11 @@ class MainBlockCheck {
|
||||
@TargetClass(value = "android.view.View", scope = ALL)
|
||||
@TargetMethod(methodName = "post")
|
||||
@ReplaceInvoke
|
||||
fun postOfViewProxy(view: View, action: Runnable): Boolean {
|
||||
val msg = Message.obtain()
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(view, action)
|
||||
msg.what = what
|
||||
val holder = Holder.acquire(null, action = action, what = what, extra = mutableMapOf("post" to "${view.javaClass.simpleName}#${action.javaClass.name}"))
|
||||
msg.obj = holder
|
||||
val inserted = HANDLER.sendMessage(msg)
|
||||
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) {
|
||||
insert(what, holder)
|
||||
getRecorder()?.insert(holder)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -490,15 +521,11 @@ class MainBlockCheck {
|
||||
@TargetClass(value = "android.view.View", scope = ALL)
|
||||
@TargetMethod(methodName = "postDelayed")
|
||||
@ReplaceInvoke
|
||||
fun postDelayedOfViewProxy(view: View, action: Runnable, delayMillis: Long): Boolean {
|
||||
val msg = Message.obtain()
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(view, action)
|
||||
msg.what = what
|
||||
val holder = Holder.acquire(null, action = action, delay = delayMillis, what = what, extra = mutableMapOf("post" to "${view.javaClass.simpleName}#${action.javaClass.name}"))
|
||||
msg.obj = holder
|
||||
val inserted = HANDLER.sendMessageDelayed(msg, delayMillis)
|
||||
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) {
|
||||
insert(what, holder)
|
||||
getRecorder()?.insert(holder)
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
@@ -508,14 +535,16 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "removeCallbacks")
|
||||
@ReplaceInvoke
|
||||
fun handlerRemoveCallbacks(handler: Handler, action: Runnable) {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.removeCallbacks(action)
|
||||
}
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
try {
|
||||
return getHandler(handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }).removeMessages(what)
|
||||
} finally {
|
||||
remove(what)
|
||||
val existed = actions.remove(action)
|
||||
if (existed != null) {
|
||||
try {
|
||||
handler.removeCallbacks(existed)
|
||||
} finally {
|
||||
getRecorder()?.remove(existed.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,14 +553,17 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "removeCallbacks")
|
||||
@ReplaceInvoke
|
||||
fun handlerRemoveCallbacksWithToken(handler: Handler, action: Runnable, token: Any?) {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.removeCallbacks(action, token)
|
||||
}
|
||||
val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action)
|
||||
getHandler(handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }).removeMessages(what, if (token != null) { synchronized(
|
||||
tokens
|
||||
) { tokens.remove(token.hashCode())?.get() } } else null)
|
||||
remove(what)
|
||||
val existed = actions.remove(action)
|
||||
if (existed != null) {
|
||||
try {
|
||||
handler.removeCallbacks(existed, token)
|
||||
} finally {
|
||||
getRecorder()?.remove(existed.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -539,22 +571,55 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "removeMessages")
|
||||
@ReplaceInvoke
|
||||
fun handlerRemoveMessages(handler: Handler, what: Int) {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.removeMessages(what)
|
||||
}
|
||||
getHandler(handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }).removeMessages(what)
|
||||
remove(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 what = ObjectHashCodeUtils.getHashCodeIfNeed(view, action)
|
||||
HANDLER.removeMessages(what)
|
||||
remove(what)
|
||||
return true
|
||||
val exist = actions.remove(action)
|
||||
if (exist != null) {
|
||||
try {
|
||||
return view.removeCallbacks(exist)
|
||||
} finally {
|
||||
getRecorder()?.remove(exist.msg)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -562,43 +627,28 @@ class MainBlockCheck {
|
||||
@TargetMethod(methodName = "removeMessages")
|
||||
@ReplaceInvoke
|
||||
fun handlerRemoveMessagesWithToken(handler: Handler, what: Int, token: Any?) {
|
||||
if (Looper.getMainLooper() != handler.looper) {
|
||||
if (main != handler.looper) {
|
||||
return handler.removeMessages(what, token)
|
||||
}
|
||||
getHandler(handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }).removeMessages(what, if (token != null) synchronized(
|
||||
tokens
|
||||
) { tokens.remove(token.hashCode())?.get() } else null)
|
||||
remove(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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fun insert(what: Int, holder: Holder) {
|
||||
val action = getAction()
|
||||
action.type = 0
|
||||
action.what = what
|
||||
action.holder = holder
|
||||
pool.execute(action)
|
||||
}
|
||||
|
||||
private fun remove(what: Int) {
|
||||
val action = getAction()
|
||||
action.type = 1
|
||||
action.what = what
|
||||
pool.execute(action)
|
||||
}
|
||||
|
||||
private fun recycle(holder: Holder, elapsedTime: Long, duration: Long) {
|
||||
val action = getAction()
|
||||
action.type = 2
|
||||
action.holder = holder
|
||||
action.elapsedTime = elapsedTime
|
||||
action.duration = duration
|
||||
pool.execute(action)
|
||||
}
|
||||
|
||||
private fun getHandler(isAsync: Boolean): Handler {
|
||||
return if (isAsync) {
|
||||
ASYNC_HANDLER
|
||||
|
||||
@@ -10,6 +10,8 @@ import android.content.ContentProvider;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.SystemClock;
|
||||
import android.view.View;
|
||||
import android.view.ViewManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.knightboost.lancet.api.synchronized_lock.ISynchronizedLockHooker;
|
||||
import com.mogo.core.lancetx.compiler.lib.annotations.LancetXGenerator;
|
||||
@@ -17,6 +19,8 @@ import com.mogo.core.lancetx.compiler.lib.generator.HookType;
|
||||
import com.mogo.core.lancetx.compiler.lib.generator.Scope;
|
||||
import com.mogo.core.lancetx.compiler.lib.generator.Type;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
@@ -178,4 +182,56 @@ class JankPointAutoGenerator {
|
||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
||||
)
|
||||
private androidx.fragment.app.Fragment androidxFragment;
|
||||
|
||||
|
||||
@LancetXGenerator(
|
||||
group = "main_block_check",
|
||||
type = Type.IO,
|
||||
scope = Scope.ALL,
|
||||
hookType = HookType.REPLACE_INVOKE,
|
||||
hookAllPublicMethods = true,
|
||||
excludeHookMethodNames = { "toString", "hashCode", "compareTo", "compareTo" },
|
||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" , "(Ljava/io/File;)I", "(Ljava/lang/Object;)I" }
|
||||
)
|
||||
private File file;
|
||||
|
||||
|
||||
@LancetXGenerator(
|
||||
group = "main_block_check",
|
||||
type = Type.IO,
|
||||
scope = Scope.ALL,
|
||||
hookType = HookType.REPLACE_INVOKE,
|
||||
hookAllPublicMethods = true,
|
||||
excludeHookMethodNames = { "toString", "hashCode" },
|
||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
||||
)
|
||||
private InputStream is;
|
||||
|
||||
|
||||
@LancetXGenerator(
|
||||
group = "main_block_check",
|
||||
type = Type.VIEW,
|
||||
scope = Scope.ALL,
|
||||
hookType = HookType.REPLACE_INVOKE,
|
||||
hookAllPublicMethods = true,
|
||||
onlyHookMethodNames = { "show" },
|
||||
onlyHookMethodDescs = { "()V" }
|
||||
)
|
||||
private Toast toast;
|
||||
|
||||
|
||||
@LancetXGenerator(
|
||||
group = "main_block_check",
|
||||
type = Type.VIEW,
|
||||
scope = Scope.ALL,
|
||||
hookType = HookType.REPLACE_INVOKE,
|
||||
hookAllPublicMethods = true,
|
||||
onlyHookMethodNames = { "addView", "updateViewLayout", "removeView" },
|
||||
onlyHookMethodDescs = {
|
||||
"(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V",
|
||||
"(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V",
|
||||
"(Landroid/view/View;)V"
|
||||
}
|
||||
)
|
||||
private ViewManager manager;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ 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.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@@ -25,12 +25,25 @@ public class HookHandler implements IHookInvoker {
|
||||
|
||||
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();
|
||||
@@ -64,25 +77,49 @@ public class HookHandler implements IHookInvoker {
|
||||
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) {
|
||||
if ("dalvik.system.VMStack".equals(trace.getClassName()) && "getThreadStackTrace".equals(trace.getMethodName())) {
|
||||
continue;
|
||||
}
|
||||
if ("java.lang.Thread".equals(trace.getClassName()) && "getStackTrace".equals(trace.getMethodName())) {
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,25 +159,38 @@ public class HookHandler implements IHookInvoker {
|
||||
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) {
|
||||
if ("dalvik.system.VMStack".equals(trace.getClassName()) && "getThreadStackTrace".equals(trace.getMethodName())) {
|
||||
continue;
|
||||
}
|
||||
if ("java.lang.Thread".equals(trace.getClassName()) && "getStackTrace".equals(trace.getMethodName())) {
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
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) {}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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.utilcode.util.AppUtils
|
||||
import com.mogo.eagle.core.utilcode.util.CleanUtils
|
||||
import java.lang.Exception
|
||||
@@ -17,6 +18,7 @@ object ARouterStartUp {
|
||||
}
|
||||
// 初始化 arouter
|
||||
ARouter.init(app)
|
||||
ThreadOptInitializer.aRouterInit.set(true)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
// 由于ARouter会在SP_AROUTER_CACHE.xml缓存路由表,如果出现了被删除的情况会报错,这里清除下就好了
|
||||
|
||||
@@ -34,7 +34,7 @@ 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.3.0"
|
||||
classpath "io.github.knight-zxw:lancet-plugin:10.50.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'
|
||||
|
||||
@@ -208,7 +208,7 @@ ext {
|
||||
|
||||
|
||||
//========================= LancetX ===================
|
||||
lancetx_runtime : "io.github.knight-zxw:lancet-runtime:10.3.0",
|
||||
lancetx_runtime : "io.github.knight-zxw:lancet-runtime:10.50.0",
|
||||
|
||||
lancetx_compiler : "com.mogo.eagle.core.lancetx:compiler:1.0.0",
|
||||
|
||||
@@ -229,7 +229,7 @@ ext {
|
||||
passport_secret : "com.zhidaoauto:sdk-java:1.0.5-SNAPSHOT",
|
||||
|
||||
// 主线程卡顿监测
|
||||
block_detector : "com.mogo.eagle.core.block:runtime:10.20.0",
|
||||
block_detector : "com.mogo.eagle.core.block:runtime:10.50.0",
|
||||
|
||||
//======================== google auto-service ===============
|
||||
google_auto_service : "com.google.auto.service:auto-service:1.0-rc7"
|
||||
|
||||
@@ -23,6 +23,7 @@ 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.*
|
||||
import com.mogo.eagle.core.function.api.devatools.mofang.*
|
||||
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider
|
||||
import com.mogo.eagle.core.function.api.lookaround.*
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.w
|
||||
@@ -53,6 +54,7 @@ import com.zhjt.mogo_core_function_devatools.mofang.*
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.MonitorManager
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb.Companion.getDb
|
||||
import com.zhjt.mogo_core_function_devatools.perf.MoGoCpuUsageProviderImpl
|
||||
import com.zhjt.mogo_core_function_devatools.report.IPCReportManager.Companion.iPCReportManager
|
||||
import com.zhjt.mogo_core_function_devatools.scene.SceneManager.Companion.sceneManager
|
||||
import com.zhjt.mogo_core_function_devatools.status.StatusManager
|
||||
@@ -86,6 +88,8 @@ class DevaToolsProvider : IDevaToolsProvider {
|
||||
|
||||
private val block by lazy { MoGoBlockProviderImpl() }
|
||||
|
||||
private val usage by lazy { MoGoCpuUsageProviderImpl() }
|
||||
|
||||
@Volatile
|
||||
private var mDockerVersion: String? = null
|
||||
|
||||
@@ -394,4 +398,6 @@ class DevaToolsProvider : IDevaToolsProvider {
|
||||
override fun logRecord(): IMoGoLogRecordProvider = logRecordProvider
|
||||
|
||||
override fun block(): IMoGoBlockProvider? = block
|
||||
|
||||
override fun usage(): IMoGoCpuUsageProvider? = usage
|
||||
}
|
||||
@@ -13,6 +13,10 @@ internal class MainBlockLinkedLog {
|
||||
}
|
||||
}
|
||||
|
||||
fun recordCpuUsage(extra: Map<String, String>) {
|
||||
recordCpuInternal(extra)
|
||||
}
|
||||
|
||||
@ChainLog(
|
||||
linkChainLog = ChainConstant.CHAIN_TYPE_ANR_LEAK,
|
||||
linkCode = ChainConstant.CHAIN_SOURCE_HMI,
|
||||
@@ -20,4 +24,13 @@ internal class MainBlockLinkedLog {
|
||||
paramIndexes = [0]
|
||||
)
|
||||
private fun recordInternal(extra: Map<String, List<String>>) {}
|
||||
|
||||
|
||||
@ChainLog(
|
||||
linkChainLog = ChainConstant.CHAIN_TYPE_ANR_LEAK,
|
||||
linkCode = ChainConstant.CHAIN_SOURCE_HMI,
|
||||
nodeAliasCode = ChainConstant.CHAIN_CODE_MAIN_BLOCK,
|
||||
paramIndexes = [0]
|
||||
)
|
||||
private fun recordCpuInternal(extra: Map<String, String>) {}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import com.mogo.eagle.core.block.runtime.config.recorder.IMessageRecorder.OnDump
|
||||
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 com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
|
||||
import java.util.concurrent.TimeUnit.SECONDS
|
||||
|
||||
internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener {
|
||||
@@ -26,7 +27,7 @@ internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener {
|
||||
.multiplier(2.0f)
|
||||
.isDebug(false)
|
||||
.period(5, SECONDS)
|
||||
.junkRateThreshold(0.8f)
|
||||
.junkRateThreshold(0.6f)
|
||||
.recorder(null, 500, 500)
|
||||
.build())
|
||||
hasInit = true
|
||||
@@ -49,6 +50,19 @@ internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener {
|
||||
override fun OnDumped(data: Map<Int, List<String>>) {
|
||||
map["history"] = data[0] ?: emptyList()
|
||||
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 }%"
|
||||
cpu.entries.sortedByDescending { it.value }.forEach { sorted ->
|
||||
it[sorted.key] = "${ sorted.value * 1.0f * 100 / processLaunchedTime }%"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
linkedLog.record(map)
|
||||
}
|
||||
})
|
||||
@@ -81,7 +95,6 @@ internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener {
|
||||
}
|
||||
|
||||
override fun recorder(): IMessageRecorder {
|
||||
|
||||
return BlockDetector.recorder()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.zhjt.mogo_core_function_devatools.perf
|
||||
|
||||
import android.os.SystemClock
|
||||
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
internal class MoGoCpuUsageProviderImpl: IMoGoCpuUsageProvider {
|
||||
|
||||
private var processLaunchTime: Long = 0L
|
||||
|
||||
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 lock by lazy { ReentrantLock() }
|
||||
|
||||
private val builder by lazy { ThreadLocal<StringBuilder>() }
|
||||
|
||||
override fun onProcessLaunched(time: Long) {
|
||||
processLaunchTime = time
|
||||
}
|
||||
|
||||
override fun startMainThreadTime(time: Long) {
|
||||
mainThreadStartTime = time
|
||||
}
|
||||
|
||||
override fun updateMainThreadTime(time: Long) {
|
||||
if (mainThreadLastTime == 0L) {
|
||||
mainThreadLastTime = mainThreadStartTime
|
||||
}
|
||||
val delta = time - mainThreadLastTime
|
||||
if (delta > 0) {
|
||||
mainThreadCpuUsage += delta
|
||||
}
|
||||
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)
|
||||
}
|
||||
map[builder.toString()] = map.getOrPut(builder.toString()) { 0 } + usage
|
||||
}
|
||||
}
|
||||
|
||||
override fun dump(): LinkedHashMap<String, Long> {
|
||||
return lock.withLock {
|
||||
if (map.isNotEmpty()) {
|
||||
val iterator = map.entries.iterator()
|
||||
val rst = LinkedHashMap<String, Long>()
|
||||
rst["MainThreadCpuUsage"] = mainThreadCpuUsage
|
||||
rst["ProcessLaunchedTime"] = SystemClock.elapsedRealtimeNanos() - processLaunchTime
|
||||
while (iterator.hasNext()) {
|
||||
val next = iterator.next()
|
||||
rst[next.key] = next.value
|
||||
}
|
||||
rst
|
||||
} else {
|
||||
LinkedHashMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,72 @@
|
||||
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.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() {
|
||||
|
||||
|
||||
private volatile boolean recorded = false;
|
||||
|
||||
private final ThreadLocal<Long> start = new ThreadLocal<>();
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onExecutorBefore(@NonNull ThreadConfig.TaskType taskType, @Nullable Object task, @Nullable String desc) {
|
||||
|
||||
if (!recorded && aRouterInit.get()) {
|
||||
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
|
||||
if (usage != null) {
|
||||
recorded = true;
|
||||
HandlerCompat.createAsync(Looper.getMainLooper()).post(() -> usage.startMainThreadTime(Debug.threadCpuTimeNanos()));
|
||||
usage.onProcessLaunched(SystemClock.elapsedRealtimeNanos());
|
||||
}
|
||||
}
|
||||
if (recorded) {
|
||||
start.set(Debug.threadCpuTimeNanos());
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecutorStateChanged(@NonNull ThreadPoolExecutor threadPoolExecutor, int core, int max, int active, long completed) {
|
||||
|
||||
}
|
||||
public void onExecutorStateChanged(@NonNull ThreadPoolExecutor threadPoolExecutor, int core, int max, int active, long completed) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ 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.*
|
||||
import com.mogo.eagle.core.function.api.devatools.mofang.*
|
||||
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider
|
||||
import com.mogo.eagle.core.function.api.lookaround.*
|
||||
import com.mogo.eagle.core.function.api.upgrade.*
|
||||
|
||||
@@ -238,4 +239,9 @@ interface IDevaToolsProvider : IProvider {
|
||||
* 主线程卡顿监控
|
||||
*/
|
||||
fun block(): IMoGoBlockProvider?
|
||||
|
||||
/**
|
||||
* 各线程CPU使用率
|
||||
*/
|
||||
fun usage(): IMoGoCpuUsageProvider?
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.mogo.eagle.core.function.api.devatools.perf
|
||||
|
||||
interface IMoGoCpuUsageProvider {
|
||||
|
||||
fun onProcessLaunched(time: Long)
|
||||
|
||||
fun startMainThreadTime(time: Long)
|
||||
|
||||
fun updateMainThreadTime(time: Long)
|
||||
|
||||
fun incrementOtherThreadUsage(group: String, t: Thread, usage: Long)
|
||||
|
||||
fun dump(): LinkedHashMap<String, Long>
|
||||
}
|
||||
@@ -18,6 +18,7 @@ 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.*
|
||||
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider
|
||||
import com.mogo.eagle.core.function.api.upgrade.*
|
||||
import com.mogo.eagle.core.function.api.devatools.strict.*
|
||||
import com.mogo.eagle.core.function.api.lookaround.*
|
||||
@@ -281,4 +282,6 @@ object CallerDevaToolsManager {
|
||||
fun logcat(): IMoGoLogRecordProvider? = devaToolsProviderApi?.logRecord()
|
||||
|
||||
fun block(): IMoGoBlockProvider? = devaToolsProviderApi?.block()
|
||||
|
||||
fun usage(): IMoGoCpuUsageProvider? = devaToolsProviderApi?.usage()
|
||||
}
|
||||
@@ -3,7 +3,10 @@ LancetX {
|
||||
enable true
|
||||
enableInDebug true
|
||||
synchronizedLock {
|
||||
enabled false
|
||||
enabled true
|
||||
blackList = [
|
||||
"okio.AsyncTimeout\$Watchdog"
|
||||
]
|
||||
}
|
||||
weaveGroup {
|
||||
|
||||
@@ -20,7 +23,7 @@ LancetX {
|
||||
enable true
|
||||
}
|
||||
main_block_check {
|
||||
enable false
|
||||
enable true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user