You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xposed模块内部绑定BridgeService失败问题求助

Xposed模块桥接服务绑定失败问题

我正在开发一款Android Xposed模块,需要实现带GUI的钩子开关管理器,核心是在handleLoadPackage()(实现IXposedHookLoadPackage)初始化的Xposed部分读写配置。

查过文档和论坛后,确定桥接服务是最优方案,还参考了同类Xposed模块的服务/客户端/桥接逻辑,但现在遇到瓶颈:模块无法绑定桥接服务,Xposed部分找不到BridgeService,Logcat仅输出以下错误:

W ActivityManager: Unable to start service Intent { cmp=com.friction/.bridge.BridgeService } U=0: not found

问题出在bindService()调用上——按传入的标志,该方法应创建并绑定服务,但执行失败,传递的Intent不被识别为有效,尽管所有必要组件都已配置。

针对同类问题,有人建议在manifest的<queries>中添加服务所在包名,但没用;而且我认为这一步没必要,因为客户端和服务在同一包内。我也试过在manifest里写全路径android:name="com.friction.bridge.BridgeService",同样无效。

这个问题卡了好几周毫无头绪,但参考的同类模块用相同方法却能正常运行。


服务定义(BridgeService.kt)

package com.friction.bridge

import android.app.Service
import android.content.Intent
import android.os.IBinder
import com.friction.bridge.types.ActionType
import com.friction.bridge.types.FileType
import com.friction.xposed.context.RemoteSideContext
import com.friction.xposed.context.SharedContextHolder

class BridgeService : Service() {
    private lateinit var remoteSideContext: RemoteSideContext

    override fun onDestroy() {
        if (::remoteSideContext.isInitialized) {
            remoteSideContext.bridgeService = null
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        remoteSideContext = SharedContextHolder.remote(this)
        remoteSideContext.apply {
            bridgeService = this@BridgeService
        }
        return BridgeBinder()
    }

    inner class BridgeBinder : BridgeInterface.Stub() {
        override fun fileOperation(action: Int, fileType: Int, content: ByteArray?): ByteArray {
            val resolvedFile = FileType.fromValue(fileType)?.resolve(this@BridgeService)

            return when (ActionType.entries[action]) {
                ActionType.CREATE_AND_READ -> {
                    resolvedFile?.let {
                        if (!it.exists()) {
                            return content?.also { content -> it.writeBytes(content) } ?: ByteArray(
                                0
                            )
                        }

                        it.readBytes()
                    } ?: ByteArray(0)
                }

                ActionType.READ -> {
                    resolvedFile?.takeIf { it.exists() }?.readBytes() ?: ByteArray(0)
                }

                ActionType.WRITE -> {
                    content?.also { resolvedFile?.writeBytes(content) } ?: ByteArray(0)
                }

                ActionType.DELETE -> {
                    resolvedFile?.takeIf { it.exists() }?.delete()
                    ByteArray(0)
                }

                ActionType.EXISTS -> {
                    if (resolvedFile?.exists() == true)
                        ByteArray(1)
                    else ByteArray(0)
                }
            }
        }

        override fun registerConfigStateListener(listener: ConfigStateListener?) {
            remoteSideContext.config.configStateListener = listener
        }
    }
}

客户端定义(BridgeClient.kt)

package com.friction.bridge
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.os.IBinder
import android.util.Log
import com.friction.BuildConfig
import com.friction.bridge.types.ActionType
import com.friction.bridge.types.FileType
import com.friction.core.Constants.LOG_TAG
import com.friction.xposed.context.ModContext
import de.robv.android.xposed.XposedHelpers
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import kotlin.system.exitProcess

fun FileLoaderWrapper.loadFromBridge(bridgeClient: BridgeClient) {
    isFileExists = { bridgeClient.isFileExists(fileType) }
    read = { bridgeClient.createAndReadFile(fileType, defaultData) }
    write = { bridgeClient.writeFile(fileType, it) }
    delete = { bridgeClient.deleteFile(fileType) }
}

class BridgeClient(private val context: ModContext): ServiceConnection {
    private lateinit var future: CompletableFuture<Boolean>
    private lateinit var service: BridgeInterface

