如何使用 SQLiteOpenHelper 与 sd 卡上的数据库?

发布于 2024-11-02 09:13:29 字数 1727 浏览 0 评论 0原文

根据此处和网络扩展应用程序中的各种答案及其继承的方法 getDatabasePath() 将允许设置从标准内部存储器位置到插入的更大尺寸的 SD 卡的数据库存储路径。

这对我不起作用。建议的构造仍然使用内存上的数据库。事实上,SQLiteOpenHelper 永远不会调用 getDatabasePath() 方法。

我想让这个启动并运行。

这是我到目前为止所做的:

1.)扩展应用程序:

public class MyApplication extends Application {

  @Override
  public File getDatabasePath(String name) {
    // Just a test
    File file = super.getDatabasePath(name);

    return file;
  }

  @Override
  public void onCreate() {
    // Just a test
    super.onCreate();
  }
}

2.)将扩展应用程序添加到清单:

<application
  ...
  android:name="MyApplication" 
  ... >

3.)扩展和使用 SQLiteOpenHelper:

public class MySqliteOpenHelper extends SQLiteOpenHelper {

  public void onCreate(SQLiteDatabase sqliteDatabase) {
    ...
  }

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

4.)以通常的方式在我的活动中使用扩展的 SQLiteOpenHelper:

public class MyActivity extends Activity {

  private MySqliteOpenHelper mySqliteOpenHelper;
  private SQLiteDatabase     sqliteDatabase;

  @Override
  public void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    ...
    mySqliteOpenHelper = new MySqliteOpenHelper(getApplicationContext());
    sqliteDatabase = mySqliteOpenHelper.getReadableDatabase();
    ...
  }

  @Override
  protected void onDestroy() {
    if (mySqliteOpenHelper != null) {
      mySqliteOpenHelper.close();
      mySqliteOpenHelper = null;
    }

    super.onDestroy();
  }
}

我想指出发现扩展的应用程序类总体上正在工作。我可以看到这一点,因为 MyApplication.onCreate() 被调用。但 MyApplication.getDatabasePath() 未被调用。

非常感谢任何帮助。

According to various answers here and in the web extending Application and it's inherited method getDatabasePath() would allow to set the database storage path from the standard internal memory location to an inserted SD-card of bigger size.

This is not working for me. The suggested construct is still using the database on internal memory. In fact the method getDatabasePath() is never called by SQLiteOpenHelper.

I would like to get this up and running.

Here's what I did so far:

1.) Extending Application:

public class MyApplication extends Application {

  @Override
  public File getDatabasePath(String name) {
    // Just a test
    File file = super.getDatabasePath(name);

    return file;
  }

  @Override
  public void onCreate() {
    // Just a test
    super.onCreate();
  }
}

2.) Adding extended Application to the Manifest:

<application
  ...
  android:name="MyApplication" 
  ... >

3.) Extending and using SQLiteOpenHelper:

public class MySqliteOpenHelper extends SQLiteOpenHelper {

  public void onCreate(SQLiteDatabase sqliteDatabase) {
    ...
  }

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

4.) Using the extended SQLiteOpenHelper in my Activities in the usual way:

public class MyActivity extends Activity {

  private MySqliteOpenHelper mySqliteOpenHelper;
  private SQLiteDatabase     sqliteDatabase;

  @Override
  public void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    ...
    mySqliteOpenHelper = new MySqliteOpenHelper(getApplicationContext());
    sqliteDatabase = mySqliteOpenHelper.getReadableDatabase();
    ...
  }

  @Override
  protected void onDestroy() {
    if (mySqliteOpenHelper != null) {
      mySqliteOpenHelper.close();
      mySqliteOpenHelper = null;
    }

    super.onDestroy();
  }
}

I want to point out that the extended Application class is working in general. I can see this because MyApplication.onCreate() is called. But MyApplication.getDatabasePath() is not called.

Any help is highly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

秋风の叶未落 2024-11-09 09:13:29

我发现我可以在 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;
  }
}

I found I could use a full path in Android 2.2, but in 2.1 the Context.openOrCreateDatabase() method threw an exception. To work around this I wrapped that method to call SQLiteDatabase.openOrCreateDatabase() directly. Here is the constructor for my extended 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;
  }
}
手心的温暖 2024-11-09 09:13:29

