0

解決

カスタム DBHelper クラスで getReadableDatabase() と getWritableDatabase() をオーバーライドする必要がありました。

私はユニークな種類の問題に直面しています。

これまで、電話のメモリ上で内部的に 1 つの SQLite DB を使用する Android アプリを作成してきましたが、完全に動作します。ここで、別の DB を追加し、それを SD カードに保存したいと考えました。これは、通常使用する DatabaseHelper クラスを変更することで実現しました。また、アセット フォルダから 1 つの DB の SD カードと別の DB の内部メモリ ロケーションに db を転送するスクリプトも作成しました。

今私が直面している問題は、外部 DB にアクセスしようとすると、Android が /data/data/package_name/databases フォルダーに内部 DB を作成するため、そのようなテーブル例外が発生しないことです。

なぜこのエラーが発生するのか誰か教えてください!


1 My DB ヘルパー クラスを編集

public class DBHelper extends SQLiteOpenHelper {
private Context myContext;
private String DB_PATH;
private String DB_NAME;
private boolean isExternal;
private SQLiteDatabase myDataBase;

private final String TAG = "ERROR";

public DBHelper(Context AppC, String DB_NAME, boolean isExternal,
        int DB_VERSION) {
    super(AppC, DB_NAME + ".sqlite", null, DB_VERSION);
    this.myContext = AppC;
    this.isExternal = isExternal;
    this.DB_NAME = DB_NAME;
    setDbPath();
}

private void setDbPath() {
    if (this.isExternal) {
        try {
            DB_PATH = Environment.getExternalStorageDirectory()
                    + "/myappname/";
        } catch (Exception e) {
            DB_PATH = "/mnt/sdcard/myappname/";
        }
    } else {
        try {
            DB_PATH = Environment.getDataDirectory()
                    + "/data/my.app.package/databases/";
        } catch (Exception e) {
            DB_PATH = "data/data/my.app.package/databases/";
        }
    }
}

/**
 * Creates a empty database on the system and rewrites it with your own
 * database.
 * */
public void createDataBase() throws IOException {
    boolean dbExist = checkDataBase();
    if (!dbExist) {
        try {
            copyDataBase();
        } catch (Exception E) {
            // MRC
        }
    }
}

/**
 * Copies your database from your local assets-folder to the just created
 * empty database in the system folder, from where it can be accessed and
 * handled. This is done by transferring byte streams.
 * */
private void copyDataBase() throws IOException {
    String filePath = DB_PATH + this.DB_NAME + ".sqlite";

    if (this.isExternal) {
        File file = new File(filePath);
        if (!file.exists()) {
            File dir = new File(DB_PATH);
            if (dir.exists() || dir.mkdirs()) {
                transferFromAssets(this.DB_NAME + ".zip", filePath);
            } else {
                Log.d(TAG, "Unable to create dir " + DB_PATH);
            }
        }
    } else {
        // By calling this method and empty database will be created
        // into the default system path of your application so we
        // will be able to overwrite that database with our
        // database.

        SQLiteDatabase db_Read = this.getReadableDatabase();
        db_Read.close();
        transferFromAssets(this.DB_NAME + ".zip", filePath);
    }
}

private void transferFromAssets(String from, String to) throws IOException {
    ZipInputStream myInput = new ZipInputStream(myContext.getAssets().open(
            from));
    myInput.getNextEntry(); // Open the empty db as the output stream
    OutputStream myOutput = new FileOutputStream(to);
    // transfer bytes from the inputfile to the outputfile
    AndroidUtil.copyStream(myInput, myOutput);
}

/**
 * Check if the database already exist to avoid re-copying the file each
 * time you open the application.
 * 
 * @return true if it exists, false if it doesn't
 */
private boolean checkDataBase() {
    File dbFile = new File(DB_PATH + this.DB_NAME + ".sqlite");
    return dbFile.exists();
}

public void openDataBase() throws SQLException {
    String myPath = DB_PATH + this.DB_NAME + ".sqlite";
    Log.d(TAG, "Opening DB:" + myPath);
    myDataBase = SQLiteDatabase.openDatabase(myPath, null,
            SQLiteDatabase.OPEN_READWRITE);
}

@Override
public synchronized void close() {
    if (myDataBase != null)
        myDataBase.close();
    super.close();
}

public void onCreate(SQLiteDatabase db) {

}

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

}

}

*私の内部メモリ DB のクラスのコンストラクター *

public WordsDB(Context AppC) {
    DBAPI = new DBHelper(AppC, "DB_NAME", false, 11);
    this.AppC = AppC;
    try {
        DBAPI.createDataBase();
        DBAPI.openDataBase();
    } catch (IOException e) {
        CustomToast.Toast(AppC, "", TCategory_E.CRITICAL_ERROR,
                TDuration_E.LONG, TPosition_E.CENTER);
        // MRC
    }
}

** 外部 DB クラスのコンストラクター **

public BackupDB(Context AppC) {
    DBAPI = new DBHelper(AppC, "/mnt/sdcard/myapp/backup-db",
            true, 11);
    this.AppC = AppC;
    try {
        DBAPI.createDataBase();
        DBAPI.openDataBase();
    } catch (IOException e) {
        CustomToast.Toast(AppC, "", TCategory_E.CRITICAL_ERROR,
                TDuration_E.LONG, TPosition_E.CENTER);
        // MRC
    }
}

編集 2

エラーにつながる外部 DB のヘルパー クラスの関数:

public void setValue(int No, int Stars) {
    try {
        ContentValues update = new ContentValues();
        update.put(COL_STARRED, Stars);
        String args[] = { No + "" };
        SQLDB = DBAPI.getWritableDatabase();
        SQLDB.acquireReference();
        SQLDB.update(TABLE_NAME, update, COL_ID + "=?", args);
        SQLDB.releaseReference();
        SQLDB.close();
    } catch (Exception E) {
        Log.d(TAG, E.getMessage());
    }
}

外部 DB を内部 DB として使用しようとすると、エラーが発生せず、外部 DB に対して何か問題があると思います。

4

1 に答える 1

1

コードを教えてください。

これは、通常使用する DatabaseHelper クラスを変更することで実現しました。

どのような変更を加えたか説明していただけますか? を使用している場合はSQLiteOpenHelper、そのコンストラクターに渡される DB パスが /data/data/package/databases/ フォルダーに相対的であるため、そこに SD パスを渡すことができないことに注意してください。それでもこれが必要な場合は、SD DB を指す方法を使用ContextWrapperして再定義することをお勧めします。getDatabasePath

于 2012-09-02T11:19:16.067 に答える