在Google Android平台上运行Berkeley DB Java Edition
Google 推出Android平台一年多以来,Android已经成为最受欢迎的手机操作系统之一(另外几个成熟的手机操作平台包括Symbian, iPhone/MacOS, Windows Mobile等)。目前,已经至少有12款手机使用Android平台,并且据报道将会有更多的手机采用Android平台。
Android平台内置的存储数据库(或者说存储引擎)是SQLite。但由于SQLite其自身的一些缺陷,比如非Java语言、并发读写性能不 佳、技术支持困难等等,所以我们的用户倾向于Berkeley DB。我在这里向大家推荐Berkeley DB Java Edition(简称BDB JE)。我们可以把BDB JE的优势(它除了是存Java语言实现外,还有很高的存储性能和良好的并发性支持、Oracle的开发及技术支持团队等等)充分运用到Android 平台上。众所周知,Android平台是类Java语言的,所以,BDB JE对Android平台的开发者而言是一款利器。
以下将手把手教你将BDB JE快速配置到Android平台上(本文,我们用Android在PC机上的模拟器 (avd) 作为实例平台)。
准备工作
- 如果你的电脑上尚未安装Android SDK。那么首先点击这里下载并且安装 Android SDK。Android的官方主页上有详细的如何安装并且启动Android 模拟器(avd)的文档,这里就不再涉及。必须注意的一点是, 将你所安装的Android目录下(假如是
<android-installation -home>)的 “tools” 文件夹路径加入到你电脑的路径(path)中去(相信有一定编程经验的读者一定很熟悉如何添加系统路径了),这样,你就可以直接在命令行上运行 Android的各种命令了。
- 点此下载最新版本的BDB JE。解压缩之后(假如你解压路径为JE_HOME/)可以看到在JE_HOME/lib/ 下有一个“je-android-4.0.71.jar”文件。如果找不到,说明你下载的不是最新版本的BDB JE。请到Oracle BDB JE官方网址进行下载:http://www.oracle.com/technology/products/berkeley-db/je/index.html。
- 在开始下面教程之前,我默认大家已经有一定的Android编程基础,如果没有,请大家到Android的官方主页上阅读相关文档(比如,如 何实现“hello world”).
选择一:在装有Android 插件的Eclipse 编程环境下进行配置
- 如果你编程的IDE是Eclipse,并且你已经在上面安装了Android编程插件(如何在Eclipse上安装Android插件,请看这里),那么就继续阅读本小节以下步骤。注意,我用的是Eclipse3.4.2(Ganymede)版本, 也许不同的版本配置上有稍微的区别。这里建议Eclipse3.4版本以上。
- 打开Eclipse,确认在Windows->Preference->Android下已经写入Android SDK的路径。进入
"File->New->Project->Android->Android Project->Next Project",在弹出的建立project的面板上选择build target(比如Android2.0),并填写Project name (JEExample)、 application name (JEExample)、package name (com.sleepycat.je) 和 activity name (JEExample)。最后点击Finish。Eclipse将会自动帮你生成一个Android工程。假设你所建立的工程路径是<eclipse-je-android-dir>
- 进入”Project->Properties->Libraries->Add External JARs”,在弹出的窗口中选择JE_HOME/lib/je-android-4.0.71.jar。点击OK,你将可以看到在你建立的工程 (JEExample)下面有Referenced Libraries目录,点击之后可以看到je-android-4.0.71.jar。
-
复制代码(这些代码可在下面的"源代码"小节找到):将JEExample.java复制到<eclipse-je-android-dir>/src/com/sleepycat/je将main.xml复制到<eclipse-je-android-dir>/res/layout/main.xmland将strings.xml复制到<eclipse-je-android-dir>/res/values/strings.xml
- 在Eclipse中,打开main.xml,你可以看到在上一步中复制的代码。点击Run,运行JEExample。在正确运行之前,你还需要在 Android上面建立一个用于存储JE数据的文件夹,具体步骤看“在Android模拟器上运行程序”小节中的第一步。
选择二:在非IDE环境下进行配置
- 如果你不喜欢用IDE(如Eclipse)进行编程,只想通过命令行进行配置,请继续阅读本小节以下步骤。
- 首先建立一个Android 的模拟器(AVD)(如果你还没有在你的电脑上建立过Android 的模拟器):
在命令行运行 android create avd --target 3 --name my_avd(注意,我在这里建立的是Android1.6的模拟器,你也可以建立Android2.0模拟器)- 在你想要建立Android工程的目录下面,运行
android create project --path JEExample --package com.sleepycat.je --name JEExample --activity JEExample --target 3
命令运行之后,将在你所在目录下面建立Android工程,文件夹的名称叫做JEExample,其中会有
JEExample/src/com/sleepycat/je/JEExample.java 文件。
- 将JE_HOME/lib/je-android-4.0.71.jar 文件复制到
JEExample/libs
- 将三个文件:
JEExample/src/com/sleepycat/je/JEExample.java,JEExample/res/layout/main.xml和 JEExample/res/values/strings.xml替换成下面“源代码”小节中对应的三个文件:JEExample.java,main.xml和strings.xml。
- 进入
<android-installation-home>/platforms/android-1.6/tools, 打开dx.bat文件, 将其中一行 “REM set javaOpts=-Xmx256M” 改成 “set javaOpts=-Xmx512M“。注意“REM”要去掉。另外,如果你之前建立的Android模拟器 (AVD)是Android2.0,那么就更改android-2.0/tools下的dx.bat文件。
- 运行Android 模拟器:emulator -avd
my_avd
- 用命令行进入JEExample目录,然后运行命令
ant install
这个命令将会编译JEExample.java ,然后生成JEExample/bin/JEExample-debug.apk ,并将它安装到你所运行的Android 模拟器中。
在Android模拟器上运行示例程序
- 首先,为Android程序建立JE environment 路径,用于存储JE的数据(这一步不可忽略,并且路径名要跟程序一致,否则无法正确运行程序)。具体做法是 :
在命令行运行adb shell mkdir /data/local/je。或者你首先运行adb shell,然后cd data/local,最后mkdir je。- 你也可以删除这个文件夹:
rm /data/local/je/*。更多adb shell的命令请参照Android文档。
- 在Android模拟器窗口上点击三角按钮(在Home界面)进入程序列表,在这里你可以看到已经安装上去的 JEExample 程序图标, 点击它,运行JEExample程序。 进入程序之后,在程序界面最上方可以看到 “JEExample” 字样,下面是一个 文字编辑框(TextEdit box), 还有两个按钮,分别是 “Put Data” 和 “Get Data” 。
- 存数据:
- 在文字编辑框里输入 key/data 数据对(比如 k1/d1)然后点击 “Put Data” 按钮,这样一条数据(键是k1,值是d1)便存进JE数据库中。
你可以在命令行运行adb shell,进入/data/local/je, 就可以看到JE的存储数据。
- 读数据:
- 在文字在文字编辑框里输入 你之前存储的键值key(比如k1),然后点击 “Get Data” 按钮,将会弹出“Get Data”的对话框,上面显示你之前存储的数据(比如d1)。
JE示例源代码
- JEExample.java
package com.sleepycat.je; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.util.Log; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import java.io.File; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseConfig; import com.sleepycat.je.DatabaseEntry; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.Environment; import com.sleepycat.je.EnvironmentConfig; import com.sleepycat.je.OperationStatus; import com.sleepycat.je.Transaction; public class JEExample extends Activity { @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); try { final File envDir = new File("/data/local/je"); final EnvironmentConfig envConfig = new EnvironmentConfig(); envConfig.setTransactional(true); envConfig.setAllowCreate(true); final Environment env = new Environment(envDir, envConfig); final DatabaseConfig dbConfig = new DatabaseConfig(); dbConfig.setTransactional(true); dbConfig.setAllowCreate(true); dbConfig.setSortedDuplicates(true); final Database db = env.openDatabase(null, "exampledb", dbConfig); setContentView(R.layout.main); final Button button1 = (Button) findViewById(R.id.do_put); button1.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { final EditText editText = (EditText) findViewById(R.id.entry); final String keyData = editText.getText().toString(); final int idx = keyData.indexOf("/"); String key = null; String data = null; String result = null; if (idx < 0) { result = "enter key/data to put"; } else { key = keyData.substring(0, idx); data = keyData.substring(idx + 1); result = key + "/" + data; final DatabaseEntry keyEntry = new DatabaseEntry(key.getBytes()); final DatabaseEntry dataEntry = new DatabaseEntry(data.getBytes()); try { final Transaction txn = env.beginTransaction(null, null); final OperationStatus res = db.put(txn, keyEntry, dataEntry); if (res != OperationStatus.SUCCESS) { result = "Error: " + res.toString(); } txn.commit(); } catch (DatabaseException DE) { result = "Caught exception: " + DE.toString(); } } Log.d("JE", "did put of: " + result); if (result.contains("Caught exception:")) { new AlertDialog.Builder(JEExample.this). setTitle("Put Data").setMessage(result). setPositiveButton("Quit", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).show(); } else { new AlertDialog.Builder(JEExample.this). setTitle("Put Data").setMessage("You put the key/data pair: " + result). setPositiveButton("Confirm", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).show(); } } }); final Button button2 = (Button) findViewById(R.id.do_get); button2.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { final EditText editText = (EditText) findViewById(R.id.entry); final String key = editText.getText().toString(); final DatabaseEntry keyEntry = new DatabaseEntry(key.getBytes()); final DatabaseEntry dataEntry = new DatabaseEntry(); String result = null; try { final Transaction txn = env.beginTransaction(null, null); final OperationStatus res = db.get(txn, keyEntry, dataEntry, null); if (res != OperationStatus.SUCCESS) { result = "Error: " + res.toString(); } else { result = new String(dataEntry.getData()); } txn.commit(); } catch (DatabaseException DE) { result = "Caught exception: " + DE.toString(); } Log.d("JE", "did get of: " + result); if (result.contains("Caught exception:")) { new AlertDialog.Builder(JEExample.this). setTitle("Get Data").setMessage(result). setPositiveButton("Quit", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).show(); } else { new AlertDialog.Builder(JEExample.this). setTitle("Get Data").setMessage("Get result: " + result). setPositiveButton("Confirm", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).show(); } } }); } catch (Exception DE) { TextView tv = new TextView(this); tv.setText("blew chunks " + DE); setContentView(tv); } } } - res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/label" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="JEExample" /> <EditText android:id="@+id/entry" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@android:drawable/editbox_background" android:layout_below="@id/label" /> <Button android:id="@+id/do_put" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button_put" /> <Button android:id="@+id/do_get" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button_get" /> </LinearLayout> - res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">JEExample</string> <string name="button_put">Put Data</string> <string name="button_get">Get Data</string> </resources>
结束语
怎么样,是不是很简单就可以将JE配置到Android中了?你也赶紧试试吧。如果在配置或者安装过程中出错,欢迎给我留言。
注:英文好的同学,可以参照JE最新发布包中的<je-home>/docs/HOWTO-Android.html的英文原文。
你好,
我在国外论坛上看到bdb可以用C写,然后用java来读取数据;
但我用java去open服务器上用C写的bdb,却拿不出数据来,count=0。
如果这样的情况,需要怎么配置java的open过程。
我用的是je-4.0.71,服务器上是4.7的bdb。
@weifeng
您好。
事实上,BDB JE是不能够读取用BDB Core(也就是C版本)存储的数据,因为存储的log file是完全不一样的。不知道您是在哪个论坛上看到的呢(可以提供链接吗?)
如果您只是想通过写java语句来读取BDB Core存储的数据,您可以用BDB Core的Java API:
http://www.oracle.com/technology/documentation/berkeley-db/db/java/index.html
谢谢您的指点。
我之后下了一个Berkeley DB 4.7.25NC.tar.gz ,
用java编译以后,在/usr/local/BerkeleyDB.4.7的lib有db.jar包,
我将它放在我的应用的lib里面,
出现java.lang.UnsatisfiedLinkError: no db_java-4.7 in java.library.path错误
请问一下,这个是不是还需要环境变量的配置?该如何配?
By the way,我还遇到一个很奇怪的问题,当我把我的应用项目编译打包时,ant老是提示
cannot find symbol
[javac] symbol : constructor StoredSortedMap(com.sleepycat.db.Database,com.sleepycat.bind.ByteArrayBinding,com.sleepycat.bind.ByteArrayBinding,boolean)
[javac] location: class com.sleepycat.collections.StoredSortedMap
明明存在这个类,怎么也出现这个问题;
是不是和上面的错误一样?
谢谢
@weifeng
本文介绍的是如何设置BDB-JE运行于Android平台,而你用的是BDB (2者差别很大)。目前BDB暂时不支持Android。请保持关注BDB的后续版本,谢谢。