重写 SQLOpenHelper 以使用 SD 卡目录而不是上下文,然后扩展它似乎对我有用。

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteException;
import android.util.Log;

/**
 * SDCardSQLiteOpenhelper is a class that is based on SQLiteOpenHelper except
 * that it does not use the context to get the database. It was written owing to
 * a bug in Android 4.0.3 so that using a ContextWrapper to override
 * openOrCreateDatabase, as was done with Android 2.3.3, no longer worked. <br>
 * <br>
 * The mContext field has been replaced by mDir. It does not use lock on the
 * database as that method is package private to
 * android.database.sqlite.SQLiteDatabase. Otherwise the implementation is
 * similar.<br>
 * <br>
 * 
 * @see android.database.sqlite.SQLiteOpenHelper
 */
public abstract class SDCardSQLiteOpenHelper {
    private static final String TAG = SDCardSQLiteOpenHelper.class
            .getSimpleName();

    // private final Context mContext;
    private final String mName;
    private final String mDir;
    private final CursorFactory mFactory;
    private final int mNewVersion;

    private SQLiteDatabase mDatabase = null;
    private boolean mIsInitializing = false;

    /**
     * Create a helper object to create, open, and/or manage a database. This
     * method always returns very quickly. The database is not actually created
     * or opened until one of {@link #getWritableDatabase} or
     * {@link #getReadableDatabase} is called.
     * 
     * @param dir
     *            the directory on the SD card. It must exist and the SD card
     *            must be available. The caller should check this.
     * @param name
     *            of the database file, or null for an in-memory database
     * @param factory
     *            to use for creating cursor objects, or null for the default
     * @param version
     *            number of the database (starting at 1); if the database is
     *            older, {@link #onUpgrade} will be used to upgrade the
     *            database; if the database is newer, {@link #onDowngrade} will
     *            be used to downgrade the database
     */
    public SDCardSQLiteOpenHelper(String dir, String name,
            CursorFactory factory, int version) {
        if (version < 1)
            throw new IllegalArgumentException("Version must be >= 1, was "
                    + version);
        // mContext = context;
        mDir = dir;
        mName = name;
        mFactory = factory;
        mNewVersion = version;
    }

    /**
     * Return the name of the SQLite database being opened, as given to the
     * constructor.
     */
    public String getDatabaseName() {
        return mName;
    }

