动态特性模块第三方库无法访问资源
Third party library of dynamic feature module cannot access resources
我有一个具有动态功能模块的应用程序。在动态功能模块中,有一个带有图像、输入字段的表单,还有一个访问另一个第三方库的按钮。
第三方库有一个activity和片段。在 activity 中打开片段时,我收到以下错误,尽管 activity 的布局中有容器:
No view found for id 0x7f080053 (com.app.sample:id/container) for fragment SampleFragment{eed53f7 (5e4c0693-09a2-4725-a6de-1df49dd818f0) id=0x7f080053}
访问此第三方库中的可绘制对象时,出现以下错误:
java.lang.NoSuchFieldError: No static field ic_back of type I in class Lcom.third.library/R$drawable; or its superclasses (declaration of 'com.third.library.R$drawable' appears in /data/app/com.app.sample-QtC8XuamC1fHEVU4FUpWaA==/split_thirdparty.apk)
当我在没有动态特性模块的应用程序中使用这个库时没有问题。
Dynamic Delivery 是一项相对较新的功能,因此有很多限制。这些限制之一是您无法以常规方式访问动态模块的代码和资源,因此它不能成为其他模块的依赖项。目前,您可以通过反射访问动态模块,并通过公共库模块中的 public 接口定义动态功能,并在运行时使用 ServiceLoader
. It has its performance downsides. They can be minimized with R8 using ServiceLoaderRewriter
但不完全加载它们的实际实现(位于动态功能模块中)已删除。
虽然使用反射很容易出错,但我们可以使用 @AutoService
将其最小化 — AutoService
是一个注释处理器,它将扫描项目以查找 class 用 [=14 注释的=],对于任何 class 它发现它会自动为它生成一个服务定义文件。
这是如何完成的小例子
// All feature definitions extend this interface, T is the dependencies that the feature requires
interface Feature<T> {
fun getMainScreen(): Fragment
fun getLaunchIntent(context: Context): Intent
fun inject(dependencies: T)
}
interface VideoFeature : Feature<VideoFeature.Dependencies> {
interface Dependencies {
val okHttpClient: OkHttpClient
val context: Context
val handler: Handler
val backgroundDispatcher: CoroutineDispatcher
}
}
internal var videoComponent: VideoComponent? = null
private set
@AutoService(VideoFeature::class)
class VideoFeatureImpl : VideoFeature {
override fun getLaunchIntent(context: Context): Intent = Intent(context, VideoActivity::class.java)
override fun getMainScreen(): Fragment = createVideoFragment()
override fun inject(dependencies: VideoFeature.Dependencies) {
if (videoComponent != null) {
return
}
videoComponent = DaggerVideoComponent.factory()
.create(dependencies, this)
}
}
并实际访问动态功能使用的代码
inline fun <reified T : Feature<D>, D> FeatureManager.getFeature(
dependencies: D
): T? {
return if (isFeatureInstalled<T>()) {
val serviceIterator = ServiceLoader.load(
T::class.java,
T::class.java.classLoader
).iterator()
if (serviceIterator.hasNext()) {
val feature = serviceIterator.next()
feature.apply { inject(dependencies) }
} else {
null
}
} else {
null
}
}
摘自 here。那里还有更多信息,所以我建议您检查一下。
一般来说,我只是不建议使用动态功能作为依赖项并相应地规划您的应用架构。
希望对您有所帮助。
一般来说,当SplitCompat.installActivity(this)
没有在Activity2
中被调用时,这将不起作用。虽然没有源代码,但您必须提取包并正确重新打包,因为 Activity2
(甚至整个库包)可能与 DFM 不兼容。
After you enable SplitCompat for your base app, you need to enable SplitCompat for each activity that your app downloads in a dynamic feature module.
这是我的另一个 ,演示了通过反射访问。
对于资源,这部分代码可以使用
R.id.settings
将是:
getResources().getIdentifier("settings", "id", "com.library.package");
我有一个具有动态功能模块的应用程序。在动态功能模块中,有一个带有图像、输入字段的表单,还有一个访问另一个第三方库的按钮。
第三方库有一个activity和片段。在 activity 中打开片段时,我收到以下错误,尽管 activity 的布局中有容器:
No view found for id 0x7f080053 (com.app.sample:id/container) for fragment SampleFragment{eed53f7 (5e4c0693-09a2-4725-a6de-1df49dd818f0) id=0x7f080053}
访问此第三方库中的可绘制对象时,出现以下错误:
java.lang.NoSuchFieldError: No static field ic_back of type I in class Lcom.third.library/R$drawable; or its superclasses (declaration of 'com.third.library.R$drawable' appears in /data/app/com.app.sample-QtC8XuamC1fHEVU4FUpWaA==/split_thirdparty.apk)
当我在没有动态特性模块的应用程序中使用这个库时没有问题。
Dynamic Delivery 是一项相对较新的功能,因此有很多限制。这些限制之一是您无法以常规方式访问动态模块的代码和资源,因此它不能成为其他模块的依赖项。目前,您可以通过反射访问动态模块,并通过公共库模块中的 public 接口定义动态功能,并在运行时使用 ServiceLoader
. It has its performance downsides. They can be minimized with R8 using ServiceLoaderRewriter
但不完全加载它们的实际实现(位于动态功能模块中)已删除。
虽然使用反射很容易出错,但我们可以使用 @AutoService
将其最小化 — AutoService
是一个注释处理器,它将扫描项目以查找 class 用 [=14 注释的=],对于任何 class 它发现它会自动为它生成一个服务定义文件。
这是如何完成的小例子
// All feature definitions extend this interface, T is the dependencies that the feature requires
interface Feature<T> {
fun getMainScreen(): Fragment
fun getLaunchIntent(context: Context): Intent
fun inject(dependencies: T)
}
interface VideoFeature : Feature<VideoFeature.Dependencies> {
interface Dependencies {
val okHttpClient: OkHttpClient
val context: Context
val handler: Handler
val backgroundDispatcher: CoroutineDispatcher
}
}
internal var videoComponent: VideoComponent? = null
private set
@AutoService(VideoFeature::class)
class VideoFeatureImpl : VideoFeature {
override fun getLaunchIntent(context: Context): Intent = Intent(context, VideoActivity::class.java)
override fun getMainScreen(): Fragment = createVideoFragment()
override fun inject(dependencies: VideoFeature.Dependencies) {
if (videoComponent != null) {
return
}
videoComponent = DaggerVideoComponent.factory()
.create(dependencies, this)
}
}
并实际访问动态功能使用的代码
inline fun <reified T : Feature<D>, D> FeatureManager.getFeature(
dependencies: D
): T? {
return if (isFeatureInstalled<T>()) {
val serviceIterator = ServiceLoader.load(
T::class.java,
T::class.java.classLoader
).iterator()
if (serviceIterator.hasNext()) {
val feature = serviceIterator.next()
feature.apply { inject(dependencies) }
} else {
null
}
} else {
null
}
}
摘自 here。那里还有更多信息,所以我建议您检查一下。
一般来说,我只是不建议使用动态功能作为依赖项并相应地规划您的应用架构。
希望对您有所帮助。
一般来说,当SplitCompat.installActivity(this)
没有在Activity2
中被调用时,这将不起作用。虽然没有源代码,但您必须提取包并正确重新打包,因为 Activity2
(甚至整个库包)可能与 DFM 不兼容。
After you enable SplitCompat for your base app, you need to enable SplitCompat for each activity that your app downloads in a dynamic feature module.
这是我的另一个
对于资源,这部分代码可以使用
R.id.settings
将是:
getResources().getIdentifier("settings", "id", "com.library.package");