N2-Works
WEB企画・制作/システム開発 大阪拠点

AndroidのSQLiteのCursorのデータ容量制限

制作中のAndroidアプリでカメラ撮影した画像をDBにBLOBで挿入していたのですが、サイズの大きい画像データだとSELECT時にアプリが落ちる現象に遭遇しました。

仮説でしかないのですが、Cursor内部の1レコード保持のためのメモリが1Mbyteしか用意されていない可能性が考えられます。

以下は検証のために作成したプログラムです。

DBHelper

package net.n2works.SQLiteTest;

// SYSTEM PACKAGE
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.database.sqlite.SQLiteDatabase.CursorFactory;

public class DatabaseHelper extends SQLiteOpenHelper
{
  public DatabaseHelper(Context context, String name, CursorFactory factory, int version)
  {
    super(context, name, factory, version);
  }

  public void onCreate(SQLiteDatabase db)
  {
    String sql = " CREATE TABLE `blob_test`(`テストデータ` BLOB NOT NULL) ";
    db.beginTransaction();
    try {
      SQLiteStatement st = db.compileStatement(sql);
      st.execute();
      db.setTransactionSuccessful();
    }
    finally {
      db.endTransaction();
    }
  }

  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
  {
  }
}

 Activity

package net.n2works.SQLiteTest;

// SYSTEM_PACKAGE
import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;

public class SQLiteTest extends Activity
{
  // DBHelper
  DatabaseHelper dbh;

  /**
   * クラス定数
   */
  // テストデータサイズ(1Mbyte = 1048576byte = 1024byte * 1024byte)
  private static final int BLOB_SIZE = 1048498;

  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);

    // DBHelperの取得
    dbh = new DatabaseHelper(
      this,
      "sqlitetest.db",
      null,
      1
    );

    // DBハンドル取得
    SQLiteDatabase db = GetDB();

    // テストデータ作成
    byte[] test_data = new byte[SQLiteTest.BLOB_SIZE];
    for (int i = 0; i < SQLiteTest.BLOB_SIZE; i++) {
      test_data[i] = 0xf;
    }

    // テストデータ入れ替え
    SQLiteStatement st = db.compileStatement(" DELETE FROM `blob_test` ");
    db.beginTransaction();
    try {
      st.execute();
      st.close();
      st = db.compileStatement(" INSERT INTO `blob_test`(`テストデータ`) VALUES(?) ");
      st.bindBlob(1, test_data);
      st.executeInsert();
      st.close();
      db.setTransactionSuccessful();
    }
    finally {
      db.endTransaction();
    }

    db.close();

    // レイアウト作成
    LinearLayout layout = new LinearLayout(this);
    LayoutParams params = new LayoutParams(
      LayoutParams.FILL_PARENT,
      LayoutParams.FILL_PARENT
    );
    layout.setLayoutParams(params);
    layout.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
    setContentView(layout);

    // ボタンの作成
    Button btn = new Button(this);
    layout.addView(btn);
    params = new LayoutParams(
      LayoutParams.WRAP_CONTENT,
      LayoutParams.WRAP_CONTENT
    );
    btn.setLayoutParams(params);
    btn.setText("データ取得");
    btn.setOnClickListener(new Listener());
  }

  private SQLiteDatabase GetDB()
  {
    try {
      return dbh.getWritableDatabase();
    }
    catch (SQLiteException e) {
      return dbh.getReadableDatabase();
    }
  }

  private class Listener
  implements OnClickListener
  {
    public void onClick(View v)
    {
      SQLiteDatabase db = GetDB();
      Cursor cur = db.rawQuery(
        " SELECT `テストデータ` FROM `blob_test` ",
        null
      );
      byte[] data = null;
      if (cur.moveToFirst()) {
        data = cur.getBlob(0);
      }
      cur.close();
      db.close();
    }
  }
}

BLOB_SIZE = 1048498

という値がmoveToFirstしてもアプリが落ちない値です。

これに1を加算して1048499にすると落ちます。

1Mbyteが1048576byteなので多少誤差がありますが、それはカラム情報などレコードの内部情報を保持しているからではないかと見ています。

スキーマを変更してIntegerのカラムを増やしてみましたが、4byteではなく9byteの差が出たからです。

androidライブラリのターゲットVersionによって今後変更される可能性があるので、実行した環境を記しておきます。

[実行機種]:SO-01B Xperia

[ターゲットversion]:android-2.1-update

BLOB型を使用する場合は注意してください。

[Android]2011年01月22日 17時31分31秒

※1000文字以内で入力してください

captcha
TOP