将内部数据库移动到SD卡上是否可行?

8
随着我的应用程序中的数据库增长,它将需要越来越多的内部手机空间。DB中没有任何敏感/私人数据,因此我有兴趣将其移动到SD卡中。
我正在使用SQLiteOpenHelper来辅助进行数据库工作。据我所知,您无法在SD卡上使用它来访问DB,因为您无法定义DB路径。然而,互联网上有一些(非常糟糕的)例子表明,您可以覆盖此限制。但是,我从来没有编译过这些代码示例之一。
这种可能吗?如果可以-如何!请注意,Froyo的“应用程序在SD卡上”功能不起作用,因为它不移动内部文件。
4个回答

9

只需使用:

SQLiteDatabase.openDatabase(DB_FULL_PATH, null, SQLiteDatabase.OPEN_READONLY);

DB_FULL_PATH可以是您的SD卡路径,例如/sdcard/mydatabase.db。

编辑:

这是我在应用程序中调用以访问数据库的方法...

private static DBUtil dbHelper = null;

public void openDatabase() {
    if(dbHelper == null) {
        dbHelper = new DBUtil(this.context);
        dbHelper.openDataBase(SQLiteDatabase.OPEN_READWRITE);
    }
}
public void closeDatabase() {
    if(dbHelper != null) {
        dbHelper.close();
        dbHelper = null;
    }
}

这是我正在使用的 db helper 类,实际上是扩展了 SQLiteOpenHelper,因此您仍然可以从这个类中获得您所需的一切。

package com.myapp.android.db;

import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.myapp.android.MyApp;

import java.io.IOException;

/**
 * Standard database utility class.
 * 
 * TODO: Refactor.
 */
public class DBUtil extends SQLiteOpenHelper {

    /**
     * Database directory.
     * 
     * <p>
     * Example: "/sdcard/myapp/db/"
     * </p>
     */
    public static String DB_DIRECTORY = null;

    /**
     * Name of the database file.
     * 
     * <p>
     * Example: "mydatabase.db"
     * </p>
     * 
     */
    public static String DB_NAME = null;

    /**
     * Full absolute path of the database.
     * 
     * <p>
     * Example: "/sdcard/myapp/db/mydatabase.db"
     * </p>
     */
    public static String DB_FULL_PATH = null;
    static {
        DB_DIRECTORY = MyApp.DATA_REPOSITORY_URI + "/myapp/db/";
        DB_NAME = "mydatabase.db";
        DB_FULL_PATH = DB_DIRECTORY + DB_NAME;
    }

    private SQLiteDatabase myDataBase;

    /**
     * Constructor Takes and keeps a reference of the passed context in order to
     * access to the application assets and resources.
     * 
     * @param context
     */
    public DBUtil(Context context) {
        super(context, DB_NAME, null, 1);

        try {
            this.createDataBase();
        } catch (IOException ioe) {
            throw new Error("Unable to create database");
        }
    }

    /**
     * Creates a empty database on the system and rewrites it with your own
     * database.
     * */
    public void createDataBase() throws IOException {        
        if (!checkDataBase()) this.getWritableDatabase();
    }

