6.7.0 - Alpha12 - 修复 Shizuku 用户服务进程未能正常结束导致进程堆积的问题 (issue #474)

This commit is contained in:
SuperMonster003
2025-12-25 12:56:42 +08:00
parent 32dbaa65e2
commit 160472a5a5
5 changed files with 83 additions and 14 deletions

View File

@@ -1,7 +1,7 @@
{
"$data": {
"v6.7.0": {
"released_date": "2025/12/23",
"released_date": "2025/12/25",
"feature": [
"插件中心功能, 支持插件的安装/卸载/更新等操作 (入口: 主页抽屉按钮)",
"cvt 模块, 用于数据单位转换 (参阅 项目文档 > [单位转换](https://docs.autojs6.com/#/cvt))",
@@ -57,6 +57,7 @@
"项目配置文件中构建版本号或构建时间出现较大数字时可能导致应用崩溃的问题",
"频繁获取或重建 ImageReader 时可能因缓冲区暂无可用帧导致应用崩溃的问题",
"输入事件观察器 InputEventObserver 可能导致应用启动时明显卡顿的问题",
"Shizuku 用户服务进程未能正常结束导致进程堆积的问题 _[`issue #474`](http://issues.autojs6.com/474)_",
"打包应用无法正常使用 Paddle OCR 与 Rapid OCR 功能的问题",
"版本历史页面部分系统因字体差别导致统计数据显示不完整的问题",
"部分设备无法正常初始化 MLKit Google OCR 的问题 (试修) _[`issue #8`](http://issues.autojs6.com/8#issuecomment-3117061768)_",

View File

@@ -3,11 +3,13 @@ package org.autojs.autojs.core.shizuku
import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.os.Process
import android.os.RemoteException
import android.util.Log
import androidx.annotation.Keep
import org.autojs.autojs.runtime.api.AbstractShell
import org.autojs.autojs.runtime.api.ProcessShell
import kotlin.system.exitProcess
class UserService : IUserService.Stub {
@@ -49,8 +51,22 @@ class UserService : IUserService.Stub {
*/
override fun destroy() {
Log.i("UserService", "destroy")
// Ensure the user service process terminates when Shizuku server requests destroy.
// zh-CN: 确保 Shizuku server 请求 destroy 时, user service 进程能够真正退出.
runCatching {
Process.killProcess(Process.myPid())
}
// Fallback to exit the process if killProcess doesn't stop it immediately.
// zh-CN: 如果 killProcess 未能立刻终止, 则使用 exitProcess 作为兜底退出.
runCatching {
exitProcess(0)
}
}
// Exit method defined by user.
// zh-CN: 用户定义的退出方法.
override fun exit() {
destroy()
}

View File

@@ -5,6 +5,7 @@ import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.os.DeadObjectException
import android.os.IBinder
import android.util.Log
import org.autojs.autojs.AbstractAutoJs.Companion.isInrt
@@ -17,6 +18,7 @@ import org.autojs.autojs.util.App.SHIZUKU
import org.autojs.autojs.util.ViewUtils
import org.autojs.autojs6.R
import rikka.shizuku.Shizuku
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import org.autojs.autojs.runtime.api.AbstractShell.Result as ShellResult
@@ -37,18 +39,27 @@ object WrappedShizuku {
}
private var mHasBinder = false
private val mServiceWaiters = CopyOnWriteArrayList<CountDownLatch>()
private val mUserServiceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, binder: IBinder?) {
Log.d(TAG, "onServiceConnected: ${componentName.className}")
if (binder?.pingBinder() == true) {
service = IUserService.Stub.asInterface(binder)
// Wake all waiters when binder is ready.
// zh-CN: 当 binder 就绪时, 唤醒所有等待者.
mServiceWaiters.forEach { it.countDown() }
mServiceWaiters.clear()
} else {
Log.w(TAG, "invalid binder for $componentName received")
service = null
}
}
override fun onServiceDisconnected(componentName: ComponentName) {
Log.d(TAG, "onServiceDisconnected: ${componentName.className}")
service = null
}
}
@@ -87,6 +98,9 @@ object WrappedShizuku {
}
internal fun bindUserServiceIfNeeded() {
if (service?.asBinder()?.pingBinder() == true) {
return
}
if (hasPermission()) {
bindUserService()
}
@@ -148,17 +162,41 @@ object WrappedShizuku {
@ScriptInterface
fun execCommand(context: Context, cmd: String): ShellResult {
return execCommandWithAutoReconnect(context, cmd, allowRetry = true)
}
private fun execCommandWithAutoReconnect(context: Context, cmd: String, allowRetry: Boolean): ShellResult {
if (service == null && hasPermission()) {
onCreate()
bindUserServiceIfNeeded()
initializeShizukuServiceAndWait(5000L)
}
val service = service ?: when {
!hasPermission() -> R.string.error_no_permission_to_access_shizuku
!isRunning() -> R.string.error_shizuku_service_may_be_not_running
else -> R.string.error_unable_to_use_shizuku_service
}.let { throw IllegalStateException(context.getString(it)) }
return try {
ShellResult.fromJson(service.execCommand(cmd.replace(Regex("^\\s*adb\\s+shell\\s+", RegexOption.IGNORE_CASE), "")))
ShellResult.fromJson(
service.execCommand(
cmd.replace(Regex("^\\s*adb\\s+shell\\s+", RegexOption.IGNORE_CASE), "")
)
)
} catch (e: Throwable) {
// Reconnect and retry once when binder is dead.
// zh-CN: 当 binder 已死亡时, 自动重连并重试一次.
if (allowRetry && e is DeadObjectException) {
this.service = null
runCatching {
onCreate()
bindUserServiceIfNeeded()
initializeShizukuServiceAndWait(5000L)
}
return execCommandWithAutoReconnect(context, cmd, allowRetry = false)
}
ShellResult().apply {
code = 1
error = e.message ?: when {
@@ -191,17 +229,25 @@ object WrappedShizuku {
}
private fun initializeShizukuServiceAndWait(@Suppress("SameParameterValue") timeout: Long) {
onCreate()
bindUserServiceIfNeeded()
if (service?.asBinder()?.pingBinder() == true) {
return
}
val latch = CountDownLatch(1)
val tmpConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) = latch.countDown()
override fun onServiceDisconnected(name: ComponentName) = Unit
mServiceWaiters.add(latch)
// Re-check after registering waiter to avoid missing a fast onServiceConnected().
// zh-CN: 注册等待者后再次检查, 避免 onServiceConnected() 很快到来导致错过唤醒.
if (service?.asBinder()?.pingBinder() == true) {
mServiceWaiters.remove(latch)
return
}
runCatching {
latch.await(timeout, TimeUnit.MILLISECONDS)
}.also {
mServiceWaiters.remove(latch)
}
Shizuku.bindUserService(mUserServiceArgs, tmpConnection)
latch.await(timeout, TimeUnit.MILLISECONDS)
Shizuku.unbindUserService(mUserServiceArgs, tmpConnection, true)
}
}

View File

@@ -155,7 +155,13 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
override fun onStart() {
super.onStart()
WrappedShizuku.bindUserServiceIfNeeded()
// @Hint by SuperMonster003 on Dec 24, 2025.
// ! Avoid binding Shizuku user service on app start.
// ! It may spawn root user-service processes repeatedly during IDE "Run" (force-stop + relaunch).
// ! zh-CN:
// ! 避免在应用启动时绑定 Shizuku user service.
// ! IDE "Run" (force-stop + relaunch) 期间可能反复拉起 root user-service 进程.
// # WrappedShizuku.bindUserServiceIfNeeded()
}
private fun recreateIfNeeded() {

View File

@@ -1,5 +1,5 @@
#Tue Dec 23 06:37:24 CST 2025
BUILD_TIME=1766443044399
#Wed Dec 24 16:10:20 CST 2025
BUILD_TIME=1766563820979
COMPILE_SDK_VERSION=36
IMAGE_QUANT_CMAKE_VERSION=3.22.1
IMAGE_QUANT_NDK_VERSION=26.1.10909125
@@ -27,6 +27,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
TARGET_SDK_VERSION=36
TARGET_SDK_VERSION_INRT=29
VERSION_BUILD=3544
VERSION_BUILD=3546
VERSION_NAME=6.7.0 Alpha12
VSCODE_EXT_REQUIRED_VERSION=1.0.8