refactor: migrate and update to YukiHookAPI 1.3.0

This commit is contained in:
2025-06-25 23:22:59 +08:00
parent ab03c74659
commit e59b7f501a
17 changed files with 170 additions and 94 deletions

View File

@@ -84,7 +84,8 @@ androidComponents {
dependencies {
implementation(com.fankes.projectpromote.project.promote)
implementation(com.highcapable.yukireflection.api)
implementation(com.highcapable.kavaref.kavaref.core)
implementation(com.highcapable.kavaref.kavaref.extension)
implementation(androidx.core.core.ktx)
implementation(androidx.appcompat.appcompat)
implementation(com.google.android.material.material)

View File

@@ -19,21 +19,20 @@
*
* This file is created by fankes on 2022/5/10.
*/
@file:Suppress("DEPRECATION")
package com.fankes.apperrorsdemo.ui.activity.base
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding
import com.fankes.apperrorsdemo.R
import com.fankes.apperrorsdemo.utils.factory.isNotSystemInDarkMode
import com.highcapable.yukireflection.factory.current
import com.highcapable.yukireflection.factory.method
import com.highcapable.yukireflection.type.android.LayoutInflaterClass
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
import com.highcapable.kavaref.extension.toClassOrNull
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
@@ -42,10 +41,11 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = current().generic()?.argument()?.method {
val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
name = "inflate"
param(LayoutInflaterClass)
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed")
parameters(LayoutInflater::class)
}?.invoke<VB>(layoutInflater) ?: error("binding failed")
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
setContentView(binding.root)
/** 隐藏系统的标题栏 */
@@ -55,6 +55,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode
}
@Suppress("DEPRECATION")
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it
window?.navigationBarColor = it

View File

@@ -22,7 +22,7 @@ repositories:
plugins:
com.android.application:
alias: android-application
version: 8.9.0
version: 8.9.3
org.jetbrains.kotlin.android:
alias: kotlin-android
version: 2.1.10
@@ -46,12 +46,14 @@ libraries:
rovo89-xposed-api
com.highcapable.yukihookapi:
api:
version: 1.2.1
version: 1.3.0
ksp-xposed:
version-ref: <this>::api
com.highcapable.yukireflection:
api:
version: 1.0.3
com.highcapable.kavaref:
kavaref-core:
version: 1.0.0
kavaref-extension:
version: 1.0.0
com.microsoft.appcenter:
appcenter-analytics:
version: 5.0.6
@@ -69,13 +71,13 @@ libraries:
version: 2.12.1
com.squareup.okhttp3:
okhttp:
version: 5.0.0-alpha.14
version: 5.0.0-alpha.16
androidx.core:
core-ktx:
version: 1.15.0
version: 1.16.0
androidx.appcompat:
appcompat:
version: 1.7.0
version: 1.7.1
com.google.android.material:
material:
version: 1.12.0

View File

@@ -82,6 +82,8 @@ dependencies {
compileOnly(de.robv.android.xposed.api)
implementation(com.highcapable.yukihookapi.api)
ksp(com.highcapable.yukihookapi.ksp.xposed)
implementation(com.highcapable.kavaref.kavaref.core)
implementation(com.highcapable.kavaref.kavaref.extension)
implementation(com.fankes.projectpromote.project.promote)
implementation(com.microsoft.appcenter.appcenter.analytics)
implementation(com.microsoft.appcenter.appcenter.crashes)

View File

@@ -30,6 +30,7 @@ import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.Build
import android.os.Bundle
import android.os.Message
import android.os.SystemClock
import android.util.ArrayMap
@@ -58,16 +59,10 @@ import com.fankes.apperrorstracking.utils.factory.toArrayList
import com.fankes.apperrorstracking.utils.factory.toast
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import com.fankes.apperrorstracking.wrapper.BuildConfigWrapper
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.VariousClass
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.constructor
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasMethod
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.android.BundleClass
import com.highcapable.yukihookapi.hook.type.android.MessageClass
import com.highcapable.yukihookapi.hook.type.java.BooleanType
object FrameworkHooker : YukiBaseHooker() {
@@ -110,40 +105,73 @@ object FrameworkHooker : YukiBaseHooker() {
* 获取当前包列表实例
* @return [Any] or null
*/
private val pkgList = if (ProcessRecordClass.hasMethod { name = "getPkgList"; emptyParam() })
ProcessRecordClass.method { name = "getPkgList"; emptyParam() }.get(proc).call()
else ProcessRecordClass.field { name { it.endsWith("pkgList", true) } }.get(proc).any()
private val pkgList by lazy {
ProcessRecordClass.resolve().optional(silent = true)
.firstMethodOrNull {
name = "getPkgList"
emptyParameters()
}?.of(proc)?.invoke()
?: ProcessRecordClass.resolve().optional(silent = true)
.firstFieldOrNull {
name { it.endsWith("pkgList", true) }
}?.of(proc)?.get()
}
/**
* 获取当前包列表数组大小
* @return [Int]
*/
private val pkgListSize = PackageListClass?.method { name = "size"; emptyParam() }?.get(pkgList)?.int()
?: ProcessRecordClass.field { name = "pkgList" }.get(proc).cast<ArrayMap<*, *>>()?.size ?: -1
private val pkgListSize by lazy {
PackageListClass?.resolve()?.optional(silent = true)
?.firstMethodOrNull {
name = "size"
emptyParameters()
}?.of(pkgList)?.invoke()
?: ProcessRecordClass.resolve().optional(silent = true)
.firstFieldOrNull { name = "pkgList" }
?.of(proc)?.get<ArrayMap<*, *>>()?.size ?: -1
}
/**
* 获取当前 pid 信息
* @return [Int]
*/
val pid = ProcessRecordClass.field { name { it == "mPid" || it == "pid" } }.get(proc).int()
val pid by lazy {
ProcessRecordClass.resolve().optional()
.firstFieldOrNull {
name { it == "mPid" || it == "pid" }
}?.of(proc)?.get<Int>() ?: 0
}
/**
* 获取当前用户 ID 信息
* @return [Int]
*/
val userId = ProcessRecordClass.field { name = "userId" }.get(proc).int()
val userId by lazy {
ProcessRecordClass.resolve().optional()
.firstFieldOrNull { name = "userId" }
?.of(proc)?.get<Int>() ?: 0
}
/**
* 获取当前 APP 信息
* @return [ApplicationInfo] or null
*/
val appInfo = ProcessRecordClass.field { name = "info" }.get(proc).cast<ApplicationInfo>()
val appInfo by lazy {
ProcessRecordClass.resolve().optional()
.firstFieldOrNull { name = "info" }
?.of(proc)?.get<ApplicationInfo>()
}
/**
* 获取当前进程名称
* @return [String]
*/
val processName = ProcessRecordClass.field { name = "processName" }.get(proc).string()
val processName by lazy {
ProcessRecordClass.resolve().optional()
.firstFieldOrNull { name = "processName" }
?.of(proc)?.get<String>() ?: ""
}
/**
* 获取当前 APP、进程 包名
@@ -167,17 +195,25 @@ object FrameworkHooker : YukiBaseHooker() {
* 获取当前进程是否为后台进程
* @return [Boolean]
*/
val isBackgroundProcess = UserControllerClass
.method { name { it == "getCurrentProfileIds" || it == "getCurrentProfileIdsLocked" } }
.get(ActivityManagerServiceClass?.field { name = "mUserController" }
?.get(AppErrorsClass.field { name = "mService" }.get(errors).any())?.any())
.invoke<IntArray>()?.takeIf { it.isNotEmpty() }?.any { it != userId } ?: false
val isBackgroundProcess by lazy {
UserControllerClass.resolve().optional()
.firstMethodOrNull { name { it == "getCurrentProfileIds" || it == "getCurrentProfileIdsLocked" } }
?.of(ActivityManagerServiceClass?.resolve()?.optional()?.firstFieldOrNull { name = "mUserController" }
?.of(AppErrorsClass.resolve().optional().firstFieldOrNull { name = "mService" }?.of(errors)?.get())?.getQuietly())
?.invokeQuietly<IntArray>()?.takeIf { it.isNotEmpty() }?.any { it != userId } ?: false
}
/**
* 获取当前进程是否短时内重复崩溃
* @return [Boolean]
*/
val isRepeatingCrash = resultData?.let { AppErrorDialog_DataClass.field { name = "repeating" }.get(it).boolean() } ?: false
val isRepeatingCrash by lazy {
resultData?.let {
AppErrorDialog_DataClass.resolve().optional()
.firstFieldOrNull { name = "repeating" }
?.of(it)?.get<Boolean>() == true
} ?: false
}
}
/** 注册生命周期 */
@@ -376,60 +412,56 @@ object FrameworkHooker : YukiBaseHooker() {
/** 注册生命周期 */
registerLifecycle()
/** 干掉原生错误对话框 - 如果有 */
ErrorDialogControllerClass?.apply {
if (hasMethod { name = "hasCrashDialogs"; emptyParam() }) {
method {
name = "hasCrashDialogs"
emptyParam()
}.hook().replaceToTrue()
} else {
constructor {
paramCount = 1
}.hook().after {
field { name = "mCrashDialogs" }.get(instance).set(emptyList<Any>())
ErrorDialogControllerClass?.resolve()?.optional(silent = true)?.apply {
val hasCrashDialogs = firstMethodOrNull {
name = "hasCrashDialogs"
emptyParameters()
}?.hook()?.replaceToTrue() != null
if (!hasCrashDialogs)
firstConstructorOrNull {
parameterCount = 1
}?.hook()?.after {
firstFieldOrNull { name = "mCrashDialogs" }?.of(instance)?.set(emptyList<Any>())
}
}
method {
firstMethodOrNull {
name = "showCrashDialogs"
paramCount = 1
}.hook().intercept()
parameterCount = 1
}?.hook()?.intercept()
}
/** 干掉原生错误对话框 - API 30 以下 */
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
ActivityTaskManagerService_LocalServiceClass?.method {
ActivityTaskManagerService_LocalServiceClass?.resolve()?.optional()?.firstMethodOrNull {
name = "canShowErrorDialogs"
emptyParam()
}?.ignored()?.hook()?.replaceToFalse()
ActivityManagerServiceClass?.method {
emptyParameters()
}?.hook()?.replaceToFalse()
ActivityManagerServiceClass?.resolve()?.optional()?.firstMethodOrNull {
name = "canShowErrorDialogs"
emptyParam()
}?.ignored()?.hook()?.replaceToFalse()
emptyParameters()
}?.hook()?.replaceToFalse()
}
/** 干掉原生错误对话框 - 如果上述方法全部失效则直接结束对话框 */
AppErrorDialogClass.apply {
method {
AppErrorDialogClass.resolve().optional(silent = true).apply {
firstMethodOrNull {
name = "onCreate"
param(BundleClass)
}.ignored().hook().after { instance<Dialog>().cancel() }
method {
parameters(Bundle::class)
}?.hook()?.after { instance<Dialog>().cancel() }
firstMethodOrNull {
name = "onStart"
emptyParam()
}.ignored().hook().after { instance<Dialog>().cancel() }
emptyParameters()
}?.hook()?.after { instance<Dialog>().cancel() }
}
/** 注入自定义错误对话框 */
AppErrorsClass.apply {
AppErrorsClass.resolve().optional().apply {
when {
Build.VERSION.SDK_INT > Build.VERSION_CODES.R -> {
method {
firstMethodOrNull {
name = "handleAppCrashLSPB"
paramCount = 6
}.hook().after {
parameterCount = 6
}?.hook()?.after {
/** 如果为用户终止则不展示异常 */
if (args(index = 1).string() == "user-terminated") return@after
/** 当前实例 */
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@after
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after
/** 当前进程信息 */
val proc = args().first().any() ?: return@after YLog.warn("Received but got null ProcessRecord (Show UI failed)")
@@ -441,29 +473,29 @@ object FrameworkHooker : YukiBaseHooker() {
}
}
else -> {
method {
firstMethodOrNull {
name = "handleShowAppErrorUi"
param(MessageClass)
}.hook().after {
parameters(Message::class)
}?.hook()?.after {
/** 当前实例 */
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@after
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after
/** 当前错误数据 */
val resultData = args().first().cast<Message>()?.obj
/** 当前进程信息 */
val proc = AppErrorDialog_DataClass.field { name = "proc" }.get(resultData).any()
val proc = AppErrorDialog_DataClass.resolve().optional().firstFieldOrNull { name = "proc" }?.of(resultData)?.get()
/** 创建 APP 进程异常数据类 */
AppErrorsProcessData(instance, proc, resultData).handleShowAppErrorUi(context)
}
}
}
method {
firstMethodOrNull {
name = "handleAppCrashInActivityController"
returnType = BooleanType
}.hook().after {
returnType = Boolean::class
}?.hook()?.after {
/** 当前实例 */
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@after
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after
/** 当前进程信息 */
val proc = args().first().any() ?: return@after YLog.warn("Received but got null ProcessRecord")

View File

@@ -19,14 +19,13 @@
*
* This file is created by fankes on 2022/5/7.
*/
@file:Suppress("DEPRECATION")
package com.fankes.apperrorstracking.ui.activity.base
import android.app.ActivityManager
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat
@@ -34,9 +33,9 @@ import androidx.viewbinding.ViewBinding
import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.utils.factory.isNotSystemInDarkMode
import com.fankes.apperrorstracking.utils.factory.toast
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
import com.highcapable.kavaref.extension.toClassOrNull
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
@@ -45,10 +44,11 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = current().generic()?.argument()?.method {
val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
name = "inflate"
param(LayoutInflaterClass)
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed")
parameters(LayoutInflater::class)
}?.invoke<VB>(layoutInflater) ?: error("binding failed")
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
setContentView(binding.root)
/** 隐藏系统的标题栏 */
@@ -58,6 +58,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode
}
@Suppress("DEPRECATION")
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it
window?.navigationBarColor = it

View File

@@ -19,6 +19,8 @@
*
* This file is created by fankes on 2022/6/3.
*/
@file:Suppress("DEPRECATION")
package com.fankes.apperrorstracking.utils.factory
import android.content.Context

View File

@@ -19,7 +19,7 @@
*
* This file is created by fankes on 2022/5/12.
*/
@file:Suppress("unused")
@file:Suppress("unused", "DEPRECATION")
package com.fankes.apperrorstracking.utils.factory

View File

@@ -19,7 +19,7 @@
*
* This file is created by fankes on 2022/5/7.
*/
@file:Suppress("unused", "NotificationPermission")
@file:Suppress("unused", "NotificationPermission", "DEPRECATION")
package com.fankes.apperrorstracking.utils.factory

View File

@@ -776,6 +776,35 @@
android:textColor="@color/colorTextGray"
android:textSize="11sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:background="@drawable/bg_permotion_round"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_kavaref" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:ellipsize="end"
android:lineSpacingExtra="6dp"
android:maxLines="2"
android:text="@string/about_module_extension"
android:textColor="@color/colorTextGray"
android:textSize="11sp" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -157,4 +157,5 @@
<string name="stack_trace_share_system_build_id">システムビルド ID</string>
<string name="stack_trace_share_package_name">アプリのパッケージ名</string>
<string name="stack_trace_share_other">その他の要件</string>
<string name="about_module_extension">このモジュールは KavaRef を搭載しています。\n詳細はこちら https://github.com/HighCapable/KavaRef</string>
</resources>

View File

@@ -159,4 +159,5 @@
<string name="stack_trace_share_other">其它必要信息</string>
<string name="share_with_file">以文件方式分享异常堆栈</string>
<string name="share_with_file_tip">使用文件的方式代替文本分享异常堆栈</string>
<string name="about_module_extension">此模块使用 KavaRef 强力驱动。\n了解更多 https://github.com/HighCapable/KavaRef</string>
</resources>

View File

@@ -155,4 +155,5 @@
<string name="stack_trace_share_system_build_id">系統建置 ID</string>
<string name="stack_trace_share_package_name">應用程式包名稱</string>
<string name="stack_trace_share_other">其它必要資訊</string>
<string name="about_module_extension">此模組使用 KavaRef 強力驅動。\n了解更多 https://github.com/HighCapable/KavaRef</string>
</resources>

View File

@@ -155,4 +155,5 @@
<string name="stack_trace_share_system_build_id">系統建置 ID</string>
<string name="stack_trace_share_package_name">應用程式包名稱</string>
<string name="stack_trace_share_other">其它必要資訊</string>
<string name="about_module_extension">此模組使用 KavaRef 強力驅動。\n了解更多 https://github.com/HighCapable/KavaRef</string>
</resources>

View File

@@ -155,4 +155,5 @@
<string name="stack_trace_share_system_build_id">系統建置 ID</string>
<string name="stack_trace_share_package_name">應用程式包名稱</string>
<string name="stack_trace_share_other">其它必要資訊</string>
<string name="about_module_extension">此模組使用 KavaRef 強力驅動。\n瞭解更多 https://github.com/HighCapable/KavaRef</string>
</resources>

View File

@@ -162,4 +162,5 @@
<string name="stack_trace_share_other">Other Requirement</string>
<string name="share_with_file">Share errors stacktrace with file</string>
<string name="share_with_file_tip">Share errors stacktrace using files instead of text</string>
<string name="about_module_extension">This Xposed Module is powered by KavaRef.\nLearn more https://github.com/HighCapable/KavaRef</string>
</resources>