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

View File

@@ -5,6 +5,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.DeadObjectException
import android.os.IBinder import android.os.IBinder
import android.util.Log import android.util.Log
import org.autojs.autojs.AbstractAutoJs.Companion.isInrt 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.autojs.util.ViewUtils
import org.autojs.autojs6.R import org.autojs.autojs6.R
import rikka.shizuku.Shizuku import rikka.shizuku.Shizuku
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import org.autojs.autojs.runtime.api.AbstractShell.Result as ShellResult import org.autojs.autojs.runtime.api.AbstractShell.Result as ShellResult
@@ -37,18 +39,27 @@ object WrappedShizuku {
} }
private var mHasBinder = false private var mHasBinder = false
private val mServiceWaiters = CopyOnWriteArrayList<CountDownLatch>()
private val mUserServiceConnection: ServiceConnection = object : ServiceConnection { private val mUserServiceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, binder: IBinder?) { override fun onServiceConnected(componentName: ComponentName, binder: IBinder?) {
Log.d(TAG, "onServiceConnected: ${componentName.className}") Log.d(TAG, "onServiceConnected: ${componentName.className}")
if (binder?.pingBinder() == true) { if (binder?.pingBinder() == true) {
service = IUserService.Stub.asInterface(binder) service = IUserService.Stub.asInterface(binder)
// Wake all waiters when binder is ready.
// zh-CN: 当 binder 就绪时, 唤醒所有等待者.
mServiceWaiters.forEach { it.countDown() }
mServiceWaiters.clear()
} else { } else {
Log.w(TAG, "invalid binder for $componentName received") Log.w(TAG, "invalid binder for $componentName received")
service = null
} }
} }
override fun onServiceDisconnected(componentName: ComponentName) { override fun onServiceDisconnected(componentName: ComponentName) {
Log.d(TAG, "onServiceDisconnected: ${componentName.className}") Log.d(TAG, "onServiceDisconnected: ${componentName.className}")
service = null
} }
} }
@@ -87,6 +98,9 @@ object WrappedShizuku {
} }
internal fun bindUserServiceIfNeeded() { internal fun bindUserServiceIfNeeded() {
if (service?.asBinder()?.pingBinder() == true) {
return
}
if (hasPermission()) { if (hasPermission()) {
bindUserService() bindUserService()
} }
@@ -148,17 +162,41 @@ object WrappedShizuku {
@ScriptInterface @ScriptInterface
fun execCommand(context: Context, cmd: String): ShellResult { 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()) { if (service == null && hasPermission()) {
onCreate()
bindUserServiceIfNeeded()
initializeShizukuServiceAndWait(5000L) initializeShizukuServiceAndWait(5000L)
} }
val service = service ?: when { val service = service ?: when {
!hasPermission() -> R.string.error_no_permission_to_access_shizuku !hasPermission() -> R.string.error_no_permission_to_access_shizuku
!isRunning() -> R.string.error_shizuku_service_may_be_not_running !isRunning() -> R.string.error_shizuku_service_may_be_not_running
else -> R.string.error_unable_to_use_shizuku_service else -> R.string.error_unable_to_use_shizuku_service
}.let { throw IllegalStateException(context.getString(it)) } }.let { throw IllegalStateException(context.getString(it)) }
return try { 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) { } 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 { ShellResult().apply {
code = 1 code = 1
error = e.message ?: when { error = e.message ?: when {
@@ -191,17 +229,25 @@ object WrappedShizuku {
} }
private fun initializeShizukuServiceAndWait(@Suppress("SameParameterValue") timeout: Long) { private fun initializeShizukuServiceAndWait(@Suppress("SameParameterValue") timeout: Long) {
onCreate() if (service?.asBinder()?.pingBinder() == true) {
bindUserServiceIfNeeded() return
}
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
val tmpConnection = object : ServiceConnection { mServiceWaiters.add(latch)
override fun onServiceConnected(name: ComponentName, binder: IBinder) = latch.countDown()
override fun onServiceDisconnected(name: ComponentName) = Unit // 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() { override fun onStart() {
super.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() { private fun recreateIfNeeded() {

View File

@@ -1,5 +1,5 @@
#Tue Dec 23 06:37:24 CST 2025 #Wed Dec 24 16:10:20 CST 2025
BUILD_TIME=1766443044399 BUILD_TIME=1766563820979
COMPILE_SDK_VERSION=36 COMPILE_SDK_VERSION=36
IMAGE_QUANT_CMAKE_VERSION=3.22.1 IMAGE_QUANT_CMAKE_VERSION=3.22.1
IMAGE_QUANT_NDK_VERSION=26.1.10909125 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 RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
TARGET_SDK_VERSION=36 TARGET_SDK_VERSION=36
TARGET_SDK_VERSION_INRT=29 TARGET_SDK_VERSION_INRT=29
VERSION_BUILD=3544 VERSION_BUILD=3546
VERSION_NAME=6.7.0 Alpha12 VERSION_NAME=6.7.0 Alpha12
VSCODE_EXT_REQUIRED_VERSION=1.0.8 VSCODE_EXT_REQUIRED_VERSION=1.0.8