完整的Android支持OSGi包

本主题将介绍如何将OSGI框架转换为在android上运行。 然后,它提供了将android包转换为OSGI包的能力,可以调用android API。

在目前阶段,这些Android OSGI捆绑软件无法做的唯一事情就是操纵活动并使用资源和资产。 我一直在努力解决这个限制。 我希望在这个问题上有好消息。

我发现在Eclipse中使用Create Plugin项目工具比在OSGI包中转换标准的android包更加困难,所以我不会多说这个。

您可以在本邮件末尾的“日志”部分中logging我的成就。

我指的是Knopflerfish项目,因为它是我工作的基础。 这些修改是为了在Knopflerfish OSGi android项目上执行,但实际上适用于其他OSGI框架。 不需要修改OSGi框架本身,我们只会更新Knopflerfish发行版tool目录下的项目KfServiceLibKfBasicApp

添加基本​​的Android支持捆绑

function和限制

这是android框架的第一级定制。 这些更改将与上下文或调用线程无关,但它们允许使用像android.util.Log这样有限的一组android API类。

由于这些变化,bundle将能够在他们的原型和实现中使用android类。 尽pipe如此,由于缺less强制性参考,它们将无法与graphics用户界面,内容提供者和系统服务等相关。

Knopflerfish应用程序中的更改

就像它们一样,tools / android / apk下的应用程序能够在android上执行OSGi框架,但前提是bundle只调用java类。 对于作为Knopflerfish框架一部分的捆绑包,情况就是这样,但是想要调用android API的自定义捆绑包是什么? 以下是在框架中进行的更改,以启用parsingandroid类的bundle。

首先,android包必须是框架包的一部分,以便解决。 这是OSGi属性org.osgi.framework.system.packages.extra的目的

在创build框架之前将属性设置为要导出的android包列表,然后进行设置。 注意野生字符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); 

备注 :如果可以的话,最好使用文件而不是程序中的代码来设置额外的configuration。

捆绑导入android包

即使android包被添加到由框架声明的系统包中,一个包仍然需要导入它们来parsing,就像任何其他导入的包一样。

例:

导入包:org.osgi.framework,android.content,android.widget,android.util

备注 :您可以使用Knopflerfish Eclipse插件的“auto”button自动更新导入。

将上下文传递给捆绑

Knopflerfish应用程序中的更多更改

在这些更改之后,您应该能够运行捆绑开始自己的活动或访问上下文的资源。 整套android API类应该完全可用于捆绑。 但是有一些约束条件适用于捆绑编码来实现这一点。 我们所需要的只是应用程序上下文的引用,所以我们将在框架中将其推上去!

要添加到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从传递给它的激活器的BundleContext获取Context

 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线程上“推送”时才能执行。 为此,devise一个可重用的实用程序方法是明智的:

 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(); } }); } 

廉价的方法来使用捆绑应用程序

我将把APK转换成OSGI捆绑简称为捆绑APK

  • 例如,创build一个常规的APK,感谢Eclipse的Android项目
  • 添加一个引用库条目到您的OSGi框架的项目构buildpath (在我的情况framework.jar)
  • 编辑描述该包的包清单文件bundle.manifest (请参阅下面的示例)。 这个文件并不是真正的APK的一部分,但会在自定义构build步骤中使用
  • 假设你的应用程序包是com.acme.helloworld (这个值在AndroidManifest.xml中用manifest:package设置),你的OSGI bundle的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文件)

要从捆绑软件APK中启动活动,请使用以下代码。

 // 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捆绑:

  • 可以调用android SDK类,只要在AndroidManifest.xml中不需要资源或声明,
  • 有权访问应用程序的android.content.Context ,它可以用来启动OSGi框架之外的活动。

软件包不能:

  • 请求android权限,
  • 从布局build设活动甚至不启动它们,
  • 定义在AndroidManifest.xml声明的静态广播接收器,虽然由代码实例化的接收器应该没问题。

这就是我迄今为止所经历的局限性,我正在努力克服和求助的目标。

