定义插件 Manifest

July 29, 2026 · View on GitHub

English · 插件开发指南

插件由 TypedExtensionServiceExtensionManifest 组成。以 Hello 示例为起点,再把身份 和功能替换为自己的内容。

1. 添加 SDK

dependencies {
    implementation(project(":extension:sdk-android"))
}

2. 声明 Service

把以下 Service 声明加入插件的 AndroidManifest.xml,只替换类名:

<service
    android:name=".HelloExtensionService"
    android:exported="true"
    android:permission="com.m3u.permission.BIND_EXTENSION_HOST">
    <intent-filter>
        <action android:name="com.m3u.extension.action.BIND_EXTENSION" />
    </intent-filter>
</service>

外部插件通过宿主 Broker 访问网络。声明了 android.permission.INTERNET 的插件会显示为不兼容。

该类必须继承 TypedExtensionService

class HelloExtensionService : TypedExtensionService() {
    override val extensionManifest = ExtensionManifest(
        id = ExtensionId("com.m3u.samples.hello"),
        displayName = "Hello Extension",
        extensionVersion = ExtensionSemanticVersion(1, 0, 0),
        apiRange = ExtensionApiRange(
            minimum = ExtensionApiVersions.Current,
            maximum = ExtensionApiVersions.Current,
        ),
        hooks = emptySet(),
        capabilities = emptySet(),
        metadata = mapOf("developer" to "M3UAndroid sample"),
    )
}

需要替换的值:

填写内容
id插件自有的小写稳定 ID。
displayNameM3UAndroid 中显示的名称。
extensionVersion当前插件构建的版本。
apiRange当前构建支持的 M3UAndroid 插件 API 范围。
metadata["developer"]随插件显示的开发者名称。

3. 声明每个已实现 Hook

处理函数和声明必须使用同一个 HookSpecrequiredCapabilities 中的每一项都要有对应 的 ExtensionCapabilityRequest,并填写用户能看懂的具体用途。下一页会给出完整的设置 Hook 声明和处理函数。

4. 按需声明固定设置

不依赖当前请求的字段写在 settingsSchema 中:

settingsSchema = ExtensionSettingSchema(
    version = 1,
    fields = listOf(
        ExtensionSettingField(
            key = "greeting",
            label = "Greeting",
            type = ExtensionSettingType.TEXT,
            defaultValue = JsonPrimitive("Hello from my extension"),
        )
    ),
)

M3UAndroid 负责显示和保存这些值。如果字段取决于 Hook 请求,例如 request.surface, 应由设置 Hook 返回。

发布更新时必须保持插件身份稳定,具体字段见准备发布或更新

下一步:注册类型化 Hook