Closed. This question needs
details or clarity。它当前不接受答案。
想要改善这个问题吗?添加详细信息,并通过
editing this post来解决问题。
3年前关闭。
Improve this question
本主题说明了如何转换OSGI框架以使其在android上运行。
然后,它提供了一些技巧,可以将android程序包转换为能够调用android API的OSGI包。
在当前阶段,这些Android OSGI bundle 包唯一不能做的就是操纵 Activity 并使用资源和 Assets 。
我一直在努力解决此限制。我希望在这个问题上有个好消息。
我发现在Eclipse中使用“创建插件”项目工具比将标准的android软件包转换为OSGI包要困难得多,因此我将不做太多讨论。
您可以在此消息末尾的“日志”部分中跟踪我的成就。
我指的是
Knopflerfish项目,因为它是我工作的基础。修改是要在
Knopflerfish OSGi android projects上执行的,但实际上适用于其他OSGI框架。无需修改OSGi框架本身,我们只更新Knopflerfish发行版的
KfServiceLib
目录中的项目
KfBasicApp
和
tool
。
将基本的android支持添加到包中
功能和限制
这是android框架的第一级定制。这些更改与上下文或调用线程无关,但它们允许使用一组有限的android API类,例如
android.util.Log
。
由于这些更改, bundle 包将能够在其原型(prototype)和实现中使用android类。但是,它们将无法与图形用户界面,内容提供者和系统服务等相关,因为它们缺少必需的引用。
Knopflerfish应用程序中的更改
照原样,在tools/android/apk下的应用程序能够在android上执行OSGi框架,但前提是这些包仅调用java类。属于Knopflerfish框架的 bundle 包就是这种情况,但是想要调用android API的自定义 bundle 包是什么?这是在框架中进行的更改,以使 bundle 包能够解析android类。
首先,android程序包必须是框架程序包的一部分,以便可以解决。这是OSGi属性
org.osgi.framework.system.packages.extra
的目的
在创建框架之前,将属性设置为要导出的android程序包列表即可。请注意,野生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);
注意:如果可以的话,最好使用文件而不是程序中的代码来设置额外的配置。
导入 bundle 包中的android软件包
即使将android程序包添加到框架声明的系统程序包中, bundle 包仍必须像其他导入的程序包一样将其导入以解决。
例子:
Import-Package: org.osgi.framework, android.content, android.widget, android.util
备注:您可以使用Knopflerfish Eclipse插件的“自动”按钮来自动更新导入内容。
将上下文传递给 bundle 包
Knopflerfish应用程序中的更多更改
进行这些更改之后,您应该能够自行运行 bundle 包或启动 Activity 或访问上下文的资源。然后,整套android API类应该对 bundle 包完全可用。但是,为了实现此目的,在 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 软件可以对此上下文保持强大的引用,这很好。
bundle 包如何使用上下文
bundle 包从传递给其激活器的
Context
中获取
BundleContext
:
static Context context;
public void start(BundleContext bc) throws Exception {
ServiceReference<Context> ref = bc.getServiceReference(Context.class);
context = bc.getService(ref);
}
由于 bundle 软件在与UI线程不同的线程中运行,因此只有在“推” UI线程时才能执行UI操作。为此,明智的做法是设计一个可重用的实用程序方法,如下所示:
public static void runOnContext(Context context, Runnable runnable) {
Handler handler = new Handler(context.getMainLooper());
handler.post(runnable);
}
该方法应该是实用程序 bundle 包中的服务的一部分,因为许多不同的Android bundle 包都应以相同的方式访问此方法。
例如,此 bundle 包开始时显示“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();
}
});
}
使用 bundle 软件之类的应用程序的廉价方法
我将简称为包APK的APK转换为OSGI包。
通过Eclipse Android Project创建一个常规APK,例如
将引用库条目添加到OSGi框架的项目“构建路径”(在我的情况下为framework.jar)
编辑描述 list 的 bundle list 文件bundle.manifest
(请参见下面的示例)。该文件实际上不是APK的一部分,但将在自定义生成步骤中使用
假设您的应用程序包是com.acme.helloworld
(此值在AndroidManifest.xml中用manifest:package设置),您的OSGI bundle 包的Activator类必须放置在包com.acme.helloworld
中,而务必设置 bundle 包 list 中的Bundle-SymbolicName: com.acme.helloworld
。如果不满足这些条件中的任何一个,则将在运行时生成java.lang.NoClassDefFoundError
。
提醒一下,您的 bundle list 文件应如下所示:
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包已准备就绪
像往常一样安装 bundle 包APK。需要安装才能解决 Activity 。没有这个 Activity 将无法解决,并且 bundle 包将失败
加载OSGi框架并启动 bundle 包APK(完全相同的APK文件)
要从 bundle 包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 bundle 了:
可以调用android SDK类,只要它们不需要资源即可
或AndroidManifest.xml中的声明
有权访问应用程序的android.content.Context
,可用于启动OSGi框架之外的 Activity 。
bundle 包不能:
请求android权限
从布局开始构建 Activity ,甚至根本不启动 Activity
定义了静态广播接收器,以AndroidManifest.xml
声明,尽管用代码实例化的接收器应该可以。
这是我迄今为止所遇到的局限性,是我要克服的问题以及我寻求帮助的目标。
我的目标是:
支持内部资源,尤其是布局
能够通过代码创建和启动内部 Activity ,如果XML构建器不可能的话,可以通过代码进行创建和启动。
通过试验,到目前为止,我取得了哪些成就:
通过混合使用Builders,导出未签名的二进制文件并手动将bundle.manifest
合并到MANIFEST.MF中,然后再使用jarsigner
进行签名,从而使一个既可视为OSGi bundle 包又视为APK/android库的android项目。结果是APK由OSGi框架加载,并达到已解决的状态,但由于我的激活器类上的java.lang.NoClassDefFoundError
而无法启动,即使该类是classes.dex的一部分,并且在该类上也没有明显的错误小路。使项目成为具有android依赖项的OSGi bundle 包可以访问激活器,但不能访问JAR中的android资源。令人费解。
验证本指南“可以做”部分中描述的所有功能。
编辑2013-09-03
我找到了一种方法来启动android bundle 包所拥有的 Activity 。参见相应章节。
编辑2013-09-10:通用OSGI框架容器
几天后,我使Knopflerfish程序通用,可以运行我想要的任何OSGi框架。例如,我目前正在以相同的方式运行Knopflerfish或Felix。仍然需要特定的框架配置。
这意味着该主题不再仅仅是Knopflerfish,即使所需的程序是由Knopflerfish发布的。
2013-09-27:状态和框架总体比较
由于优先级的变化,我不得不将该项目搁置一段时间。但是,到目前为止,我评估了以下解决方案:
Knopflerfish OSGi [开源],
Felix和FelixDroid [开源],
ProSyst mBS SDK(基于Equinox的商业用途)
综上所述,它们在GUI支持方面没有一个整齐的优势:它们都无法以android的方式处理 Assets 或资源(字符串,布局,图像),但是您仍然可以使用OSGi资源将其作为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中实现的类似东西,但是如果没有将实现 bundle 在一起,则有点浪费。可以说,mBS SDK通过定义以特定意图开始的OSGi应用程序框架,使事情做得更好。两者都以相同的方式将 View 整合到主要 Activity 中。
mBS SDK中的另一个惊人的区别是,您无需在 bundle 包中添加android框架依赖项,也无需为其添加Import-Package指令。依靠Knopflerfish或Felix一段时间后,肯定会令人不安。它还完全集成在Eclipse中,并为开发人员提供了许多方便的任务:PC到目标OSGi框架监视(Kf和Felix仅提供目标管理控制台)和快速部署。这些坑本质上不是免费的,而且容器应用程序几乎无法自定义。
我是一名优秀的程序员,十分优秀!