Full Android support for OSGi bundles [closed]

2019-01-16 06:10发布

问题:

This topic explains how you can transform an OSGI framework to run on android. Then it gives tips to convert android packages as OSGI bundles capable of calling android API.

At the current stage, the only thing these Android OSGI bundles cannot do is manipulate activities and use resources and assets. I am continuously working at fixing this limitation. I hope to have good news on this subject.

I found more difficult to use the Create Plugin project facility in Eclipse than converting standard android packages as OSGI bundles so I won't talk much about it.

You can keep track of my achievements in the Log section at the end of this message.

I am referring to Knopflerfish projects because it was the basis for my work. The modifications are meant to be performed on Knopflerfish OSGi android projects but are actually applicable to other OSGI frameworks. There is no need for modification of the OSGi framework itself, we'll only update the projects KfServiceLib and KfBasicApp in the tool directory of the Knopflerfish distribution.

Add basic android support to bundles

Features and limits

This is the first level of customization of the framework for android. These changes will yet have nothing to do with the context or calling thread but they allow the use of a limited set of android API classes like android.util.Log.

Thanks to these changes, bundles will be able to use android classes in their prototypes and in their implementation. Nevertheless, they will be capable of nothing related to graphical user interface, content providers, and system services and so on, because they lack the mandatory references for it.

Changes in Knopflerfish apps

As they are, the applications under tools/android/apk are able to execute the OSGi framework on android but only if the bundles are only calling java classes. That’s the case for the bundles that are part of the Knopflerfish framework but what of custom bundles wanting to call android API? Here are the changes to make in the framework to enable the bundles resolving android classes.

First, the android packages must be part of the framework packages so they can be resolved. This is the purpose of the OSGi property org.osgi.framework.system.packages.extra

Set the property to the list of android packages to export before creating the framework and you are set. Take notice that the wild char android.* seems to have no effect: we have to tell each package one by one like below.

To add to KfServiceLib in file src/org/knopflerfish/android/service/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");

Then we are setting the extra packages in 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);

Remark: if you can, it is way better to set the extra configuration with a file rather than by code in the program.

Import android packages in bundles

Even if the android packages were added to the system packages declared by the framework, a bundle still has to import them to resolve, like any other imported package.

Example:

Import-Package: org.osgi.framework, android.content, android.widget, android.util

Remark: you can use the button « auto » of the Knopflerfish Eclipse plugin to automatically update the imports like they should be.

Pass the Context to bundles

More changes in Knopflerfish apps

After these changes, you should be able to run bundles starting activities on their own or accessing context’s resources. The whole set of android API classes should then be fully available to bundles. But there are some constraints applying to bundle coding to achieve this. All we need is the reference on the application’s Context so we are going to push it in the framework!

To add into 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;
}

We are passing the application’s context, and only this one, because it is the only Context that will exist for the entire life of the application. It will be set as soon as the application starts, meaning after install or system boot. The bundles can maintain a strong reference on this Context, it is fine.

How bundles use the context

A bundle gets the Context from the BundleContext passed to its activator:

static Context context;

public void start(BundleContext bc) throws Exception {
  ServiceReference<Context> ref = bc.getServiceReference(Context.class);
  context = bc.getService(ref);
}

As a bundle is running in a different thread than the UI thread, UI operations can only be performed if “pushed” on the UI thread. To do so, it is wise to design a reusable utility method like this:

public static void runOnContext(Context context, Runnable runnable) {
    Handler handler = new Handler(context.getMainLooper());
    handler.post(runnable);
}

This method should be part of a service from an utility bundle since it should be accessed by many different android bundles the same way.

For example, this bundle is showing “Hello” as it starts:

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

Cheap method to use applications like bundles

I will refer to the APK converted to an OSGI bundle as bundle APK for short.

  • Create a regular APK, thanks to Eclipse Android Project for example
  • Add a Reference Library entry to the project Build Path for your OSGi framework (in my case framework.jar)
  • Edit a bundle manifest file bundle.manifest describing the bundle (see example below). This file is not really part of the APK but will be used during custom build steps
  • Say your application package is com.acme.helloworld (this value is set with manifest:package in AndroidManifest.xml), your OSGI bundle's Activator class MUST be placed in the package com.acme.helloworld and you MUST set Bundle-SymbolicName: com.acme.helloworld in the bundle manifest. If any of these conditions is not met then will result in a java.lang.NoClassDefFoundError on runtime.