    /**
     * Create and/or open a database that will be used for reading and writing.
     * The first time this is called, the database will be opened and
     * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
     * called.
     * 
     * <p>
     * Once opened successfully, the database is cached, so you can call this
     * method every time you need to write to the database. (Make sure to call
     * {@link #close} when you no longer need the database.) Errors such as bad
     * permissions or a full disk may cause this method to fail, but future
     * attempts may succeed if the problem is fixed.
     * </p>
     * 
     * <p class="caution">
     * Database upgrade may take a long time, you should not call this method
     * from the application main thread, including from
     * {@link android.content.ContentProvider#onCreate
     * ContentProvider.onCreate()}.
     * 
     * @throws SQLiteException
     *             if the database cannot be opened for writing
     * @return a read/write database object valid until {@link #close} is called
     */
    public synchronized SQLiteDatabase getWritableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling
                // mDatabase.close()
                mDatabase = null;
            } else if (!mDatabase.isReadOnly()) {
                return mDatabase; // The database is already open for business
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException(
                    "getWritableDatabase called recursively");
        }

        // If we have a read-only database open, someone could be using it
        // (though they shouldn't), which would cause a lock to be held on
        // the file, and our attempts to open the database read-write would
        // fail waiting for the file lock. To prevent that, we acquire the
        // lock on the read-only database, which shuts out other users.

        boolean success = false;
        SQLiteDatabase db = null;
        // NOT AVAILABLE
        // if (mDatabase != null) {
        // mDatabase.lock();
        // }
        try {
            mIsInitializing = true;
            if (mName == null) {
                db = SQLiteDatabase.create(null);
            } else {
                String path = mDir + "/" + mName;
                // db = mContext.openOrCreateDatabase(mName, 0, mFactory,
                // mErrorHandler);
                db = SQLiteDatabase.openDatabase(path, null,
                        SQLiteDatabase.CREATE_IF_NECESSARY);
            }

            int version = db.getVersion();
            if (version != mNewVersion) {
                db.beginTransaction();
                try {
                    if (version == 0) {
                        onCreate(db);
                    } else {
                        if (version > mNewVersion) {
                            onDowngrade(db, version, mNewVersion);
                        } else {
                            onUpgrade(db, version, mNewVersion);
                        }
                    }
                    db.setVersion(mNewVersion);
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }
            }

            onOpen(db);
            success = true;
            return db;
        } finally {
            mIsInitializing = false;
            if (success) {
                if (mDatabase != null) {
                    try {
                        mDatabase.close();
                    } catch (Exception e) {
                        // Do nothing
                    }
                    // NOT AVAILABLE
                    // mDatabase.unlock();
                }
                mDatabase = db;
            } else {
                // NOT AVAILABLE
                // if (mDatabase != null) {
                // mDatabase.unlock();
                // }
                if (db != null)
                    db.close();
            }
        }
    }

    /**
     * Create and/or open a database. This will be the same object returned by
     * {@link #getWritableDatabase} unless some problem, such as a full disk,
     * requires the database to be opened read-only. In that case, a read-only
     * database object will be returned. If the problem is fixed, a future call
     * to {@link #getWritableDatabase} may succeed, in which case the read-only
     * database object will be closed and the read/write object will be returned
     * in the future.
     * 
     * <p class="caution">
     * Like {@link #getWritableDatabase}, this method may take a long time to
     * return, so you should not call it from the application main thread,
     * including from {@link android.content.ContentProvider#onCreate
     * ContentProvider.onCreate()}.
     * 
     * @throws SQLiteException
     *             if the database cannot be opened
     * @return a database object valid until {@link #getWritableDatabase} or
     *         {@link #close} is called.
     */
    public synchronized SQLiteDatabase getReadableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling
                // mDatabase.close()
                mDatabase = null;
            } else {
                return mDatabase; // The database is already open for business
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException(
                    "getReadableDatabase called recursively");
        }

        try {
            return getWritableDatabase();
        } catch (SQLiteException e) {
            if (mName == null)
                throw e; // Can't open a temp database read-only!
            Log.e(TAG, "Couldn't open " + mName
                    + " for writing (will try read-only):", e);
        }

        SQLiteDatabase db = null;
        try {
            mIsInitializing = true;
            // String path = mContext.getDatabasePath(mName).getPath();
            String path = mDir + "/" + mName;

            db = SQLiteDatabase.openDatabase(path, mFactory,
                    SQLiteDatabase.OPEN_READONLY);
            if (db.getVersion() != mNewVersion) {
                throw new SQLiteException(
                        "Can't upgrade read-only database from version "
                                + db.getVersion() + " to " + mNewVersion + ": "
                                + path);
            }

            onOpen(db);
            Log.w(TAG, "Opened " + mName + " in read-only mode");
            mDatabase = db;
            return mDatabase;
        } finally {
            mIsInitializing = false;
            if (db != null && db != mDatabase)
                db.close();
        }
    }

    /**
     * Close any open database object.
     */
    public synchronized void close() {
        if (mIsInitializing)
            throw new IllegalStateException("Closed during initialization");

        if (mDatabase != null && mDatabase.isOpen()) {
            mDatabase.close();
            mDatabase = null;
        }
    }

    /**
     * Called when the database is created for the first time. This is where the
     * creation of tables and the initial population of the tables should
     * happen.
     * 
     * @param db
     *            The database.
     */
    public abstract void onCreate(SQLiteDatabase db);

    /**
     * Called when the database needs to be upgraded. The implementation should
     * use this method to drop tables, add tables, or do anything else it needs
     * to upgrade to the new schema version.
     * 
     * <p>
     * The SQLite ALTER TABLE documentation can be found <a
     * href="http://sqlite.org/lang_altertable.html">here</a>. If you add new
     * columns you can use ALTER TABLE to insert them into a live table. If you
     * rename or remove columns you can use ALTER TABLE to rename the old table,
     * then create the new table and then populate the new table with the
     * contents of the old table.
     * 
     * @param db
     *            The database.
     * @param oldVersion
     *            The old database version.
     * @param newVersion
     *            The new database version.
     */
    public abstract void onUpgrade(SQLiteDatabase db, int oldVersion,
            int newVersion);

    /**
     * Called when the database needs to be downgraded. This is stricly similar
     * to onUpgrade() method, but is called whenever current version is newer
     * than requested one. However, this method is not abstract, so it is not
     * mandatory for a customer to implement it. If not overridden, default
     * implementation will reject downgrade and throws SQLiteException
     * 
     * @param db
     *            The database.
     * @param oldVersion
     *            The old database version.
     * @param newVersion
     *            The new database version.
     */
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        throw new SQLiteException("Can't downgrade database from version "
                + oldVersion + " to " + newVersion);
    }

    /**
     * Called when the database has been opened. The implementation should check
     * {@link SQLiteDatabase#isReadOnly} before updating the database.
     * 
     * @param db
     *            The database.
     */
    public void onOpen(SQLiteDatabase db) {
    }
}

