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

时间:2010-08-08 23:06:30

标签: android sqlite sd-card

随着我应用中数据库的增长,它将需要越来越多的内部手机空间。数据库中没有任何敏感/私人数据,因此我有兴趣将其移至SD卡。

我正在使用SQLiteOpenHelper来协助数据库工作。我的理解是,您不能将其用于SD卡上的DB访问,因为您无法定义数据库路径。但是,Internet上有一些(非常差的)示例表明您可以覆盖此限制。但是我从来没有得到其中一个代码示例来编译。

有可能吗?如果是这样 - 怎么样!请注意,Froyo的“SD卡上的应用程序”功能不起作用,因为它不会移动内部文件。

4 个答案:

答案 0 :(得分:10)

只需使用:

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

其中DB_FULL_PATH可以是sdcard的路径,例如/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帮助程序类,它实际上扩展了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) {
    }

}

答案 1 :(得分: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;
  }
}

答案 2 :(得分: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调用super。

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

}

请注意,SQLiteopenHelper也会在存储卡上创建数据库。 但是,在应用程序卸载时,不会从SD卡中删除数据库。

这不是如何将现有内部数据库移动到SDCard的答案,但这样您就可以在创建时选择一个选项。我正在努力将已经存在的数据库从应用程序的“数据”目录移动到SD卡,但没有直接的方法。一旦我找到了什么就会更新。

答案 3 :(得分: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();
}
}