问题
I am working on developing OSGI bundles that have a full support for android. So far, by my previous questions, I was able to use android API inside OSGI bundle. It works fine and I tried it. I am using Felix framework.
However, I am stuck now at the mission of making an OSGI bundle to have an android activity and to start that activity. I also need these activities to be able to request permissions so I guess I will need an AndroidManifest.xml
in the OSGI bundle.
While doing a research, I was able to find only one person describing his experience in achieving this. Unfortunately, the steps he mentioned is ambiguous to me.
In his question "Full Android Support for OSGI" , Here's what he said:
I have found a way to start activities owned by android bundles:
•the android bundle MUST be an APK which can be created using Eclipse Android Project
•add a Reference Library entry to the project Build Path for your OSGi framework (in my case framework.jar)
•edit bundle.manifest describing the bundle. The file is not part of the APK but will be used on build
•the bundle's code, especially the Activator class, MUST be in the same package as defined in AndroidManifest.xml AND the symbolic name of the bundle MUST be the package name as well. If these conditions are met then all of the classes will be correctly loaded. If not, it will result in seeing java.lang.NoClassDefFoundError on runtime
•Use Android Tools > Export Unsigned Android Package
•copy bundle.manifest in the unsigned APK as META-INF/MANIFEST.MF
•sign the APK using whatever certificate you want
•install the signed APK like any standard android application. Installation is required in order to have the Activity resolved. Without this the activity won't resolve and the bundle will fail
•have the OSGi framework load the bundle APK
Where he says:
•edit bundle.manifest describing the bundle. The file is not part of the APK but will be used on build
All I did is to create an android project (APK) and to follow the first two steps. But at this third step above, I couldn't find "bundle.manifest"
to edit it. It is not there at all, so how come he says edit it?
Also, when I Export Unsigned Android Package
, from where to where should I copy the manifest file?
Finally, is the final signed APK file my bundle that should be loaded by the framework? This seems odd because It is not even a jar file.
If these steps would not help me, then can someone lead me in the right direction? Thank you.
UPDATE:
No one answers my question, so I did the following:
1- In my android application project (which I am trying to make it act as a bundle), I included my Activator class in the same package mentioned in the AndroidManifest.xml.
Here's my Activator.java
class:
package com.example.patient;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
private static BundleContext context;
static BundleContext getContext() {
return context;
}
public void start(BundleContext bundleContext) throws Exception {
Activator.context = bundleContext;
//I WOULD LIKE TO START THE ACTIVITY HERE TO DISPLAY THE TOAST MESSAGE
System.out.println("Android APK Bundle Started");
}
public void stop(BundleContext bundleContext) throws Exception {
Activator.context = null;
}
}
and here's my AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.patient"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.patient.View_Patient_File_Activity"
android:label="View Patient File" >
</activity>
<activity
android:name="com.example.patient.Enter_Patient_ID_Activity"
android:label="View Patient File" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2- I added a Reference Library entry to the project Build Path for my OSGi framework (felix.jar)
3- I generated an unsigned copy of my project using Android Tools.
4- I added a folder to the root directory of the unsigned copy called META-INF
, and inside that folder, I added a file called MANIFEST.MF
, below is the content of that file:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Patient
Bundle-SymbolicName: com.example.patient
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.example.patient.Activator
Import-Package: org.osgi.framework;version="1.3.0"
Bundle-RequiredExecutionEnvironment: OSGi/Minimum-1.0
5- I manually signed the unsigned copy using command line and jar signer tool. Something like:
jarsigner -verbose -keystore /path_to_keystore/mykeystore.keystore my_application.apk my_keystore_alias
6- I installed the signed copy on my PC and on my tab.
7- Finally, I run my application, and let the OSGI framework load the same signed apk file.
NO USE, although the bundle status is active, but I do NOT see the message in the activator start() method, meaning that, my bundle is not loaded correctly. Where did I go worng? Please help.
UPDATE 25-11-2013
I made sure to do the steps correctly, and now I got this:
11-25 17:54:08.600: W/System.err(2714): org.osgi.framework.BundleException: Not found: com.example.patient.Activator
11-25 19:22:36.590: W/System.err(6652): Caused by: java.lang.ClassNotFoundException: com.example.patient.Activator not found by com.example.patient
Meaning that my bundle does not contain the Activator class, but I am sure it does. What could be wrong?
UPDATE 26-11-2013
I opened the signed APK using WinZip. I noticed that unlike the bundles I used to build, the signed APK does not contain the .class files including the "Activator.class", so I copied the com directory that contains all the .class files of the project, and I pasted it in the signed APK. Next, I signed that APK again. Now, when I install the APK, I get the following log which contains many errors:
11-25 23:16:25.651: D/dalvikvm(5617): DexOpt: --- BEGIN 'bundle.jar' (bootstrap=0) ---
11-25 23:16:26.271: D/dalvikvm(5617): DexOpt: --- END 'bundle.jar' (success) ---
11-25 23:16:26.271: D/dalvikvm(5617): DEX prep '/sdcard/felix-cache-1472376252.tmp/bundle1/version0.0/bundle.jar': unzip in 102ms, rewrite 620ms
11-25 23:16:26.271: W/dalvikvm(5617): Class resolved by unexpected DEX: Lcom/example/patient/Activator;(0x4074fa08):0x18a7b8 ref [Lorg/osgi/framework/BundleActivator;] Lorg/osgi/framework/BundleActivator;(0x40714410):0xbd630
11-25 23:16:26.271: W/dalvikvm(5617): (Lcom/example/patient/Activator; had used a different Lorg/osgi/framework/BundleActivator; during pre-verification)
11-25 23:16:26.271: I/dalvikvm(5617): Failed resolving Lcom/example/patient/Activator; interface 902 'Lorg/osgi/framework/BundleActivator;'
11-25 23:16:26.271: W/dalvikvm(5617): Link of class 'Lcom/example/patient/Activator;' failed
11-25 23:16:26.271: E/dalvikvm(5617): ERROR: defineClass(0x4074fa08, com.example.patient.Activator, 0x4079d468, 0, 955, 0x4073e918)
11-25 23:16:26.271: E/Zaid Log(5617): Problem installing the bundle :s
11-25 23:16:26.271: W/System.err(5617): org.osgi.framework.BundleException: Activator start error in bundle com.example.patient [1].
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.Felix.activateBundle(Felix.java:2196)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.Felix.startBundle(Felix.java:2064)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:955)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:942)
11-25 23:16:26.271: W/System.err(5617): at com.example.patient_application.MainActivity.onCreate(MainActivity.java:136)
11-25 23:16:26.271: W/System.err(5617): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1048)
11-25 23:16:26.271: W/System.err(5617): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1715)
11-25 23:16:26.271: W/System.err(5617): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1767)
11-25 23:16:26.271: W/System.err(5617): at android.app.ActivityThread.access$1500(ActivityThread.java:122)
11-25 23:16:26.271: W/System.err(5617): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1005)
11-25 23:16:26.271: W/System.err(5617): at android.os.Handler.dispatchMessage(Handler.java:99)
11-25 23:16:26.271: W/System.err(5617): at android.os.Looper.loop(Looper.java:132)
11-25 23:16:26.271: W/System.err(5617): at android.app.ActivityThread.main(ActivityThread.java:4028)
11-25 23:16:26.271: W/System.err(5617): at java.lang.reflect.Method.invokeNative(Native Method)
11-25 23:16:26.271: W/System.err(5617): at java.lang.reflect.Method.invoke(Method.java:491)
11-25 23:16:26.271: W/System.err(5617): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
11-25 23:16:26.271: W/System.err(5617): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
11-25 23:16:26.271: W/System.err(5617): at dalvik.system.NativeStart.main(Native Method)
11-25 23:16:26.271: W/System.err(5617): Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
11-25 23:16:26.271: W/System.err(5617): at java.lang.VMClassLoader.defineClass(Native Method)
11-25 23:16:26.271: W/System.err(5617): at java.lang.ClassLoader.defineClass(ClassLoader.java:319)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.findClass(BundleWiringImpl.java:2279)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1501)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:75)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1955)
11-25 23:16:26.271: W/System.err(5617): at java.lang.ClassLoader.loadClass(ClassLoader.java:500)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.BundleWiringImpl.getClassByDelegation(BundleWiringImpl.java:1374)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.Felix.createBundleActivator(Felix.java:4329)
11-25 23:16:26.271: W/System.err(5617): at org.apache.felix.framework.Felix.activateBundle(Felix.java:2141)
11-25 23:16:26.271: W/System.err(5617): ... 17 more
回答1:
There were two problems, and by solving them I managed to finally get my APK bundle installed and started on Felix.
1- Unlike OSGI bundles, the final signed APK does not contain the .class
files. That's why I was getting: Not found: com.example.patient.Activator
. By manually copying the com
directory from my android project to the signed APK, and then signing it again, I managed to solve this problem.
2- The second step mentioned by @slash33 which is:
•add a Reference Library entry to the project Build Path for your OSGi framework
was causing me the following error:
(Lcom/example/patient/Activator; had used a different Lorg/osgi/framework/BundleActivator; during pre-verification)
With help of this post, I simply deleted the reference to the library,and removed felix.jar
from my build path. Then I did this: Build Path->Configure Build Path->Projects, and then I added my application project that will load the bundle, and which already has felix.jar
in its build path. This as far as I understand will make the application and the APK bundle use the same felix.jar
. (Instead of keeping different one in both, which makes Dalvik complains).
Therefore, I believe that the correct steps to create an APK android bundle, and then to load it to the framework would be:
- Create a regular APK, for example by creating Eclipse Android Project.
- Make your bundle use the same OSGI framework library used by your application by:
Build Path->Configure Build Path->Projects, and then add your application project that will load the bundle. Your application project should contain the OSGI framework jar file in its build path, (in my case
felix.jar
). - Create the bundle manifest file describing the bundle. You can call it
bundle.manifest
. - 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 packagecom.acme.helloworld
and you MUST setBundle-SymbolicName: com.acme.helloworld
in the bundle manifest. If any of these conditions is not met then will result in ajava.lang.NoClassDefFoundError
on runtime. - Use Android Tools > Export Unsigned Android Package
- Copy
bundle.manifest
to the root directory of the generated unsigned APK asMETA-INF/MANIFEST.MF
. You can use Winzip to open the unsigned APK and add the folderMETA-INF
. - Sign the APK using the command:
jarsigner -verbose -keystore /path_to_keystore/mykeystore.keystore my_application.apk my_keystore_alias
. - Copy your directory that contains all the
.class
files from your android project to the root directory of your signed apk. In my case: it is thecom
directory. - Sign your APK once again.
- Install the APK bundle.
- Have the OSGi framework load and start the APK bundle (the very same APK file)
回答2:
Finally, is the final signed APK file my bundle that should be loaded by the framework? This seems odd because It is not even a jar file. Yes it is. As an APK, it is still a compatible JAR except that it contains dex files too.
The steps you have followed seem legit to me. According to my experience, it should have worked. Do you have any logtrace we can exploit? Knopflerfish console allows you to see the stacktrace when starting and stopping the bundles manually. It is extremely valuable here.
回答3:
the bundle's code, especially the Activator class, MUST be in the same package as defined in AndroidManifest.xml AND the symbolic name of the bundle MUST be the package name as well. If these conditions are met then all of the classes will be correctly loaded. If not, it will result in seeing java.lang.NoClassDefFoundError on runtime
You don't have to make the symbolic name of the bundle and the bundle's code to be in the same package as the android application, as long as the export-package and the import-package in the manifest.mf is correctly set. You can use bndtools(http://bndtools.org) to help generate the correct manifest.mf automatically(for an android application, bndtool doesn't seem to work though. I have to use its obsolete one bnd.jar to do the job. bnd.jar can be downloaded here: http://www.java2s.com/Code/Jar/b/Downloadbndjar.htm)
来源:https://stackoverflow.com/questions/19922775/android-activity-in-osgi-bundle