Skip to content

Feat/independent task && foreground service - #1433

Merged
shifujun merged 1 commit into
Tencent:masterfrom
dylanzduan:feat/independent-task-foreground-service
Oct 19, 2026
Merged

shifujun merged 1 commit into
Tencent:masterfrom
dylanzduan:feat/independent-task-foreground-service

Conversation

@dylanzduan

Copy link
Copy Markdown
Contributor

背景:插件 Activity 需要按 launchMode 和 taskAffinity 运行在独立 Task 中;插件需要能托管前台 Service(包括 foregroundServiceType,以及多个 Service 共享同一个宿主容器)。

设计:宿主通过 ComponentManager 的 getContainerActivityPool() 和 getContainerActivityProfile() 提供独立 Task 容器池,框架负责分配和回收,并用租约 ID 防止旧 Task 迟到的 onNewIntent 被转发给新的插件 Activity。
前台 Service 由宿主注册的 PluginContainerService 承载,通过 getContainerServicePool() 或 onBindContainerService() 指定容器。

兼容性:不实现这两个容器池方法时,行为和现在完全一样;保留了旧版 Manifest 生成代码用的兼容构造方法。
宿主接入:需要在宿主 Manifest 里注册容器 Activity(声明 launchMode 和 taskAffinity),以及 PluginContainerService 及其 foregroundServiceType。具体写法可以参考测试宿主 test-dynamic-host 的配置。

测试:新增 5 个自动化用例;testSdk 在 API 36 上通过,185 个用例 0 失败,跳过 3 个;目前只在 API 36 上测过,API 28 和 API 19 还没测。

@tencent-adm

tencent-adm commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

CLA assistant check
All committers have signed the CLA.

@jarlen

jarlen commented Oct 12, 2026 via email

Copy link
Copy Markdown

@shifujun

Copy link
Copy Markdown
Collaborator

感谢贡献代码!麻烦先修复一下CI不通过的问题。其实也可以在你fork的仓库跑相同的CI的。目前看是代码格式化的问题。CI里用的android studio版本很低了,不知道是不是跟你用的比较新的ide自动格式化的效果不一致。如果是这样,也可以帮忙升级下CI里的配置。

@shifujun
shifujun requested a balanced review from Copilot October 15, 2026 04:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Manifest generation, container ownership, service lifecycle, and concurrency defects remain unresolved.

13 open findings
What changed in this PR

Adds independent-task Activity allocation and shared foreground-service containers to Shadow’s plugin runtime.

Changes:

  • Adds container pools, Activity leases, and foreground-service lifecycle management.
  • Extends manifest metadata for task affinity, launch modes, permissions, and service types.
  • Adds host configuration, manual scenarios, and five automated tests; updates formatting tooling.