我的目标是:

  • 支持内部资源,特别是布局,
  • 能够通过代码创build和启动内部活动,如果不能通过XML生成器。

到目前为止,我的成就是什么?

  • 通过混合构build器,导出未签名的二进制文件以及在使用jarsigner签名之前手动合并bundle.manifest到MANIFEST.MF中,将一个Android项目看作是一个OSGi包和一个APK / android库。 结果是APK被OSGi框架加载,并且由于我的激活器类上的java.lang.NoClassDefFoundError而起了解决状态,但无法启动,即使这个类是classes.dex的一部分,并且没有在path上的明显错误。 使项目成为一个OSGi捆绑与Android依赖访问激活,但不是在JAR的Android资源。 令人费解。
  • validation本指南的“可以”部分所述的所有function。

编辑2013-09-03

我已经find了一个方法来启动Android捆绑拥有的活动。 见相应的章节。

编辑2013-09-10:通用OSGI框架容器

几天后,我已经使Knopflerfish程序通用运行任何我想要的OSGi框架。 例如,我现在正在以同样的方式运行Knopflerfish或Felix。 仍然需要一个特定的框架configuration。

这意味着这个话题不再只是Knopflerfish,即使所需的程序是由Knopflerfish发行的。

2013-09-27:状态和框架总体比较

由于优先级的改变,我必须搁置这个项目一段时间。 不过我到目前为止评估了以下解决scheme:

  • Knopflerfish OSGi [开源],
  • 费利克斯和FelixDroid [开源],
  • ProSyst mBS SDK(基于Equinox的商业用途)

总而言之,在GUI支持方面没有一个优点:它们都不能以android方式处理资源或资源(string,布局,图像),但是您仍然可以将它们作为OSGi资源处理OSGi的API,但你不能像往常一样在android中使用它们。

我个人喜欢Knopflerfish的pipe理控制台servlet,但是它的GUI支持不算什么。 Felix + FelixDroid在免费的OSGi解决scheme方面具有良好的平衡性,而mBS SDK支持大量不同的VM目标,并定义了一个基于意图的应用程序框架,可能适合专业开发人员的品味。

鉴于Knopflerfish和Felix的使用方式几乎相同,mBS SDK在很多方面都有很大的不同。 Knopflerfish和Felix是可交换的:我已经写了一个容器程序,在那里selectOSGi框架只是select一个不同的手工JAR依赖的问题!

当谈到GUI时,Knopflerfish一无所获。 你需要通过我的指导方针,以获得更多的支持。 FelixDroid中的主要思想是好的,它实际上是在mBS SDK中实现的类似的东西,但是没有将实现作为一个包来实现是有点浪费的。 更重要的是,通过定义由特定意图启动的OSGi应用程序框架,mBS SDK已经做得更好。 两者都以同样的方式在主要活动中整合观点。

在mBS SDK中另一个惊人的区别是你不需要添加android框架依赖关系,也不需要为你的bundle添加Import-Package指令。 在依靠Knopflerfish或Felix一段时间后,这当然令人不安。 此外,它完全集成在Eclipse中,为开发人员提供许多便利的任务:PC到目标OSGi框架监视(Kf和Felix仅提供目标pipe理控制台)和快速部署。 这些坑基本上不是免费的,容器应用几乎不可能定制。

我已经find了一些有希望的开放源代码(Apache License 2)框架。 这就是所谓的DEMUX框架 。 随意评估这个解决scheme。 我没有自己,但浏览function和源代码让我觉得它有一个很好的潜力和整洁的整合。 关于GUI支持,它使用类似于FelixDroid的方法。 它可能成为Prosyst mBS SDK的开源替代品。

这是它的devise者如何定义框架:

DEMUX Framework使Java开发人员可以轻松地从单个代码库构build桌面,Web和移动设备的应用程序。 它提供了基于OSGI的模块化应用程序体系结构,可以轻松构build强大的可扩展应用程序。

不过,Android是现阶段唯一支持的移动操作系统,我希望他能为其他两个人带来好运(我在这方面曾经有过一些痛苦的经历)