首页 > Berkeley DB JE, Haomian Wang > 在Google Android平台上运行Berkeley DB Java Edition

在Google Android平台上运行Berkeley DB Java Edition

2009年11月20日 haomianwang

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的各种命令了。
  • 在开始下面教程之前,我默认大家已经有一定的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.xml and
    • 将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.javaJEExample/res/layout/main.xml 和 JEExample/res/values/strings.xml 替换成下面“源代码”小节中对应的三个文件:JEExample.javamain.xmlstrings.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的英文原文。

  1. 2009年12月19日11:32 | #1

    你好,
    我在国外论坛上看到bdb可以用C写,然后用java来读取数据;
    但我用java去open服务器上用C写的bdb,却拿不出数据来,count=0。
    如果这样的情况,需要怎么配置java的open过程。

    我用的是je-4.0.71,服务器上是4.7的bdb。

  2. haomianwang
    2009年12月21日17:51 | #2

    @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

  3. weifeng
    2010年1月23日19:28 | #3

    谢谢您的指点。

    我之后下了一个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
    明明存在这个类,怎么也出现这个问题;
    是不是和上面的错误一样?

    谢谢

  4. chaohuang
    2010年1月25日11:20 | #4

    @weifeng
    本文介绍的是如何设置BDB-JE运行于Android平台,而你用的是BDB (2者差别很大)。目前BDB暂时不支持Android。请保持关注BDB的后续版本,谢谢。

本文的评论功能被关闭了.
Դ