全面支持OSGi捆绑软件

本主题说明如何将OSGI框架转换为在android上运行。 然后它提供了将android包转换为能够调用android API的OSGI包的提示。

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

我发现在Eclipse中使用Create Plugin项目工具比在OSGI包中转换标准的android包要困难得多,所以我不会谈论太多。

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

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

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

功能和限制

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

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

Knopflerfish应用程序中的更改

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

首先,android包必须是框架包的一部分,这样才能解决它们。 这是OSGi属性org.osgi.framework.system.packages.extra的用途

将该属性设置为在创建框架之前要导出的Android包列表,然后进行设置。 请注意,wild char android.*似乎没有任何作用:我们必须像下面一样告诉每个包。

要添加到KfServiceLib文件SRC /组织/ knopflerfish /安卓/服务/ KfApk.java

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插件的“auto”按钮来自动更新导入。

将上下文传递给捆绑包

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

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。

  • 例如,创建一个常规的APK,这要归功于Eclipse Android Project
  • 将参考库条目添加到OSGi框架的项目构建路径中(在我的情况下为framework.jar)
  • 编辑描述该包的包清单文件bundle.manifest (请参见下面的示例)。 这个文件并不是APK的一部分,但会在自定义构建步骤中使用
  • 假设你的应用程序包是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权限,
  • 从布局构建活动甚至根本不启动它们,
  • 定义静态广播接收器,在AndroidManifest.xml声明,尽管通过代码实例化的接收器应该没问题。
  • 这就是我迄今为止所经历的局限性,即我正努力克服的问题以及我寻求帮助的目标。

    我的目标是:

  • 支持内部资源,特别是布局,
  • 能够通过代码创建并开始内部活动,如果XML生成器不可行的话。
  • 到目前为止,我通过试验取得了哪些成就:

  • 通过混合Builders,导出未签名的二进制文件以及在使用jarsigner签名之前手动将bundle.manifest合并到MANIFEST.MF中,将一个Android项目看作OSGi包和APK / android库。 结果是APK由OSGi框架加载,并且由于我的激活器类上的java.lang.NoClassDefFoundError而起了解决状态,但无法启动,即使该类是classes.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中实现的类似的东西,但是如果没有将实现作为一个捆绑包,那将是一种浪费。 更重要的是,通过定义由特定意图启动的OSGi应用程序框架,mBS SDK已经做得更好。 两者都以同样的方式在主要活动中整合观点。

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


    我已经通过纯粹的运气找到了一些有希望的开源(Apache许可证2)框架。 它被称为DEMUX框架。 随时评估这个解决方案。 我没有自己,但浏览功能和源代码让我觉得它有一个很好的潜力和整洁的整合。 关于GUI支持,它使用类似于FelixDroid的方法。 它可能成为Prosyst mBS SDK的开源替代品。

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

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

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

    链接地址: http://www.djcxy.com/p/74735.html

    上一篇: Full Android support for OSGi bundles

    下一篇: start an OSGI bundle