这是当上面由 Roger Keays 描述的方法在 Android 4.0.3 上停止工作时完成的。

Rewriting SQLOpenHelper to use the SD card directory rather than the context and then extending that seems to work for me.

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteException;
import android.util.Log;

/**
 * SDCardSQLiteOpenhelper is a class that is based on SQLiteOpenHelper except
 * that it does not use the context to get the database. It was written owing to
 * a bug in Android 4.0.3 so that using a ContextWrapper to override
 * openOrCreateDatabase, as was done with Android 2.3.3, no longer worked. <br>
 * <br>
 * The mContext field has been replaced by mDir. It does not use lock on the
 * database as that method is package private to
 * android.database.sqlite.SQLiteDatabase. Otherwise the implementation is
 * similar.<br>
 * <br>
 * 
 * @see android.database.sqlite.SQLiteOpenHelper
 */
public abstract class SDCardSQLiteOpenHelper {
    private static final String TAG = SDCardSQLiteOpenHelper.class
            .getSimpleName();

    // private final Context mContext;
    private final String mName;
    private final String mDir;
    private final CursorFactory mFactory;
    private final int mNewVersion;

    private SQLiteDatabase mDatabase = null;
    private boolean mIsInitializing = false;

    /**
     * Create a helper object to create, open, and/or manage a database. This
     * method always returns very quickly. The database is not actually created
     * or opened until one of {@link #getWritableDatabase} or
     * {@link #getReadableDatabase} is called.
     * 
     * @param dir
     *            the directory on the SD card. It must exist and the SD card
     *            must be available. The caller should check this.
     * @param name
     *            of the database file, or null for an in-memory database
     * @param factory
     *            to use for creating cursor objects, or null for the default
     * @param version
     *            number of the database (starting at 1); if the database is
     *            older, {@link #onUpgrade} will be used to upgrade the
     *            database; if the database is newer, {@link #onDowngrade} will
     *            be used to downgrade the database
     */
    public SDCardSQLiteOpenHelper(String dir, String name,
            CursorFactory factory, int version) {
        if (version < 1)
            throw new IllegalArgumentException("Version must be >= 1, was "
                    + version);
        // mContext = context;
        mDir = dir;
        mName = name;
        mFactory = factory;
        mNewVersion = version;
    }

    /**
     * Return the name of the SQLite database being opened, as given to the
     * constructor.
     */
    public String getDatabaseName() {
        return mName;
    }

