GitBook 提供支持
2014-08-13 在Android中使用OSGi框架(Knopflerfish)
OSGi是用Java实现的一个模块化服务平台。每个模块被称之为Bundle,可以提供服务,也可以在不重启OSGi框架的情况下被安装或卸载。Knopflerfish是一个完全开源的OSGi R4.2标准的实现。
Android能够无缝的集成现有的Java代码,尽管使用的是与现有java字节码格式不兼容的虚拟机Dalvik,但是它可以轻松的将现有的jar文件和类转换为Android使用的Dalvik字节码格式。由于OSGi框架自身和Bundle都只是普通的jar文件,所以他们都应该可以在Android上运行。事实上,大多数时候是没问题的。
注意:这里只是如何在Android中嵌入OSGi系列文章的第一部分
如果只是想让OSGi框架在Android上跑起来,那么只需要编译Knopflerfish的Android版本,复制到设备上,然后就可以通过命令行启动起来了(见上一篇文章这里)。
现在来看看如何将Knopflerfish和一系列Bundle嵌入到Android应用中,并且从应用中启动和管理OSGi框架和Bundle。
通过代码启动OSGi大概需要下面这几个步骤:
    1.
    创建framework实例(通过framework factory)
    2.
    初始化framework
    3.
    设置initlevel,并启动/安装 bunldes
    4.
    为所有的initlevel重复前述步骤
    5.
    设置startlevel
    6.
    启动framework

嵌入Framework

现在创建一个Android应用,包含一个Actviity。然后在app中引入framework.jar,这样就可以通过一个FrameworkFactory创建OSGi的framework实例了。
1
import org.knopflerfish.framework.FrameworkFactoryImpl;
2
import org.osgi.framework.BundleException;
3
import org.osgi.framework.launch.Framework;
4
import org.osgi.framework.launch.FrameworkFactory;
5
...
6
private Framework mFramework;
7
...
8
Dictionary fwprops = new Hashtable();
9
// add any framework properties to fwprops
10
FrameworkFactory ff = new FrameworkFactoryImpl();
11
mFramework = ff.newFramework(fwprops);
12
try {
13
mFramework.init();
14
} catch (BundleException be) {
15
// framework initialization failed
16
}
Copied!
引入的jar文件不需要dex化,后面build的时候会自动完成这一步的。

Bundle文件dex化

现在bundle的jar文件可以被添加到应用中了,可以作为raw资源放在res/raw下面,也可以放在assets/bunldes。后面这种方式有一个优势:不需要被重命名,而且res资源的名字数量是有限的。
Bundle的jar文件需要被转换成dex格式,下面这个简单的脚本可以完成这件事:
1
dexify() {
2
for f in $*; do
3
tmpdir="`mktemp -d`"
4
tmpfile="${tmpdir}/classes.dex"
5
dx --dex --output=${tmpfile} ${f}
6
aapt add ${f} ${tmpfile}
7
rm -f ${tmpfile}
8
rmdir ${tmpdir}
9
done
10
}
Copied!
然后就可以通过命令dexify assets/bundles/*将bundles转换为dex文件。如果是按照Knopflerfish的教程编译的Knopflerfish,那么不需要将这些bundle的jar文件dex化,但是必须从knopflerfish的framework.jar文件中去掉classes.dex。

安装、启动Bundles

下面这段代码可以帮助启动bundle,并设置initlevel/startlevel。
1
private void startBundle(String bundle) {
2
Log.d(TAG, "starting bundle " + bundle);
3
InputStream bs;
4
try {
5
bs = getAssets().open("bundles/" + bundle);
6
} catch (IOException e) {
7
Log.e(TAG, e.toString());
8
return;
9
}
10
11
long bid = -1;
12
Bundle[] bl = mFramework.getBundleContext().getBundles();
13
for (int i = 0; bl != null && i < bl.length; i++) {
14
if (bundle.equals(bl[i].getLocation())) {
15
bid = bl[i].getBundleId();
16
}
17
}
18
19
Bundle b = mFramework.getBundleContext().getBundle(bid);
20
if (b == null) {
21
Log.e(TAG, "can't start bundle " + bundle);
22
return;
23
}
24
25
try {
26
b.start(Bundle.START_ACTIVATION_POLICY);
27
Log.d(TAG, "bundle " + b.getSymbolicName() + "/" + b.getBundleId() + "/"
28
+ b + " started");
29
} catch (BundleException be) {
30
Log.e(TAG, be.toString());
31
}
32
33
try {
34
bs.close();
35
} catch (IOException e) {
36
Log.e(TAG, e.toString());
37
}
38
}
39
40
private void installBundle(String bundle) {
41
Log.d(TAG, "installing bundle " + bundle);
42
InputStream bs;
43
try {
44
bs = getAssets().open("bundles/" + bundle);
45
} catch (IOException e) {
46
Log.e(TAG, e.toString());
47
return;
48
}
49
50
try {
51
mFramework.getBundleContext().installBundle(bundle, bs);
52
Log.d(TAG, "bundle " + bundle + " installed");
53
} catch (BundleException be) {
54
Log.e(TAG, be.toString());
55
}
56
57
try {
58
bs.close();
59
} catch (IOException e) {
60
Log.e(TAG, e.toString());
61
}
62
}
63
64
private void setStartLevel(int startLevel) {
65
ServiceReference sr = mFramework.getBundleContext()
66
.getServiceReference(StartLevel.class.getName());
67
if (sr != null) {
68
StartLevel ss =
69
(StartLevel)mFramework.getBundleContext().getService(sr);
70
ss.setStartLevel(startLevel);
71
mFramework.getBundleContext().ungetService(sr);
72
} else {
73
Log.e(TAG, "No start level service " + startLevel);
74
}
75
}
76
77
private void setInitlevel(int level) {
78
ServiceReference sr = mFramework.getBundleContext()
79
.getServiceReference(StartLevel.class.getName());
80
if (sr != null) {
81
StartLevel ss =
82
(StartLevel)mFramework.getBundleContext().getService(sr);
83
ss.setInitialBundleStartLevel(level);
84
mFramework.getBundleContext().ungetService(sr);
85
Log.d(TAG, "initlevel " + level + " set");
86
} else {
87
Log.e(TAG, "No start level service " + level);
88
}
89
}
Copied!
现在可以安装并启动bundle了
1
setInitlevel(1);
2
installBundle("event_all-3.0.4.jar");
3
startBundle("event_all-3.0.4.jar");
4
// install/start other bundles...
5
6
setStartLevel(10);
7
8
try {
9
mFramework.start();
10
} catch (BundleException be) {
11
Log.e(TAG, be.toString());
12
// framework start failed
13
}
14
15
Log.d(TAG, "OSGi framework running, state: " + mFramework.getState());
Copied!

问题

如果你按照上文所述一步步做下来了,你可能会发现还是没法跑起来。由于framework的classloader是在运行期加载的bundle文件,Dalvik虚拟机会试图将优化过的dex类文件放到一个系统目录下面/data/dalvik-cache,但是没有root权限的普通应用程序是不能写入那儿的。
下回将如何解决这个问题。
via source
最近更新 1yr ago