File Description
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​task/​TestIndependentTaskActivity.java Automated task lifecycle fixture.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​task/​IndependentTaskTestActivity.java Manual independent-task scenario.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​service/​ThirdPartyForegroundServiceTestActivity.java Manual multi-service scenarios.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​service/​TestForegroundServiceB.java Second automated service fixture.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​service/​TestForegroundServiceActivity.java Service controls and observable state.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​service/​TestForegroundServiceA.java First automated service fixture.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​service/​MockThirdForegroundService.java Third shared-container mock.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​service/​MockScreenShareService.java Media-projection service mock.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​service/​MockMicrophoneService.java Microphone service mock.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​service/​MockFourthForegroundService.java Fourth shared-container mock.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​java/​com/​tencent/​shadow/​test/​plugin/​general_cases/​lib/​usecases/​service/​BaseTestForegroundService.java Shared notification and lifecycle fixture.
projects/​test/​plugin/​general-cases/​test-plugin-general-cases/​src/​main/​AndroidManifest.xml Registers test components and permissions.
projects/​test/​dynamic/​plugin/​test-dynamic-runtime/​src/​main/​java/​com/​tencent/​shadow/​test/​dynamic/​runtime/​container/​PluginIndependentTask2ProxyActivity.java Second independent-task container.
projects/​test/​dynamic/​plugin/​test-dynamic-runtime/​src/​main/​java/​com/​tencent/​shadow/​test/​dynamic/​runtime/​container/​PluginIndependentTask1ProxyActivity.java First independent-task container.
projects/​test/​dynamic/​plugin/​test-dynamic-loader/​src/​main/​java/​com/​tencent/​shadow/​test/​dynamic/​loader/​TestComponentManager.java Supplies test container pools.
projects/​test/​dynamic/​host/​test-dynamic-host/​src/​main/​AndroidManifest.xml Registers host containers.
projects/​test/​dynamic/​host/​test-dynamic-host/​src/​androidTest/​java/​com/​tencent/​shadow/​test/​cases/​plugin_main/​PluginForegroundServiceTest.java Three service lifecycle tests.
projects/​test/​dynamic/​host/​test-dynamic-host/​src/​androidTest/​java/​com/​tencent/​shadow/​test/​cases/​plugin_main/​IndependentTaskTest.java Two independent-task tests.
projects/​test/​dynamic/​host/​test-dynamic-host/​src/​androidTest/​java/​com/​google/​devtools/​build/​android/​desugar/​runtime/​ThrowableExtension.java Formatting only.
projects/​sdk/​dynamic/​dynamic-loader-impl/​src/​main/​kotlin/​com/​tencent/​shadow/​dynamic/​loader/​impl/​PluginLoaderBinder.kt Formatting only.
projects/​sdk/​dynamic/​dynamic-apk/​src/​main/​java/​com/​tencent/​shadow/​dynamic/​apk/​ApkClassLoader.java Formatting only.
projects/​sdk/​core/​transform/​src/​test/​kotlin/​com/​tencent/​shadow/​core/​transform/​specific/​SimpleRenameTransformTest.kt Formatting only.
projects/​sdk/​core/​transform/​src/​test/​kotlin/​com/​tencent/​shadow/​core/​transform/​specific/​ReceiverSupportTransformTest.kt Formatting only.
projects/​sdk/​core/​runtime/​src/​main/​java/​com/​tencent/​shadow/​core/​runtime/​ShadowService.java Delegates foreground operations and tracks start IDs.
projects/​sdk/​core/​runtime/​src/​main/​java/​com/​tencent/​shadow/​core/​runtime/​ShadowContext.java Routes foreground-service starts.
projects/​sdk/​core/​runtime/​src/​main/​java/​com/​tencent/​shadow/​core/​runtime/​PluginManifest.java Extends component metadata.
projects/​sdk/​core/​runtime/​src/​main/​java/​com/​tencent/​shadow/​core/​runtime/​HostServiceDelegator.java Defines foreground-operation delegation.
projects/​sdk/​core/​manifest-parser/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​manifest_parser/​PluginManifestGenerator.kt Generates new manifest metadata.
projects/​sdk/​core/​manifest-parser/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​manifest_parser/​AndroidManifestReader.kt Reads new attributes and permissions.
projects/​sdk/​core/​manifest-parser/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​manifest_parser/​AndroidManifestKeys.kt Defines new manifest keys.
projects/​sdk/​core/​loader/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​loader/​ShadowPluginLoader.kt Registers service delegates.
projects/​sdk/​core/​loader/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​loader/​managers/​PluginServiceManager.kt Manages shared service lifecycles and notifications.
projects/​sdk/​core/​loader/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​loader/​managers/​ComponentManager.kt Allocates containers and tracks leases and pending stops.
projects/​sdk/​core/​loader/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​loader/​infos/​ContainerActivityInfo.kt Describes Activity container capabilities.
projects/​sdk/​core/​loader/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​loader/​delegates/​ShadowActivityDelegate.kt Checks and releases Activity leases.
projects/​sdk/​core/​gradle-plugin/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​gradle/​ShadowPlugin.kt Formatting only.
projects/​sdk/​core/​gradle-plugin/​src/​main/​kotlin/​com/​tencent/​shadow/​core/​gradle/​extensions/​PackagePluginExtension.kt Formatting only.
projects/​sdk/​core/​common/​src/​main/​java/​com/​tencent/​shadow/​core/​runtime/​container/​PluginContainerServiceDelegateHolder.java Stores delegates by loader key.
projects/​sdk/​core/​common/​src/​main/​java/​com/​tencent/​shadow/​core/​runtime/​container/​PluginContainerServiceDelegate.java Defines container lifecycle callbacks.
projects/​sdk/​core/​common/​src/​main/​java/​com/​tencent/​shadow/​core/​runtime/​container/​PluginContainerService.java Adds the Android service container.
projects/​sdk/​core/​common/​build.gradle Adds build-version metadata dependency.
projects/​sdk/​coding/​code-generator/​src/​main/​kotlin/​com/​tencent/​shadow/​coding/​code_generator/​ActivityCodeGenerator.kt Formatting only.
.github/​workflows/​check-build-test.yml Updates Android Studio formatter download.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +233 to +237
if (releaseLease) {
mComponentManager.releaseContainerActivity(
mPluginActivityComponent,
ComponentName(hostActivity.packageName, hostActivity.javaClass.name),
mActivityLeaseId
)
mAliveServicesMap[componentName] = service
mServiceStartByStartServiceSet.add(componentName)
service.onCreate()
Comment on lines +243 to +245
val launchModeLiteral = makeResIdLiteral(AndroidManifestKeys.launchMode) {
it
}
Comment on lines +132 to 135
if (startId != mLastStartId) {
return false;
}
stopSelf();
stopSelfResult(startId);
return START_NOT_STICKY;
}
mDelegate = PluginContainerServiceDelegateHolder.getDelegate(delegateProviderKey);
Comment on lines +372 to +374
updatePluginForeground(
pluginComponent, container, id, notification, foregroundServiceType
)
Comment on lines +398 to +400
mForegroundServiceRecords[pluginComponent] = ForegroundServiceRecord(
id, notification, foregroundServiceType
)
AndroidManifestKeys.name,
AndroidManifestKeys.theme,
AndroidManifestKeys.appComponentFactory,
AndroidManifestKeys.taskAffinity,
Comment on lines +6 to +7
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<service
android:name="com.tencent.shadow.core.runtime.container.PluginContainerService"
android:exported="false"
android:foregroundServiceType="dataSync" />
Comment thread projects/sdk/core/common/build.gradle Resolved
@shifujun