For reminder, your bundle manifest file should look like this:

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
  • Use Android Tools > Export Unsigned Android Package
  • Copy bundle.manifest in the generated unsigned APK as META-INF/MANIFEST.MF
  • Sign the APK using whatever certificate you want. Here you have your bundle APK ready
  • Install the bundle APK like usual. Installation is required in order to have the activities resolved. Without this the activity won't resolve and the bundle will fail
  • Have the OSGi framework load and start the bundle APK (the very same APK file)

To start the activity from the bundle APK, use the following code.

// 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);

Log

Initial

At this point of my efforts, android bundles:

  • can call android SDK classes as long as they don't require resources or declaration in AndroidManifest.xml,
  • have access to the application's android.content.Context, which can be used to start activities out of the OSGi framework.

Bundles cannot:

  • request android permission,
  • build activities from layout not even start them at all,
  • define static broadcast receiver, declared in AndroidManifest.xml, though a receiver instantiated by code should be fine.

That's for the limitations I have experienced so far that I am trying to overcome and the goal of my request for help.

What I am aiming at:

  • have support for internal resources especially layouts,
  • be able to create and start internal activities, by code if not possible by XML builder.

What are my achievements so far, through trials:

  • Make an android project seen as both an OSGi bundle and an APK/android library by mixing the Builders, exporting unsigned binaries and manually merging bundle.manifest into the MANIFEST.MF before signing with jarsigner. The results are the APK is loaded by the OSGi framwork and gets up to the state resolved but is not able to start due to java.lang.NoClassDefFoundError on my activator class, even if the class is part of classes.dex and there is no apparent error on the path. Making the project be an OSGi bundle with android dependencies gets access to the activator but not to android resources in the JAR. Puzzling.
  • Validate all of the features described in the "can do" section of this guide.

Edit 2013-09-03

I have found a way to start activities owned by android bundles. See the corresponding chapter.

Edit 2013-09-10: generic OSGI framework container

After several days, I have made the Knopflerfish programs generic to run whatever OSGi framework I want. For instance, I am presently running Knopflerfish or Felix the same way. There is still a need for a specific framework configuration.

That means the topic is no longer Knopflerfish only, even if the needed programs were issued by Knopflerfish.

2013-09-27: status and frameworks overall comparison

I have to put this project aside for some time due to a change in priority. However I evaluated the following solutions so far:

  • Knopflerfish OSGi [Open source],
  • Felix and FelixDroid [Open source],
  • ProSyst mBS SDK (Equinox-based, commercial use)

To sum it up, none of them has a neat advantage when it comes to GUI support: none of them can handle assets or resources (strings, layouts, images) in the android way, but you can still handle them as OSGi resource with the OSGi API but you won't be able to use them in android as usual.

I personally like the Knopflerfish's administration console servlet but its GUI support amounts to nothing. Felix + FelixDroid has a good balance for a free OSGi solution whereas mBS SDK supports a wide number of different VM targets and defines an intent based application framework that might fit professional developers taste more.

Whereas Knopflerfish and Felix are used almost the same way, mBS SDK is very different in many aspects. Knopflerfish and Felix are exchangeable: I have written a container program where choosing the OSGi framework is only a matter of selecting a different hand made JAR dependency!

When it comes to GUI, Knopflerfish provides about nothing. You'll need to go through my guidelines in order to have a bit more support. The main idea in FelixDroid is good, it is actually something similar implemented in mBS SDK, but it is a bit of a waste to not have the implementation as a bundle. More to say, mBS SDK has done the thing more nicely by defining an OSGi applications framework started by specific intents. Both are integrating views in the main activity about the same way.

Another stunning difference in mBS SDK is you don't need to add the android framework dependencies nor add Import-Package directives for them in your bundles. It is certainly disturbing after relying on Knopflerfish or Felix for a while. Also it is fully integrated in Eclipse and supply the developer with many handy tasks: PC to target OSGi framework monitoring (Kf and Felix provide only a on-target administration console) and fast deployment. The pits are essentially that is is not free and that the container application is nearly impossible to customize.

回答1:

I have found by pure luck some promising Open Source (Apache License 2) framework that might be of interest. It's called DEMUX Framework . Feel free to evaluate this solution. I haven't myself but browsing through the features and source code let me think it has a good potential and neat integration. Regarding the GUI support, it uses an approach similar to FelixDroid. It could become an Open Source alternative to Prosyst mBS SDK.

This is how its designer defines the framework:

DEMUX Framework makes it easy for Java developers to build applications for desktop, web and mobile devices from single code base. It provides modular applications architecture based on OSGI, which makes it easy to build robust and extensible applications.

However Android is the only mobile OS supported at this stage and I wish him good luck for the other two (I had some painful experiences in the past on this subject)