    fun connect(onFailure: (Throwable) -> Unit, onResult: (Boolean) -> Unit) {
        this.future = CompletableFuture()

        with(context.androidContext) {
            startActivity(Intent()
                .setClassName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".bridge.ForceStartActivity")
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
            )

            val intent = Intent()
                .setClassName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".bridge.BridgeService")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                if (!bindService(
                    intent,
                    Context.BIND_AUTO_CREATE,
                    Executors.newSingleThreadExecutor(),
                    this@BridgeClient
                )) {
                    onFailure(IllegalStateException("Cannot bind to bridge service"))
                } else {
                    onResult(true)
                }
            } else {
                XposedHelpers.callMethod(
                    this,
                    "bindServiceAsUser",
                    intent,
                    this@BridgeClient,
                    Context.BIND_AUTO_CREATE,
                    Handler(HandlerThread("BridgeClient").apply {
                        start()
                    }.looper),
                    android.os.Process.myUserHandle()
                )
            }
        }
        runCatching {
            onResult(future.get(15, TimeUnit.SECONDS))
        }.onFailure {
            onFailure(it)
        }
    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        this.service = BridgeInterface.Stub.asInterface(service)
        future.complete(true)
    }

    override fun onNullBinding(name: ComponentName?) {
        Log.i("BridgeClient", "cannot connect to bridge service")
        exitProcess(1)
    }

    override fun onServiceDisconnected(name: ComponentName?) {
        Log.i("BridgeClient", "service disconnected")
        exitProcess(1)
    }

    fun createAndReadFile(type: FileType, defaultData: ByteArray): ByteArray {
        return service.fileOperation(ActionType.CREATE_AND_READ.ordinal, type.value, defaultData)
    }

    fun writeFile(type: FileType, data: ByteArray) {
        service.fileOperation(ActionType.WRITE.ordinal, type.value, data)
    }

    fun readFile(type: FileType): ByteArray {
        return service.fileOperation(ActionType.READ.ordinal, type.value, null)
    }

    fun deleteFile(type: FileType) {
        service.fileOperation(ActionType.DELETE.ordinal, type.value, null)
    }

    fun isFileExists(type: FileType): Boolean {
        return service.fileOperation(ActionType.EXISTS.ordinal, type.value, null).isNotEmpty()
    }

    fun registerConfigStateListener(listener: ConfigStateListener) {
        service.registerConfigStateListener(listener)
    }
}

Manifest中的服务声明(AndroidManifest.xml)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Friction"
        tools:targetApi="34">
        <meta-data
            android:name="xposedmodule"
            android:value="true" />
        <meta-data
            android:name="xposeddescription"
            android:value="@string/module_description" />
        <meta-data
            android:name="xposedminversion"
            android:value="93" />

        <service
            android:name=".bridge.BridgeService"
            android:exported="true"
            tools:ignore="ExportedService">
        </service>

        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".bridge.ForceStartActivity"
            android:theme="@android:style/Theme.NoDisplay"
            android:excludeFromRecents="true"
            android:exported="true" />
    </application>
</manifest>

补充信息

在应用内部尝试绑定服务时失败,但用独立的外部测试应用却能成功绑定同一服务——问题仅出在模块内部绑定,外部应用完全正常。测试代码如下:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        runCatching {
            val intent = Intent()
                .setClassName("com.friction",
                    "com.friction.bridge.BridgeService")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                val serviceConnection = object : ServiceConnection {
                    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                        Log.i("BridgeClient", "Service connected")
                    }

                    override fun onServiceDisconnected(name: ComponentName?) {
                        Log.i("BridgeClient", "Service disconnected")
                    }

                    fun connect() {
                         val ret = bindService(
                            intent,
                            Context.BIND_AUTO_CREATE,
                            Executors.newSingleThreadExecutor(),
                            this
                        )
                        Log.i("BridgeClient", "bindService returned $ret")
                    }
                }
                serviceConnection.connect()
            }
        }
    }
}

需要更多信息可以补充,求帮忙解决。


内容的提问来源于stack exchange,提问作者Rogelio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 13:59:54