    /**
     * Create and/or open a database that will be used for reading and writing.
     * The first time this is called, the database will be opened and
     * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
     * called.
     * 
     * <p>
     * Once opened successfully, the database is cached, so you can call this
     * method every time you need to write to the database. (Make sure to call
     * {@link #close} when you no longer need the database.) Errors such as bad
     * permissions or a full disk may cause this method to fail, but future
     * attempts may succeed if the problem is fixed.
     * </p>
     * 
     * <p class="caution">
     * Database upgrade may take a long time, you should not call this method
     * from the application main thread, including from
     * {@link android.content.ContentProvider#onCreate
     * ContentProvider.onCreate()}.
     * 
     * @throws SQLiteException
     *             if the database cannot be opened for writing
     * @return a read/write database object valid until {@link #close} is called
     */
    public synchronized SQLiteDatabase getWritableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling
                // mDatabase.close()
                mDatabase = null;
            } else if (!mDatabase.isReadOnly()) {
                return mDatabase; // The database is already open for business
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException(
                    "getWritableDatabase called recursively");
        }

        // If we have a read-only database open, someone could be using it
        // (though they shouldn't), which would cause a lock to be held on
        // the file, and our attempts to open the database read-write would
        // fail waiting for the file lock. To prevent that, we acquire the
        // lock on the read-only database, which shuts out other users.

        boolean success = false;
        SQLiteDatabase db = null;
        // NOT AVAILABLE
        // if (mDatabase != null) {
        // mDatabase.lock();
        // }
        try {
            mIsInitializing = true;
            if (mName == null) {
                db = SQLiteDatabase.create(null);
            } else {
                String path = mDir + "/" + mName;
                // db = mContext.openOrCreateDatabase(mName, 0, mFactory,
                // mErrorHandler);
                db = SQLiteDatabase.openDatabase(path, null,
                        SQLiteDatabase.CREATE_IF_NECESSARY);
            }

            int version = db.getVersion();
            if (version != mNewVersion) {
                db.beginTransaction();
                try {
                    if (version == 0) {
                        onCreate(db);
                    } else {
                        if (version > mNewVersion) {
                            onDowngrade(db, version, mNewVersion);
                        } else {
                            onUpgrade(db, version, mNewVersion);
                        }
                    }
                    db.setVersion(mNewVersion);
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }
            }

            onOpen(db);
            success = true;
            return db;
        } finally {
            mIsInitializing = false;
            if (success) {
                if (mDatabase != null) {
                    try {
                        mDatabase.close();
                    } catch (Exception e) {
                        // Do nothing
                    }
                    // NOT AVAILABLE
                    // mDatabase.unlock();
                }
                mDatabase = db;
            } else {
                // NOT AVAILABLE
                // if (mDatabase != null) {
                // mDatabase.unlock();
                // }
                if (db != null)
                    db.close();
            }
        }
    }

    /**
     * Create and/or open a database. This will be the same object returned by
     * {@link #getWritableDatabase} unless some problem, such as a full disk,
     * requires the database to be opened read-only. In that case, a read-only
     * database object will be returned. If the problem is fixed, a future call
     * to {@link #getWritableDatabase} may succeed, in which case the read-only
     * database object will be closed and the read/write object will be returned
     * in the future.
     * 
     * <p class="caution">
     * Like {@link #getWritableDatabase}, this method may take a long time to
     * return, so you should not call it from the application main thread,
     * including from {@link android.content.ContentProvider#onCreate
     * ContentProvider.onCreate()}.
     * 
     * @throws SQLiteException
     *             if the database cannot be opened
     * @return a database object valid until {@link #getWritableDatabase} or
     *         {@link #close} is called.
     */
    public synchronized SQLiteDatabase getReadableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling
                // mDatabase.close()
                mDatabase = null;
            } else {
                return mDatabase; // The database is already open for business
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException(
                    "getReadableDatabase called recursively");
        }

        try {
            return getWritableDatabase();
        } catch (SQLiteException e) {
            if (mName == null)
                throw e; // Can't open a temp database read-only!
            Log.e(TAG, "Couldn't open " + mName
                    + " for writing (will try read-only):", e);
        }

        SQLiteDatabase db = null;
        try {
            mIsInitializing = true;
            // String path = mContext.getDatabasePath(mName).getPath();
            String path = mDir + "/" + mName;

            db = SQLiteDatabase.openDatabase(path, mFactory,
                    SQLiteDatabase.OPEN_READONLY);
            if (db.getVersion() != mNewVersion) {
                throw new SQLiteException(
                        "Can't upgrade read-only database from version "
                                + db.getVersion() + " to " + mNewVersion + ": "
                                + path);
            }

            onOpen(db);
            Log.w(TAG, "Opened " + mName + " in read-only mode");
            mDatabase = db;
            return mDatabase;
        } finally {
            mIsInitializing = false;
            if (db != null && db != mDatabase)
                db.close();
        }
    }

    /**
     * Close any open database object.
     */
    public synchronized void close() {
        if (mIsInitializing)
            throw new IllegalStateException("Closed during initialization");

        if (mDatabase != null && mDatabase.isOpen()) {
            mDatabase.close();
            mDatabase = null;
        }
    }

    /**
     * Called when the database is created for the first time. This is where the
     * creation of tables and the initial population of the tables should
     * happen.
     * 
     * @param db
     *            The database.
     */
    public abstract void onCreate(SQLiteDatabase db);

    /**
     * Called when the database needs to be upgraded. The implementation should
     * use this method to drop tables, add tables, or do anything else it needs
     * to upgrade to the new schema version.
     * 
     * <p>
     * The SQLite ALTER TABLE documentation can be found <a
     * href="http://sqlite.org/lang_altertable.html">here</a>. If you add new
     * columns you can use ALTER TABLE to insert them into a live table. If you
     * rename or remove columns you can use ALTER TABLE to rename the old table,
     * then create the new table and then populate the new table with the
     * contents of the old table.
     * 
     * @param db
     *            The database.
     * @param oldVersion
     *            The old database version.
     * @param newVersion
     *            The new database version.
     */
    public abstract void onUpgrade(SQLiteDatabase db, int oldVersion,
            int newVersion);

    /**
     * Called when the database needs to be downgraded. This is stricly similar
     * to onUpgrade() method, but is called whenever current version is newer
     * than requested one. However, this method is not abstract, so it is not
     * mandatory for a customer to implement it. If not overridden, default
     * implementation will reject downgrade and throws SQLiteException
     * 
     * @param db
     *            The database.
     * @param oldVersion
     *            The old database version.
     * @param newVersion
     *            The new database version.
     */
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        throw new SQLiteException("Can't downgrade database from version "
                + oldVersion + " to " + newVersion);
    }

    /**
     * Called when the database has been opened. The implementation should check
     * {@link SQLiteDatabase#isReadOnly} before updating the database.
     * 
     * @param db
     *            The database.
     */
    public void onOpen(SQLiteDatabase db) {
    }
}

