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ファイル
- いじる部分無いでしょー?
ソースコードは日記の末尾につけます。
そのまえにサンプルアプリの説明。
ボタンごとにどういう機能を持っているか説明するよ。
- 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がからっぽだと落ちる。