    /**
     * 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() {
        SQLiteDatabase checkDB = null;
        try {
            checkDB = SQLiteDatabase.openDatabase(DB_FULL_PATH, null,
                    SQLiteDatabase.OPEN_READONLY);
        } catch (SQLiteException e) {
            // database does't exist yet.
        }
        if (checkDB != null) {
            checkDB.close();
        }
        return checkDB != null ? true : false;
    }

    public void openDataBase(int mode) throws SQLException {        
        try {
            myDataBase = SQLiteDatabase.openDatabase(DB_FULL_PATH, null, mode);
        } catch(IllegalStateException e) {
            // Sometimes, esp. after application upgrade, the database will be non-closed, raising a IllegalStateException
            // below. Try to avoid by simply opening it again.
            Log.d(MyApp.APP, "Database non-closed. Reopening.");
            myDataBase = SQLiteDatabase.openDatabase(DB_FULL_PATH, null, mode);
        }
    }

    public void openDataBase() throws SQLException {
        openDataBase(SQLiteDatabase.OPEN_READWRITE);
    }

    public SQLiteDatabase getDb() {
        return myDataBase;
    }

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

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

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

}

我不确定这能否与SQLiteOpenHelper一起使用。不幸的是,我现在不在我的工作空间,无法测试。如果只是这么简单,我会投赞成票的。 - user213345
3
为什么您明确需要SQLiteOpenHelper? - Mathias Conradt
它有许多用于数据库创建和升级的辅助方法。我不熟悉处理这些操作的更简单的方法。 - user213345
这个回答是无意义的。你完全错误地使用了SQLIteOpenHelper,也就是说,你并没有真正使用它,而是自己打开了一个单独的数据库,使得继承变得毫无意义。 - Adrian Crețu
为什么这样呢?我在代码中并没有提到我使用它来解决问题(打开数据库),事实上,我问OP为什么首先想要使用SQLiteOpenHelper,可以看到我的第二条评论。我只是继承,以防他以后想在程序中使用它。另一方面,这个答案已经超过6年了,我不再记得具体细节了。 - Mathias Conradt

1

我发现在Android 2.2中可以使用完整路径,但在2.1中,Context.openOrCreateDatabase()方法会抛出异常。为了解决这个问题,我包装了该方法以直接调用SQLiteDatabase.openOrCreateDatabase()。这是我扩展的SQLOpenHelper的构造函数:

public class Database extends SQLiteOpenHelper {
  public Database(Context context) {
    super(new ContextWrapper(context) {
        @Override public SQLiteDatabase openOrCreateDatabase(String name, 
                int mode, SQLiteDatabase.CursorFactory factory) {

            // allow database directory to be specified
            File dir = new File(DIR);
            if(!dir.exists()) {
                dir.mkdirs();
            }
            return SQLiteDatabase.openDatabase(DIR + "/" + NAME, null,
                SQLiteDatabase.CREATE_IF_NECESSARY);
        }
    }, NAME, null, VERSION);
    this.context = context;
  }
}

0

这样就可以了。我以为

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Log.i("DATABASE EXIST : ", ""+checkDataBase());
    if(!checkDataBase())
        copyDataBase();

    DatabaseHandler dbhandler = new DatabaseHandler(MainActivity.this);
    Cursor cursor = dbhandler.getAllContacts();

    ListView list = (ListView) findViewById(R.id.datalist);
    CustomCursorAdapter cursoradapter = new CustomCursorAdapter(MainActivity.this, cursor);
    list.setAdapter(cursoradapter);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

private void copyDataBase()
{
    ContextWrapper cw =new ContextWrapper(getApplicationContext());
    String DB_PATH = "/data/data/com.example.copydatabase/databases/";
    String DB_NAME = "testing";

    Log.i("Database", "New database is being copied to device!");
    byte[] buffer = new byte[1024];
    OutputStream myOutput = null;
    int length;
    // Open your local db as the input stream
    InputStream myInput = null;
    try
    {
        myInput = MainActivity.this.getAssets().open(DB_NAME);
        // transfer bytes from the inputfile to the
        // outputfile
        myOutput =new FileOutputStream(DB_PATH+ DB_NAME);
        while((length = myInput.read(buffer)) > 0)
        {
            myOutput.write(buffer, 0, length);
        }
        myOutput.close();
        myOutput.flush();
        myInput.close();
        Log.i("Database", "New database has been copied to device!");

    }
    catch(IOException e)
    {
        e.printStackTrace();
    }
}

public boolean checkDataBase()
{
    String DB_PATH = "/data/data/com.example.copydatabase/databases/";
    String DB_NAME = "testing";
    File dbFile = new File(DB_PATH + DB_NAME);
    return dbFile.exists();
}
}

0
    String dbPath = DATABASE_NAME;
    File sdcard = Environment.getExternalStorageDirectory();
    if (sdcard != null && sdcard.canWrite()){
        dbPath = sdcard.getAbsolutePath() + "/mypath/onsdcard/" + DATABASE_NAME;
    }
    else {
        dbPath = DATABASE_NAME;
    }
    mDBHelper  = new WorkoutDBOpenHelper(context, dbPath);
    if(null != mDBHelper)
        mDB = mDBHelper.getWritableDatabase();

对我来说,这个可以工作,WorkoutDBOpenHelper扩展了SQLiteOpenHelper,它的构造函数只是调用了SQLiteOpenHelper的超类。

WorkoutDBOpenHelper(Context context, String dbPath) {
    super(context, dbPath, null, DATABASE_VERSION);

}

请注意,SQLiteopenHelper也会在存储卡上创建数据库。但是,在应用程序卸载时,数据库不会从SD卡中删除。
这不是将现有的内部数据库移动到SD卡的答案,但是您可以在创建时选择一种选项。我正在努力将已经存在于应用程序“数据”目录中的数据库移动到SD卡,但没有直接的方法。一旦我找到解决方案,我会进行更新。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接