This was done when the method described above by Roger Keays stopped working on Android 4.0.3.

是伱的 2024-11-09 09:13:29

这段代码解决了我的类似问题,我的应用程序类:

@Override
public File getDatabasePath(String name) {
    File result = new File(getExternalFilesDir(null), name);
    return result;
}

@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
    return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory);
}

希望它会对您有所帮助。

This code fixed my similar problem, my application class:

@Override
public File getDatabasePath(String name) {
    File result = new File(getExternalFilesDir(null), name);
    return result;
}

@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
    return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory);
}

Hope it will help you.

请恋爱 2024-11-09 09:13:29

好吧,我想你不能那样做。如果有人知道方法,请告诉我们如何做。

因此,当您调用时,

mySqliteOpenHelper.getReadableDatabase();

一切都应该没问题,就像我们查看 实现 我们看到:

 String path = mContext.getDatabasePath(mName).getPath();

一切都很好。但是如果我们看一下几行:

return getWritableDatabase();

所以它实际上是在调用另一个方法,如果失败,只有然后它才会继续使用 getDatabasePath()。
如果我们看一下 getWritableDatabase 的实现 - 我们可以清楚地看到它没有使用 getDatabasePath 而是:

db = mContext.openOrCreateDatabase(mName, 0, mFactory);

这让我们看到 openOrCreateDatabase 是如何实现的,我们将看一下 ContextImpl.java

 if (name.charAt(0) == File.separatorChar) {
            String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar));
            dir = new File(dirPath);
            name = name.substring(name.lastIndexOf(File.separatorChar));
            f = new File(dir, name);
        } else {
            dir = getDatabasesDir();
            f = makeFilename(dir, name);
        }

