Можно ли переместить внутреннюю БД на SDCard? - PullRequest
8 голосов
/ 09 августа 2010

По мере роста базы данных в моем приложении требуется все больше и больше внутреннего телефонного пространства. В БД нет конфиденциальных / конфиденциальных данных, поэтому мне интересно перенести их на SD-карту.

Я использую SQLiteOpenHelper для помощи в работе с базой данных. Насколько я понимаю, вы не можете использовать это для доступа к БД на SD-карте, поскольку вы не можете определить путь к БД. Тем не менее, есть несколько (очень плохих) примеров в Интернете, которые предлагают вам отменить это ограничение. Однако я никогда не получал один из этих примеров кода для компиляции.

Возможно ли это? И если так - КАК! Обратите внимание, что функция «приложения на SD-карте» Froyo не будет работать, поскольку она не перемещает внутренние файлы.

Ответы [ 4 ]

10 голосов
/ 09 августа 2010

Просто используйте:

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, который фактически расширяет 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 голос
/ 20 января 2012

Я обнаружил, что могу использовать полный путь в 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 голосов
/ 12 ноября 2015

Это будет хорошо. Я думал

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 голосов
/ 07 декабря 2011
    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, а его конструктор просто вызывает super для SQLiteOpenHelper.

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

}

Обратите внимание, что SQLiteopenHelper также создает базу данных на карте памяти. Однако при удалении приложения БД не будет удалена из SDCard.

Это не ответ о том, как перенести существующую внутреннюю БД на SDCard, но таким образом вы можете выбрать один вариант во время создания. Я работаю над перемещением уже существующей базы данных из каталога «data» приложения в sdcard, но прямого пути нет. Буду обновлять, когда я что-нибудь выясню.

...