对 OSGi 捆绑包的完整 Android 支持 [关闭]
Posted
技术标签:
【中文标题】对 OSGi 捆绑包的完整 Android 支持 [关闭]【英文标题】:Full Android support for OSGi bundles [closed] 【发布时间】:2013-09-06 10:14:28 【问题描述】:本主题说明如何转换 OSGI 框架以在 android 上运行。 然后给出了将 android 包转换为能够调用 android API 的 OSGI 包的提示。
在当前阶段,这些 Android OSGI 包唯一不能做的就是操纵活动和使用资源和资产。 我一直在努力解决这个限制。我希望有关于这个主题的好消息。
我发现在 Eclipse 中使用 Create Plugin 项目工具比将标准 android 包转换为 OSGI 包更难,所以我不会多说。
您可以在此消息末尾的“日志”部分中跟踪我的成就。
我指的是Knopflerfish 项目,因为它是我工作的基础。这些修改是在Knopflerfish OSGi android projects 上执行的,但实际上适用于其他 OSGI 框架。无需修改 OSGi 框架本身,我们只更新 Knopflerfish 发行版的tool
目录中的KfServiceLib
和KfBasicApp
项目。
为 bundle 添加基本的 android 支持
特性和限制
这是android框架的第一级定制。这些更改与上下文或调用线程无关,但它们允许使用有限的一组 android API 类,例如android.util.Log
。
由于这些更改,bundle 将能够在其原型和实现中使用 android 类。然而,他们将无法处理与图形用户界面、内容提供者和系统服务等相关的事情,因为他们缺乏对其的强制引用。
Knopflerfish 应用的变化
事实上,tools/android/apk 下的应用程序能够在 android 上执行 OSGi 框架,但前提是 bundle 只调用 java 类。作为 Knopflerfish 框架一部分的包就是这种情况,但是想要调用 android API 的自定义包呢?下面是在框架中进行的更改,以使捆绑包能够解析 android 类。
首先,android 包必须是框架包的一部分,这样它们才能被解析。这就是 OSGi 属性 org.osgi.framework.system.packages.extra
在创建框架之前将属性设置为要导出的 android 包列表,然后您就设置好了。请注意,wild char android.*
似乎没有任何作用:我们必须像下面这样一个一个地告诉每个包。
添加到文件 src/org/knopflerfish/android/service/KfApk.java 中的KfServiceLib
static final String ANDROID_FRAMEWORK_PACKAGES = (
"android,"
+ "android.app,"
+ "android.content,"
+ "android.database,"
+ "android.database.sqlite,"
+ "android.graphics,"
+ "android.graphics.drawable,"
+ "android.graphics.glutils,"
+ "android.hardware,"
+ "android.location,"
+ "android.media,"
+ "android.net,"
+ "android.net.wifi,"
+ "android.opengl,"
+ "android.os,"
+ "android.provider,"
+ "android.sax,"
+ "android.speech.recognition,"
+ "android.telephony,"
+ "android.telephony.gsm,"
+ "android.text,"
+ "android.text.method,"
+ "android.text.style,"
+ "android.text.util,"
+ "android.util,"
+ "android.view,"
+ "android.view.animation,"
+ "android.webkit,"
+ "android.widget");
然后我们在KfApk.newFramework()
中设置额外的包
config.put(Constants.FRAMEWORK_STORAGE, fwDir);
// Export android packages so they can be referenced by bundles
config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA,
ANDROID_FRAMEWORK_PACKAGES);
备注:如果可以的话,最好用文件来设置额外的配置,而不是通过程序中的代码来设置。
在包中导入 android 包
即使 android 包被添加到框架声明的系统包中,bundle 仍然需要导入它们才能解析,就像任何其他导入的包一样。
例子:
导入包:org.osgi.framework, android.content, android.widget, android.util
备注:您可以使用 Knopflerfish Eclipse 插件的“自动”按钮来自动更新导入内容。
将上下文传递给包
Knopflerfish 应用的更多变化
在这些更改之后,您应该能够运行捆绑包自行启动活动或访问上下文的资源。整套 android API 类应该对 bundle 完全可用。但是有一些限制适用于捆绑编码来实现这一点。我们所需要的只是应用程序上下文的引用,因此我们将把它推送到框架中!
添加到org.knopflerfish.android.service.Knopflerfish.onStartCommand()
if (fw != null)
// Register the application's context as an OSGi service!
BundleContext bundleContext = fw.getBundleContext();
regContext = bundleContext.registerService(Context.class,
getApplicationContext(), new Hashtable());
sendMessage(Msg.STARTED, (Serializable) KfApk.getFrameworkProperties());
else
// framework did not init/start
sendMessage(Msg.NOT_STARTED);
stopSelf();
return;
我们正在传递应用程序的上下文,并且只传递这个,因为它是应用程序整个生命周期中唯一存在的上下文。它将在应用程序启动后立即设置,这意味着在安装或系统启动之后。 bundle 可以在这个 Context 上保持强引用,很好。
bundle 如何使用上下文
一个包从传递给它的激活器的BundleContext
中获取Context
:
static Context context;
public void start(BundleContext bc) throws Exception
ServiceReference<Context> ref = bc.getServiceReference(Context.class);
context = bc.getService(ref);
由于包在与 UI 线程不同的线程中运行,因此 UI 操作只能在 UI 线程上“推送”时执行。为此,明智的做法是设计一个可重用的实用方法,如下所示:
public static void runOnContext(Context context, Runnable runnable)
Handler handler = new Handler(context.getMainLooper());
handler.post(runnable);
此方法应该是实用程序包中服务的一部分,因为它应该由许多不同的 android 包以相同的方式访问。
例如,这个包在开始时显示“Hello”:
public void start(BundleContext bc) throws Exception
ServiceReference<Context> ref = bc.getServiceReference(Context.class);
final Context context = bc.getService(ref);
runOnContext(context, new Runnable()
public void run()
Toast.makeText(context, "Hello", Toast.LENGTH_LONG).show();
);
使用捆绑软件等应用程序的廉价方法
我将把转换为 OSGI bundle 的 APK 简称为 bundle APK。
创建一个常规 APK,这要归功于 Eclipse Android Project 示例 为您的 OSGi 框架(在我的例子中是 framework.jar)添加一个 Reference Library 条目到项目构建路径 编辑描述捆绑包的捆绑清单文件bundle.manifest
(参见下面的示例)。此文件实际上不是 APK 的一部分,但将在自定义构建步骤中使用
假设你的应用程序包是com.acme.helloworld
(这个值是通过AndroidManifest.xml中的manifest:package设置的),你的OSGI包的Activator类必须放在包com.acme.helloworld
中并且你必须在捆绑清单中设置 Bundle-SymbolicName: com.acme.helloworld
。如果不满足这些条件中的任何一个,则会在运行时产生java.lang.NoClassDefFoundError
。
提醒一下,您的捆绑清单文件应如下所示:
Manifest-Version: 1.0
Bundle-Vendor: Acme
Bundle-Version: 1.0.0
Bundle-Name: HelloWorldBundle
Bundle-ManifestVersion: 2
Bundle-Activator: com.acme.helloworld.Activator
Bundle-Description: Hello World Bundle
Import-Package: org.osgi.framework
Bundle-SymbolicName: com.acme.helloworld
Bundle-RequiredExecutionEnvironment: OSGi/Minimum-1.0
使用Android 工具> 导出未签名的Android 包
在生成的未签名APK中复制bundle.manifest
为META-INF/MANIFEST.MF
使用您想要的任何证书对 APK 进行签名。在这里,您已准备好捆绑包 APK
像往常一样安装捆绑包 APK。需要安装才能解决这些活动。没有这个,活动将无法解决,并且捆绑包将失败
让 OSGi 框架加载并启动捆绑 APK(同一个 APK 文件)
要从捆绑包 APK 启动 Activity,请使用以下代码。
// This is the application's context provided by the framework
// Context ctx = ...
Intent intent = new Intent();
String pkgName = YourActivity.class.getPackage().getName();
String clssName = YourActivity.class.getName();
intent.setClassName(pkgName, clssName);
// You may add the NEW_TASK flag
intent.addFlag(Intent.FLAG_ACTIVITY_NEW_TASK);
// Important: do not use startActivity(Context, Class) version because it will fail to resolve the activity
ctx.startActivity(intent);
日志
首字母
在我努力的这一点上,android bundles:
可以调用android SDK类,只要它们不需要资源 或 AndroidManifest.xml 中的声明, 有权访问应用程序的android.content.Context
,可用于在 OSGi 框架之外启动活动。
捆绑包不能:
请求安卓权限, 从布局构建活动甚至根本不启动它们, 定义静态广播接收器,在AndroidManifest.xml
中声明,但通过代码实例化的接收器应该没问题。
这是我迄今为止所经历的限制,我正在努力克服以及我请求帮助的目标。
我的目标:
支持内部资源,尤其是布局, 能够通过代码创建和启动内部活动,如果 XML 构建器不可能的话。到目前为止,我通过试验取得了哪些成就:
通过混合构建器、导出未签名的二进制文件并在使用jarsigner
签名之前手动将bundle.manifest
合并到 MANIFEST.MF 中,将一个 android 项目视为 OSGi 包和 APK/android 库。结果是 APK 由 OSGi 框架加载并达到状态 resolved 但由于我的激活器类上的java.lang.NoClassDefFoundError
而无法启动,即使该类是类的一部分。 dex 并且路径上没有明显的错误。使项目成为具有 android 依赖项的 OSGi 包可以访问激活器,但不能访问 JAR 中的 android 资源。令人费解。
验证本指南“可以做”部分中描述的所有功能。
编辑 2013-09-03
我找到了一种方法来启动 android 捆绑包拥有的活动。见相应章节。
2013-09-10 编辑:通用 OSGI 框架容器
几天后,我使 Knopflerfish 程序通用,可以运行我想要的任何 OSGi 框架。例如,我目前正在以相同的方式运行 Knopflerfish 或 Felix。仍然需要特定的框架配置。
这意味着主题不再仅仅是 Knopflerfish,即使所需的程序是由 Knopflerfish 发布的。
2013-09-27:状态和框架整体比较
由于优先级发生变化,我不得不将这个项目搁置一段时间。但是到目前为止,我评估了以下解决方案:
Knopflerfish OSGi [开源], Felix 和 FelixDroid [开源], ProSyst mBS SDK(基于 Equinox,商业用途)总而言之,它们在 GUI 支持方面都没有明显的优势:它们都不能以 android 方式处理资产或资源(字符串、布局、图像),但您仍然可以将它们作为 OSGi 处理使用 OSGi API 的资源,但您将无法像往常一样在 android 中使用它们。
我个人喜欢 Knopflerfish 的管理控制台 servlet,但它的 GUI 支持却一无所获。 Felix + FelixDroid 为免费的 OSGi 解决方案提供了很好的平衡,而 mBS SDK 支持大量不同的 VM 目标,并定义了一个基于意图的应用程序框架,可能更适合专业开发人员的口味。
Knopflerfish 和 Felix 的使用方式几乎相同,而 mBS SDK 在许多方面却大不相同。 Knopflerfish 和 Felix 是可互换的:我编写了一个容器程序,其中选择 OSGi 框架只是选择不同的手工 JAR 依赖项!
在 GUI 方面,Knopflerfish 几乎一无所获。您需要阅读我的指南才能获得更多支持。 FelixDroid 的主要思想是好的,它实际上是在 mBS SDK 中实现的类似的东西,但是没有将实现作为一个包有点浪费。更重要的是,mBS SDK 通过定义一个由特定意图启动的 OSGi 应用程序框架,做得更好。两者都以相同的方式将视图集成到主要活动中。
mBS SDK 的另一个惊人区别是您不需要添加 android 框架依赖项,也不需要在包中为它们添加 Import-Package 指令。在依赖 Knopflerfish 或 Felix 一段时间后肯定会感到不安。它还完全集成在 Eclipse 中,为开发人员提供了许多方便的任务:PC 到目标 OSGi 框架监控(Kf 和 Felix 仅提供目标管理控制台)和快速部署。坑基本上就是不是免费的,容器应用几乎是不可能定制的。
【问题讨论】:
感谢您的信息。我正在尝试在捆绑包中进行活动并从激活器启动它。我发现你说的有些内容令人困惑。您能否详细说明启动 Bundle 拥有的活动的步骤? “编辑 bundle.manifest”是什么意思?我在android项目中没有找到这个文件。另外,确切的编辑是什么? 您也说过“Activator 类,必须与 AndroidManifest.xml 中定义的包在同一个包中”,但是“AndroidManifext.xml 没有定义这些类的名称。它只描述了活动就我而言。“捆绑的符号名称也必须是包的名称”是什么意思?你能举个例子吗?谢谢。 您永远无法从捆绑包中启动活动。原因是捆绑包从未将其活动注册到 PackageManager,这是在安装常规 APK 时完成的。您也不能在包内使用 LayoutInflater 创建活动布局,因为这里存在有关上下文和访问资源文件的问题。我的诀窍是拥有两倍的 APK: 你能看看这个吗? ***.com/questions/19922775/… 我只需要你给我解释一下这两句话。首先,您所说的“•编辑捆绑清单文件 bundle.manifest”。我在 APK 甚至其他任何地方都没有找到任何名为“bundle.manifest”的文件,那你怎么会说“编辑”呢?!你的意思是我创造一个?如果是这样,那么我应该将其保存在 APK 中吗?第二件事:当您说“•将生成的未签名 APK 中的 bundle.manifest 复制为 META-INF/MANIFEST.MF”时。从哪里复制和粘贴?我是否从未签名的 APK 复制到自身?这是我从你的句子中理解的。请详细说明。 【参考方案1】:我很幸运地发现了一些很有前途的开源(Apache 许可证 2)框架,可能会引起人们的兴趣。它被称为 DEMUX Framework 。随意评估此解决方案。我自己没有,但浏览功能和源代码让我认为它具有很好的潜力和巧妙的集成。关于 GUI 支持,它使用类似于 FelixDroid 的方法。它可以成为 Prosyst mBS SDK 的开源替代品。
这是它的设计者定义框架的方式:
DEMUX 框架使 Java 开发人员可以轻松构建 来自单一代码的桌面、Web 和移动设备应用程序 根据。它提供基于 OSGI 的模块化应用架构, 这使得构建健壮且可扩展的应用程序变得容易。
然而,Android 是现阶段唯一支持的移动操作系统,我祝他在其他两个操作系统上好运(我过去在这个问题上经历过一些痛苦的经历)
【讨论】:
以上是关于对 OSGi 捆绑包的完整 Android 支持 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章