完整的Android支持OSGi包
本主题将介绍如何将OSGI框架转换为在android上运行。 然后,它提供了将android包转换为OSGI包的能力,可以调用android API。
在目前阶段,这些Android OSGI捆绑软件无法做的唯一事情就是操纵活动并使用资源和资产。 我一直在努力解决这个限制。 我希望在这个问题上有好消息。
我发现在Eclipse中使用Create Plugin项目工具比在OSGI包中转换标准的android包更加困难,所以我不会多说这个。
您可以在本邮件末尾的“日志”部分中logging我的成就。
我指的是Knopflerfish项目,因为它是我工作的基础。 这些修改是为了在Knopflerfish OSGi android项目上执行,但实际上适用于其他OSGI框架。 不需要修改OSGi框架本身,我们只会更新Knopflerfish发行版tool
目录下的项目KfServiceLib
和KfBasicApp
。
添加基本的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是现阶段唯一支持的移动操作系统,我希望他能为其他两个人带来好运(我在这方面曾经有过一些痛苦的经历)