Hack the World!

プログラミングや,ネットワークに関する話題を取り扱っています.知識をつけて,優雅にお仕事するのを目指しています.

HamsterDB をAndroidに移植してみました。

GitHubにあげてあります。
https://github.com/gracefullife/Hamster-DB-for-Android

自分より先に記事として取り上げていただいてます。
@dumapick さんの日記
http://androdev.blog9.fc2.com/blog-entry-33.html

Hamster DB とは、組み込み向けKVSです。
とても軽量なライブラリとなっており、わずか300KBにてトランザクションの機能を実現しています。

もともとJavaのWrapperなどが用意されていることもあり、NDKにてNative部分をビルドしてあげるだけで使えるようになりました。

今回は、使い方を調べると同時にJavaのサンプルをAndroidように移植してみました。
cursolを使う形式とクエリを投げる(というより、メソッドを叩く)形式があるようです。
今回は後者です。(次回があるのかはわかりません)



KeyとValueにbyte列を用いるようです。
まぁJavaのためのものではないのでこうなるのも当然かな?という印象です。

byte列にシリアライズしてやればなんでもできる!のかもしれません。
(そもそもKVSの何が嬉しいのかよくわかってませんが。。。高速性かなー???



以下の三つのものを用意してもらいます。

  • AndroidManifest.xml
    • permission いじるだけ
  • ソースコード(1ファイル)
    • コピペしてください。(ファイル名をサンプルから変える場合は、ManifestのActivityの変更も忘れずに)
  • レイアウトのXMLファイル
    • いじる部分無いでしょー?


ソースコードは日記の末尾につけます。
そのまえにサンプルアプリの説明。


サンプルアプリのAPK

ボタンごとにどういう機能を持っているか説明するよ。


  • Create DBボタン

SDカードにデータベースファイルを作る。
これがないと、どんなボタンもエラーになる。

SDカードのない端末を使ってる人は諦めて新しい端末買ってください。
古い端末は私にくれてもいいと思います。

  • Insert ボタン

keyが0-9の物を突っ込みます。
対応するValueはkey値+10を入れています。

  • Erase ボタン

データベースファイルを削除する。
keyに対応するValueを削除ではありません。
メンドイので作ってません。

  • Findボタン

insertを実行後に対応するEditTextをKeyとして対応するValueを取りだします。

全部Toastで結果表示するから簡単なはず!




  • AndroidManifest.xml
    • permissionタブ
      • use-permission

以下のものを追加

android.permission.WRITE_EXTERNAL_STORAGE

  • 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="match_parent" android:layout_height="match_parent">
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Create DB" android:id="@+id/createdbtn"></Button>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Insert" android:id="@+id/InsertBtn"></Button>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/erasebtn" android:text="Erase"></Button>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/findValueLabel" android:text="Find Value"></TextView>
<EditText android:layout_width="match_parent" android:id="@+id/editText1" android:layout_height="wrap_content" android:text="0"></EditText>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/findBtn" android:text="Find"></Button>
</LinearLayout>
  • src/com/miyabi/product/HamsterdbTest.java

package com.miyabi.product.hamsterdb;

import java.io.File;
import java.lang.reflect.Array;

import de.crupp.hamsterdb.Const;
import de.crupp.hamsterdb.Database;
import de.crupp.hamsterdb.DatabaseException;
import de.crupp.hamsterdb.Environment;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class HamsterdbTest extends Activity {

	String mDbPath;
	String mDbName;
	static final String LOGTAG="HamsterdbTest";
    Context mContext;
    public static final int LOOP=10;

    Button btnCreate;
    Button btnInsert;
    Button btnErase;
    TextView tv;
    EditText etFindValue;
    Button btnFind;
    
    Environment mHamdbEnv ;
	/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mContext = getApplicationContext();
        
        mDbPath = android.os.Environment.getExternalStorageDirectory() + "/" + getPackageName();
        mDbName = mDbPath + "/ham.db";

        btnCreate = (Button)findViewById(R.id.createdbtn);
        btnInsert=(Button)findViewById(R.id.InsertBtn);
        btnErase = (Button)findViewById(R.id.erasebtn);
        tv = (TextView)findViewById(R.id.findValueLabel);
        etFindValue = (EditText)findViewById(R.id.editText1);
        btnFind  = (Button)findViewById(R.id.findBtn);
        
        if(btnCreate.equals(null)){
        }else{
	        btnCreate.setOnClickListener(new View.OnClickListener() {
				@Override
				public void onClick(View arg0) {
					// TODO Auto-generated method stub
	//				arg0.setEnabled(false);
					int ret = createDb();
					String retStr = "";
					switch(ret){
					case 0:
						retStr ="DB Created";
						break;
					case 1:
						retStr ="DB Creat Failed";
						break;
					
					}
					Toast.makeText(mContext,retStr, Toast.LENGTH_SHORT).show();
	//				arg0.setEnabled(true);
				}
			});
	        btnInsert.setOnClickListener(new View.OnClickListener() {
				
				@Override
				public void onClick(View arg0) {
					// TODO Auto-generated method stub
					int ret = writeDb();
					String retStr = "";
					switch(ret){
					case 0:
						retStr ="Value Inserted:key 0-9 inserted";
						break;
					case 1:
						retStr ="Key Deprecated";
						break;
					
					}
					Toast.makeText(mContext,retStr, Toast.LENGTH_SHORT).show();
				}
			});
	        btnErase.setOnClickListener(new View.OnClickListener() {
				
				@Override
				public void onClick(View arg0) {
					// TODO Auto-generated method stub
					int ret = eraseDb();
					String retStr = "";
					switch(ret){
					case 0:
						retStr ="DB Erased";
						break;
					case 1:
						retStr ="DB Erase Failed : Not Exist";
						break;
					
					}
					Toast.makeText(mContext,retStr, Toast.LENGTH_SHORT).show();
				}
			});
	        btnFind.setOnClickListener(new View.OnClickListener() {
				
				@Override
				public void onClick(View arg0) {
					// TODO Auto-generated method stub
					
					byte[] key = new byte[5];
					CharSequence value  = etFindValue.getText();
					String keyStr = value.toString();
					int keyInt = Integer.parseInt(keyStr);
					key[0] = (byte)keyInt;
					findValue(key);
				}
			});
	        
        }
    }
    
    public int createDb(){
    	int ret = 0;
    	
    	File file = new File(mDbPath);
    	if( !file.exists()){
        	file.mkdir();
    	}
		Database database = new Database();
    	
        mHamdbEnv = new Environment();
        try {
        	database.create(mDbName);
        	database.close();
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			ret = 1;
			e.printStackTrace();
		}
		
		return ret;
    }
    
    public int writeDb(){
    	int ret =0;
        byte[] key=new byte[5];
        byte[] record=new byte[5];
		Database database = new Database();
    	try {
    		database.open(mDbName,Const.HAM_WRITE_THROUGH);
//    		database.open(mDbName);

			for(int i=0;i<LOOP;i++){
	            key[0]=(byte)i;
	            record[0]=(byte)(i+10);
	    
	            Log.i(LOGTAG,"Write:"+i+":"+record[0]);
	            database.insert(key, record);
			}
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			ret = 1;
			e.printStackTrace();
		}finally{
			database.close();
		}
    	return ret;
    }

    
    
    public int eraseDb(){
    	int ret=-1;
    	
    	File file = new File(mDbName);
    	if(file.exists()){
    		ret = 0;
        	file.delete();
    	}else{
    		ret =1;
    	}
    	Log.i(LOGTAG,"Erase DB:" + ret);
    	return ret;
    }
    /**
     * 
     * @author miyabi
     * @param key 
     * @return value of kvs 
     * 
     */
    public int findValue(byte[] key){
    	int ret =0;
    	Integer keyInt,retInt;
    	byte[] retByte;
		Database database = new Database();

		try {
			database.open(mDbName);
			Log.i(LOGTAG,"FindKey"+new String(key));
			retByte = database.find(key);
			//Log.i(LOGTAG,new String(retByte));
//			ret = Integer.parseInt(new String(retByte));
			Toast.makeText(mContext,"FindValue:"+retByte[0], Toast.LENGTH_SHORT).show();
			
		} catch (DatabaseException e1) {
			// TODO Auto-generated catch block
			Toast.makeText(mContext,"FindValue:Error", Toast.LENGTH_SHORT).show();
			e1.printStackTrace();
		}finally{
			database.close();
		}
    	return ret;
    }
}

以上
終わり。

View.OnClickListenerつくりすぎた気がする。
ひとつのOnClickListenerの中で、Switchするのとどっちがいいのだろう?

だれか正しいコーディング教えて!

Database#closeが必要なのかは不明
かつ、EditTextがからっぽだと落ちる。