Copy link
Copy Markdown
Collaborator

我已经算是看懂这个PR的全部内容了。非常高质量的一个PR。有多个自动化测试用例验证了新功能。我重点关注了对旧代码的改动影响,可以看到旧逻辑切换到新逻辑的代码分支很少,如果新代码有bug应该是比较容易回退和定位问题的。对其他不需要这个新feature的用户用户来说影响应该是可控的。

在理解了那个CR中留言的新增依赖的问题后应该就可以合入了。

我会把这些提交rebase成一个提交合入。你也可以自己做一下,写下你觉得更合适的提交记录。另外请仔细检查commit的author设置和github账号一致,以便github能正确统计到你的贡献记录。

再次点赞👍

@shifujun

Copy link
Copy Markdown
Collaborator

忘说了,copilot的CR仅供参考,不用理会。

插件Activity声明非standard的launchMode或非默认taskAffinity时,原有容器映射无法让其
运行在独立任务栈中;插件前台Service也缺少由宿主真实Service承载的通道。

独立任务栈:
- manifest-parser解析并生成插件Activity的launchMode、taskAffinity
- ComponentManager新增getContainerActivityPool()/getContainerActivityProfile(),
  由框架按launchMode、profile和taskAffinity分组分配、复用和回收宿主容器Activity
- 容器在插件Activity首次启动时获得租约,finish销毁后释放;每次分配生成新租约ID,
  拒绝旧任务栈迟到的onNewIntent

前台Service:
- manifest-parser解析插件Service的foregroundServiceType
- 新增宿主侧容器PluginContainerService及委托通道,ComponentManager新增
  getContainerServicePool()/onBindContainerService()
- 多个插件Service可共享同一容器,分别转发生命周期、startForeground、stopForeground
  和停止请求;全部插件Service停止后容器才停止
- 容器onStartCommand回调前收到的stop请求延迟到插件onStartCommand返回后执行,
  避免startForegroundService超时

兼容性:
- 宿主未提供容器池时保持原有行为
- 保留旧版Manifest生成代码所需的兼容构造方法

测试:
- 测试宿主接入独立任务栈容器池和PluginContainerService
- 新增IndependentTaskTest、PluginForegroundServiceTest自动化用例
- 测试插件增加第三方SDK前台Service和独立任务栈的手动验证页面

CI:
- 代码格式化检查使用的Android Studio从2021.1.1升级到2026.1.1 Patch 2,
  新旧版本格式化规则不一致;同时按新版规则重新格式化受影响的已有代码
@dylanzduan
dylanzduan force-pushed the feat/independent-task-foreground-service branch from c64f98e to ef52996 Compare October 15, 2026 07:33
@dylanzduan

Copy link
Copy Markdown
Contributor Author

@shifujun 非常感谢细致的 review 和认可!

已按建议处理:

  1. 新增依赖:core/common/build.gradle 已回滚,与 master 保持一致。这个依赖原本用于 PluginContainerService 校验 Loader 版本,但它位于宿主侧,而 Loader 可以动态下发,两者版本可能不一致,会误拒正常的启动。因此去掉了版本校验,只保留 PROCESS_ID 校验,用来拦截进程重启后残留的 Intent。
  2. 合并提交:已经 squash 成一个提交,基于当前 master,提交信息按改动内容分段写了说明。
  3. Author:提交的 author 是 dylanzduan dylanzduan@tencent.com,这个邮箱已绑定到我的 GitHub 账号@dylanzduan。

如果还有需要调整的地方,随时告诉我。再次感谢!

@dylanzduan
dylanzduan requested a review from shifujun October 16, 2026 08:52

@shifujun shifujun left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

目前看不出什么问题了。我设了个周一上午的提醒会直接合入。留两天时间以防想起什么问题。

@shifujun
shifujun merged commit b2a7a74 into Tencent:master Oct 19, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants