在应用程序内使用外部应用程序片段/活动
是否可以使用外部应用程序中的片段/活动并按嵌入方式使用?
例如:嵌入 PDF 阅读器应用程序中的 PDF 阅读器片段。
Is it possible to use a fragment/activity from an external application and use as it is embedded?
For example: embed a PDF reader fragment from a PDF reader application.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
可能有点晚了,但仍然觉得可以添加并且可能对其他人有帮助。
对于活动来说,嵌入它确实没有意义,有一种方便的方法可以使用其他应用程序活动 - 有意识地启动它。对于片段来说,如果在应用程序内实现某种“插件”功能,则可能有意义。
Android 博客“Dalvik 中的自定义类加载”。请注意,Android 与其他平台/环境没有太大区别,因此两个部分(您的应用程序和您想要加载到应用程序中的片段)都应该支持某种合同。这意味着您无法从任何应用程序加载任何组件,这很常见,并且有很多原因导致这种情况。
所以,这里有一些实现的小例子。它由 3 部分组成:
接口项目 - 该项目包含应由主应用程序加载的接口定义,以便使用外部类:
对于这个示例,我们只需要单个接口来演示如何加载片段。
插件应用程序,其中包含您需要加载的代码 - 在我们的例子中它是一个片段。请注意,IDE 中的该项目应依赖于使用“提供”类型且不导出的接口一,因为它将由主应用程序导入。
Fragment,我们将加载PlugInFragment:
它的布局fragment_layout.xml:
想要从另一个应用程序加载片段的主应用程序。它应该导入接口项目:
活动本身MyActivity:
它的布局main.xml:
最后我们能够在真实设备上观察到以下内容:
可能的问题处理(感谢@MikeMiller的更新) :
classLoader.loadClass
的调用:java.lang.IllegalAccessError:预先验证的类中的类引用解析为意外实现
确保片段模块包含在主应用程序中( as 'compiled')
如果在调用
context.getPackageManager().getApplicationInfo(packageName,0).sourceDir
时收到NameNotFoundException
,则确保片段位于已安装的应用程序中(而不仅仅是库依赖项)。请按照以下步骤确保情况确实如此:1) 在主应用程序的 build.gradle 中,将
applyplugin: 'android-library'
更改为applyplugin: 'android'
并确保有一个虚拟 Activity java 文件。在主应用程序中,删除对fragment模块的依赖(在步骤3中没有指定,但我必须将对fragment模块的依赖添加到主应用程序中。但是fragment模块现在是一个活动应用程序,你可以'没有依赖项),否则您将得到以下信息:错误:项目上未指定的依赖项解析为不支持作为编译依赖项的 APK 存档。
2) 运行片段模块(您现在可以执行此操作,因为它是一个活动应用程序)。以 getApplicationInfo 调用可以找到它的方式安装它 恢复 build.gradle 并将依赖项添加回主应用程序中(作为“编译”依赖项) 现在一切都应该可以工作了。当您更新片段代码时,您无需再次执行此过程。不过,如果您想在新设备上运行或者添加新的片段模块,您就会这样做。我希望这能够节省我尝试解决上述错误所花费的时间。
Android L
似乎基于正常 Android L 支持 multidex,不需要上述步骤,因为类加载不同。可以使用 multidex 支持中描述的方法代替 Android 博客“Dalvik 中的自定义类加载”,因为它明确指出:
可能需要更改
android.support.multidex
才能重用该方法。May be a little bit late, but still feel that it can be added and might help others.
For activity, there's really no point to have it embedded, there's convenient way to use other apps activities - start it with intent. For fragments at might make sense in case of implementation some kind of 'plug-in' functionality inside the app.
There's an official way to use code from other applications (or load code from network) in Android Blog 'Custom Class Loading in Dalvik'. Please note, the android is not much different from other platforms/environments, so both parts (your app and fragment You want load into your app) should support some kind of contract. That means You cannot load any component from any application, which is quite common and there are number of reasons for it to be that way.
So, here's some small example of the implementation. It consists of 3 parts:
Interfaces project - this project contains definitions of interfaces which should be loaded by main app in order to use external classes:
For this example we need only single interface just to demonstrate how to load the fragment.
Plug-in application, which contains the code You need to load - in our case it's a fragment. Please note, that this project in your IDE should depend on Interface one using 'provided' type and without exporting, because it will be imported by main application.
Fragment, we're going to load PlugInFragment:
And it's layout fragment_layout.xml:
Main application which wants to load the fragment from another application. It should have Interface project imported:
Activity itself MyActivity:
And it's layout main.xml:
And the finally we able to observe the following on the real device:
Possible issues handling (thanks to @MikeMiller for the update):
classLoader.loadClass
:java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
Make sure the fragment modules are included in the main app (as 'compiled')
If you get a
NameNotFoundException
in the call tocontext.getPackageManager().getApplicationInfo(packageName,0).sourceDir
, then make sure the fragment is in an installed APPLICATION (not just a library dependency). Follow the steps below to make sure that's the case:1) In the main application's build.gradle, change
apply plugin: 'android-library'
toapply plugin: 'android'
and make sure there's a dummy activity java file. In the main application, remove the dependency on the fragment module (It's not specified in step 3, but I had to add a dependency on the fragment module to the main application. But the fragment module is now an activity application, and you can't have dependencies on those) or you'll get this:Error:Dependency unspecified on project resolves to an APK archive which is not supported as a compilation dependency.
2) Run the fragment module (which you can do now, because it's an activity application). That installs it in a way that the getApplicationInfo call can find it Revert build.gradle and add the dependency back in the main app (as a 'compile' dependency) Everything should work now. When you make updates to the fragment code, you won't need to go through this process again. You will, though, if you want to run on a new device or if you add a new fragment module. I hope this is able to save someone the time I spent trying to resolve the above errors.
Android L
Seems, based on normal multidex support with Android L, above steps are not needed, because class loading is different. Approach, described in multidex support can be used instead of Android Blog 'Custom Class Loading in Dalvik', because it clearly states that:
Probably, changes in
android.support.multidex
might be needed to reuse that approach.不,您不能“重用”其他应用程序的代码。唯一官方的方法是使用
Intent
来调用整个Activity。No, you can not "reuse" code from other applications. The only official way is to use
Intent
to invoke the whole Activity.我使用与 sandrstart 非常相似的方法。也许它不太安全。
我使用从使用插件包名称创建的包上下文派生的普通类加载器。所有插件的包名称都会与配置网站的其他配置一起加载和保存。
但我想强调,我的方法和我认为 sandrstar 的方法仅适用于 android 内部 android.app.Fragment 类。
我正在尝试(再次)作为采用 android 9 从 android 切换的一部分.app.Fragment(已弃用)到 android.support.v4.Fragment 但无法让它工作。
原因是:
apk1:framework.class.A == apk2.framework.class.A 但是
apk1:someJarOrAar.class.B != aps2.someJarOrAar.class.B
即使两个项目中使用了完全相同的 jar/aar。
因此,我总是会在
(Fragment)c.newInstance();
上收到 ClassCastException。我尝试通过插件中的类加载器进行转换,并通过主包中的类加载器进行转换,但无济于事。我认为没有办法解决这个问题,因为不能保证 jars/aars 确实相同,即使它们具有相同的名称和相同的类名,因此即使它们相同,将它们视为不同也是一个安全问题。
我当然希望有一些解决方法(除了像我现在一样在 android 9 下继续使用 android.app.Fragment 之外),但我也将感谢您的评论,解释为什么共享类绝对不可能(例如支持.v4.Fragment)。
I use a quite similar approach to sandrstart. Maybe it's less secure.
I use a normal Classloader derived from a packagecontext created with the plugin package name. The package names of all plugins are loaded and saved along with other configurations from a config website.
But I wanted to stress that my approach and I think sandrstar's approach only work with the android internal android.app.Fragment class.
I was trying (again) as part for adopting to android 9 to switch from android.app.Fragment (depricated) to android.support.v4.Fragment but could'nt get it to work.
The reason is that:
apk1:framework.class.A == apk2.framework.class.A but
apk1:someJarOrAar.class.B != aps2.someJarOrAar.class.B
even if the same exact jar/aar is used in both projects.
So I will always get a ClassCastException on
(Fragment)c.newInstance();
.I have tried to cast via the classloader from the plugin and cast via classloader from main package but to no avail. I think there is no way around it as it can't be guaranteed that jars/aars are really the same even if they have same names and same classnames in them so it's a security issue to treat them as different even if they are the same.
I surely hope for some workaround (other than keep on using the android.app.Fragment even under android 9 as I will do for now) but I will also be grateful for comments for why it is definetly not possible with shared classes (such as support.v4.Fragment).