因此,我们可以看到,如果此帮助器方法 validateFilePath 获取完整路径(如 /some/truly/full/path)或尝试将 getDatabasesDir() 与文件名连接,则它返回 File 。
getDatabasesDir() 实现使用 getDataDirFile() ,它是公共的,理论上可能会被覆盖..但您必须检查。

目前我看到两种解决方案:

1)如果您不需要写入访问强制 sqlite db 进入只读模式,则 getWritableDatabase 将失败并且 getDatabasePath 将被调用
2)将完整路径传递到 SQLiteOpenHelper 构造函数中,并确保 db 可写,如下所示:

public class MyDbOpenHelper extends SQLiteOpenHelper {

    public MyDbOpenHelper(final Context context) {
        super(context, Environment.getExternalStorageDirectory()
                + "/path/to/database/on/sdcard/database.sqlite", null, 1);
    }

这对我来说确实没有意义,但查看 android 源代码(至少 2.3.1),似乎这就是它的实现方式。

Well, i guess you cannot do that. If anyone knows a way, please tell us how.

So when you are calling

mySqliteOpenHelper.getReadableDatabase();

It should all be ok as if we look at the implementation we see that:

 String path = mContext.getDatabasePath(mName).getPath();

All good. But if we take a look few lines up:

return getWritableDatabase();

So it is actually calling another method, and if it fails, only then it procedes to use getDatabasePath().
If wee look at the implementation of getWritableDatabase - we can clearly see that it does not use getDatabasePath but instead:

db = mContext.openOrCreateDatabase(mName, 0, mFactory);

This brings us to see how openOrCreateDatabase is implemented for that we'll take a look at ContextImpl.java

 if (name.charAt(0) == File.separatorChar) {
            String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar));
            dir = new File(dirPath);
            name = name.substring(name.lastIndexOf(File.separatorChar));
            f = new File(dir, name);
        } else {
            dir = getDatabasesDir();
            f = makeFilename(dir, name);
        }

So we can see that this helper method validateFilePath returns File if it gets a full path (like /some/truly/full/path) or tries to concat getDatabasesDir() with filename.
getDatabasesDir() implementation uses getDataDirFile() which is public and maybe in theory could be overwritten .. but you would have to check.

Currently i see two solutions:

1) If you don't need write access force sqlite db into readonly mode, getWritableDatabase will fail and getDatabasePath will get called
2) Pass in full path into SQLiteOpenHelper constructor, and make sure db is writable, something like:

public class MyDbOpenHelper extends SQLiteOpenHelper {

    public MyDbOpenHelper(final Context context) {
        super(context, Environment.getExternalStorageDirectory()
                + "/path/to/database/on/sdcard/database.sqlite", null, 1);
    }

This truly makes no sense to me, but looking at android sources (at least 2.3.1) it seems this is the way it is implemented.

π浅易 2024-11-09 09:13:29

调用此函数将调用 SqliteOpen 帮助程序类中的 onCreate 方法

    public dbOperation open() throws SQLException 
    {
        db = DBHelper.getWritableDatabase();
        return this;
    }

oncreate 方法如下所示

       public void onCreate(SQLiteDatabase db) 
        {
            try {
                db.execSQL(DATABASE_CREATE);

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

DATABASE_CREATE 是包含用于创建数据库的查询的字符串

Calling this function will invoke the onCreate method in the SqliteOpen helper class

    public dbOperation open() throws SQLException 
    {
        db = DBHelper.getWritableDatabase();
        return this;
    }

The oncreate method is like this

       public void onCreate(SQLiteDatabase db) 
        {
            try {
                db.execSQL(DATABASE_CREATE);

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

DATABASE_CREATE is the string which contain the query for creating database

反差帅 2024-11-09 09:13:29

您的数据库保存在其内部存储器中,以便其他应用程序无法访问它并更改/损坏数据。

android数据库的默认路径是 /data/data/APPLICATIONPACKAGENAME/databases/ 。以下是关于如何将数据库存储在文件中然后在运行时填充它的很好的指南。

文章

Your database is kept in its internal memory so that other applications can't access it and change/corrupt data.

The default path of the android database is /data/data/APPLICATIONPACKAGENAME/databases/ . The following is a pretty good guide on how to store your database in a file and then populate it at run time.

Article

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文