android.database.sqlite.SQLiteOpenHelper provides the ability to use an in-memory database if the name argument to its constructor is null:
String: of the database file, or null for an in-memory database
If SQLiteOpenHelper is instantiated multiple times with a null name argument, do they access the same in-memory database or is a separate in-memory database created each time?
From SQLite official documentation In-Memory Databases
Opening two database connections each with the filename ":memory:" will create two independent in-memory databases.
In Android, pass null instead of ":memory:"
So, If you instantiate SQLiteOpenHelper multiple times with a null name argument then it create separate in-memory database created each time
If we look at the source code, we see that in the constructor mName would get set to null.
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
DatabaseErrorHandler errorHandler) {
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
mContext = context;
mName = name;
mFactory = factory;
mNewVersion = version;
mErrorHandler = errorHandler;
}
Which means getDatabaseName() returns null.
public String getDatabaseName() {
return mName;
}
Later, through the use of getReadableDatabase() or getWritableDatabase(), if mName is null, then it calls the create method for an in-memory database instead of trying to opening one from disk.
if (mName == null) {
db = SQLiteDatabase.create(null); // in-memory
} else {
// db file opened or created
}
...
return db;
That db variable is maintained in the SQLiteOpenHelper until it is closed, which in the case of an in-memory database, means the data is deleted.
To clarify,
Each instance of a SQLiteOpenHelper that uses an in-memory database will its own database while the same instance will use one database and persist that data until it is closed.
Related
I have a SqlDelight database that is working nicely. I create it like this:
Database(AndroidSqliteDriver(Database.Schema, context, DatabaseName)
For unit-tests, I create an in-memory database like this:
Database(JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).apply {
Database.Schema.create(this)
})
I wanted to do the same for androidTests that run on the emulator or on a physical device, but JdbcSqliteDriver doesn't work in Android, presumably because Android doesn't have that package installed by default.
How do I run an in-memory database in AndroidTest (or in production)?
It turns out if you don't name your database, it creates an in-memory version:
Database(AndroidSqliteDriver(Database.Schema, context, null)
Because AndroidSqliteDriver uses SupportSQLiteOpenHelper.Builder which has this in the documentation:
/**
* #param name Name of the database file, or null for an in-memory database.
* #return This
*/
#NonNull
public Builder name(#Nullable String name) {
mName = name;
return this;
}
My app is currently using room database. I'm tying to migrate to use Sqlcipher data base. I have fallbackToDestructiveMigration() enabled but still throwing the following error
java.lang.RuntimeException: Exception while computing database live data.
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master;
at net.sqlcipher.database.SQLiteCompiledSql.native_compile(Native Method)
at net.sqlcipher.database.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:91)
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:64)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:91)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:2016)
at net.sqlcipher.database.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1902)
at net.sqlcipher.database.SQLiteDatabase.keyDatabase(SQLiteDatabase.java:2673)
at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2603)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1247)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1322)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:166)
at net.sqlcipher.database.SupportHelper.getWritableDatabase(SupportHelper.java:83)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at androidx.room.RoomDatabase.query(RoomDatabase.java:324)
at androidx.room.util.DBUtil.query(DBUtil.java:83)
at com.screenlocker.secure.room.MyDao_Impl$29.call(MyDao_Impl.java:1249)
at com.screenlocker.secure.room.MyDao_Impl$29.call(MyDao_Impl.java:1246)
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Is there any way to destroy all my database and move to Sqlcipher? i have also tried database.delete("table_name",null,null) command to manually deleting tables n migration but it is still not working. My code fro database creation is following.
DatabaseSecretProvider provider = new DatabaseSecretProvider(context);
byte[] passphrase = provider.getOrCreateDatabaseSecret().asBytes();
SupportFactory factory = new SupportFactory(passphrase);
instance = Room.databaseBuilder(context, MyAppDatabase.class, AppConstants.DATABASE_NAME)
.openHelperFactory(factory)
.fallbackToDestructiveMigration()
.build();
I'm using following version of Sqlcipher
implementation 'net.zetetic:android-database-sqlcipher:4.3.0'
implementation "androidx.sqlite:sqlite:2.1.0"
You can encrypt an unencrypted database with the sqlcipher_export convenience method from sqlcipher. So you don't have to do use fallbackToDestructiveMigration or spend time writing your custom migration tool.
From the developer's website:
To use sqlcipher_export() to encrypt an existing database, first open up the standard SQLite database, but don’t provide a key. Next, ATTACH a new encrypted database, and then call the sqlcipher_export() function in a SELECT statement, passing in the name of the attached database you want to write the main database schema and data to.
$ ./sqlcipher plaintext.db
sqlite> ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'newkey';
sqlite> SELECT sqlcipher_export('encrypted');
sqlite> DETACH DATABASE encrypted;
Finally, securely delete the existing plaintext database, and then open up the new encrypted database as usual using sqlite3_key or PRAGMA key.
Source: https://discuss.zetetic.net/t/how-to-encrypt-a-plaintext-sqlite-database-to-use-sqlcipher-and-avoid-file-is-encrypted-or-is-not-a-database-errors/868
#commonsguy also has an example of how to do this in Android:
/**
* Replaces this database with a version encrypted with the supplied
* passphrase, deleting the original. Do not call this while the database
* is open, which includes during any Room migrations.
*
* #param ctxt a Context
* #param originalFile a File pointing to the database
* #param passphrase the passphrase from the user
* #throws IOException
*/
public static void encrypt(Context ctxt, File originalFile, byte[] passphrase)
throws IOException {
SQLiteDatabase.loadLibs(ctxt);
if (originalFile.exists()) {
File newFile=File.createTempFile("sqlcipherutils", "tmp",
ctxt.getCacheDir());
SQLiteDatabase db=
SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(),
"", null, SQLiteDatabase.OPEN_READWRITE);
int version=db.getVersion();
db.close();
db=SQLiteDatabase.openDatabase(newFile.getAbsolutePath(), passphrase,
null, SQLiteDatabase.OPEN_READWRITE, null, null);
final SQLiteStatement st=db.compileStatement("ATTACH DATABASE ? AS plaintext KEY ''");
st.bindString(1, originalFile.getAbsolutePath());
st.execute();
db.rawExecSQL("SELECT sqlcipher_export('main', 'plaintext')");
db.rawExecSQL("DETACH DATABASE plaintext");
db.setVersion(version);
st.close();
db.close();
originalFile.delete();
newFile.renameTo(originalFile);
}
else {
throw new FileNotFoundException(originalFile.getAbsolutePath()+ " not found");
}
}
Source: https://github.com/commonsguy/cwac-saferoom/blob/v1.2.1/saferoom/src/main/java/com/commonsware/cwac/saferoom/SQLCipherUtils.java#L175-L224
You can pass context.getDatabasePath(DATABASE_NAME) as originalFile parameter.
Also, see this comment from commonsguy which explains how you can use this in combination with the getDatabaseState function to migrate your data from an existing plain-text database to a sqlcipher encrypted database.
This worked for me but I feel like its not the best answer:
val factory: SupportFactory = SupportFactory(masterKeyAlias.toByteArray())
private fun buildDatabase(context: Context) =
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"MyDatabaseNew.db" // <<--- change the name of this database file
).openHelperFactory(factory)
.build()
This is a brand new database and the data needs to be populated brand new. Maybe there is a way to migrate it in a migration.
It can be fixed as using SQLCipherUtils.encrypt method from thelib based on SQLCipherUtils Database state as mentioned below:
#Synchronized
fun getInstance(context: Context,key :String): AppDB? {
val state= SQLCipherUtils.getDatabaseState(context,
DB_NAME)
if (state == SQLCipherUtils.State.UNENCRYPTED) {
SQLCipherUtils.encrypt(
context,
DB_NAME,
Base64.decode(key, Base64.DEFAULT)
)
}
if (INSTANCE == null) {
val factory =
SafeHelperFactory(Base64.decode(key, Base64.DEFAULT))
INSTANCE = Room.databaseBuilder(
context.applicationContext,
AppDB::class.java, DB_NAME
)
.openHelperFactory(factory)
.build()
appContext = context.applicationContext
}
return INSTANCE
}
make sure to call SQLCipherUtils encrypt before DB opened
There is one ugly catch with Mad's answer. It crashes on some devices (Xiaomi for me) with "file is not a database". Apparently files created using File.createTempFile are not considered database on such devices. Don't ask me why, I simply don't know. So I've had to use standard new File call
So my implementation looks like this:
private fun encrypt(context: Context, originalFile: File, passphrase: ByteArray) {
SQLiteDatabase.loadLibs(context)
if (originalFile.exists()) {
val newFile = File(context.cacheDir, "sqlcipherutils.db")
if (!newFile.createNewFile()) {
throw FileNotFoundException(newFile.absolutePath.toString() + " not created")
}
// get database version from existing database
val databaseVersion = SQLiteDatabase.openDatabase(originalFile.absolutePath, "", null, SQLiteDatabase.OPEN_READWRITE).use { database ->
database.version
}
SQLiteDatabase.openDatabase(
newFile.absolutePath, passphrase, null,
SQLiteDatabase.OPEN_READWRITE, null, null
).use { temporaryDatabase ->
temporaryDatabase.rawExecSQL("ATTACH DATABASE '${originalFile.absolutePath}' AS sqlcipher4 KEY ''")
temporaryDatabase.rawExecSQL("SELECT sqlcipher_export('main', 'sqlcipher4')")
temporaryDatabase.rawExecSQL("DETACH DATABASE sqlcipher4")
temporaryDatabase.version = databaseVersion
}
originalFile.delete()
newFile.renameTo(originalFile)
} else {
throw FileNotFoundException(originalFile.absolutePath.toString() + " not found")
}
}
We try to update sqlite_sequence with the following code.
WeNoteRoomDatabase weNoteRoomDatabase = WeNoteRoomDatabase.instance();
weNoteRoomDatabase.query(new SimpleSQLiteQuery("UPDATE sqlite_sequence SET seq = 0 WHERE name = 'attachment'"));
However, it has no effect at all. I exam the sqlite_sequence table content using SQLite browser. The counter is not reset to 0.
If we try to run the same query manually using SQLite browser on same SQLite file, it works just fine.
Our Room database is pretty straightforward.
#Database(
entities = {Attachment.class},
version = 6
)
public abstract class WeNoteRoomDatabase extends RoomDatabase {
private volatile static WeNoteRoomDatabase INSTANCE;
private static final String NAME = "wenote";
public abstract AttachmentDao attachmentDao();
public static WeNoteRoomDatabase instance() {
if (INSTANCE == null) {
synchronized (WeNoteRoomDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
WeNoteApplication.instance(),
WeNoteRoomDatabase.class,
NAME
)
.build();
}
}
}
return INSTANCE;
}
}
Any idea what we had missed out?
Additional information : clearing sqlite_sequence is not working in android room
Room doesn't use SQLiteDatabase - but it uses SupportSQLiteDatabase, while it's source code states, that it delegates all calls to an implementation of {#link SQLiteDatabase}... I could even dig further - but I'm convinced, that this is a consistency feature and not a bug.
SQLiteDatabase.execSQL() still works fine, but with SupportSQLiteDatabase.execSQL() the same UPDATE or DELETE queries against internal tables have no effect and do not throw errors.
my MaintenanceHelper is available on GitHub. it is important that one initially lets Room create the database - then one can manipulate the internal tables with SQLiteDatabase.execSQL(). while researching I've came across annotation #SkipQueryVerification, which could possibly permit UPDATE or DELETE on table sqlite_sequence; I've only managed to perform a SELECT with Dao... which in general all hints for the internal tables are being treated as read-only, from the perspective of the publicly available API; all manipulation attempts are being silently ignored.
i think query is wrong, you should try below query
weNoteRoomDatabase.query(new SimpleSQLiteQuery("UPDATE sqlite_sequence SET seq = 0 WHERE name = attachment"));
I'm using room database version 2.2.5
Here I'm unable to execute this query using Room Dao structure, so make one simple class and access method as like this and I got successful outcomes so this one is tested result. I'm using RxJava and RxAndroid for same.
public class SqlHelper {
private static SqlHelper helper = null;
public static SqlHelper getInstance() {
if (helper == null) {
helper = new SqlHelper();
}
return helper;
}
public Completable resetSequence(Context context) {
return Completable.create(emitter -> {
try {
AppDatabase.getDatabase(context)
.getOpenHelper()
.getWritableDatabase()
.execSQL("DELETE FROM sqlite_sequence WHERE name='<YOUR_TABLE_NAME>'");
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
}
}
Execute:
SqlHelper.getInstance()
.resetQuizSequence(context)
.subscribeOn(Schedulers.io()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> {}, error -> {});
Table sql_sequence is not managed by Room, so you need to edit it using a SupportSQLiteDatabase.
Try this:
String sqlQuery = "DELETE FROM sqlite_sequence WHERE name='attachment'";
weNoteRoomDatabase().getOpenHelper().getWritableDatabase().execSQL(sqlQuery);
This works for me - Room 2.2.6
String sqlQuery = "DELETE FROM sqlite_sequence WHERE name='attachment'";
<YourDatabase>.getInstance(mContext).getOpenHelper().getWritableDatabase().execSQL(sqlQuery);
In my app, I use...
myFilesDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/Android/data/" + packageName + "/files");
myFilesDir.mkdirs();
This is fine and the resulting path is...
/mnt/sdcard/Android/data/com.mycompany.myApp/files
I need a SQLite DB which I want to store on the SD card so I extend SQLiteOpenHelper as follows...
public class myDbHelper extends SQLiteOpenHelper {
public myDbHelper(Context context, String name, CursorFactory factory, int version) {
// NOTE I prefix the full path of my files directory to 'name'
super(context, myFilesDir + "/" + name, factory, version);
}
#Override
public void onCreate(SQLiteDatabase db) {
// Create tables and populate with default data...
}
}
So far so good - the first time I call getReadableDatabase() or getWriteableDatabase() the empty DB is created on the SD card and onCreate() populates it.
So here's the problem - the app is in beta testing with maybe 5 or 6 people and, like me, they're running Android v2.2 and everything works fine. I have one tester, however, running v2.1 and when myDbHelper tries to create the DB on first use, it crashes with the following...
E/AndroidRuntime( 3941): Caused by: java.lang.IllegalArgumentException: File /nand/Android/data/com.mycompany.myApp/files/myApp-DB.db3 contains a path separator
E/AndroidRuntime( 3941): at android.app.ApplicationContext.makeFilename(ApplicationContext.java:1445)
E/AndroidRuntime( 3941): at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:473)
E/AndroidRuntime( 3941): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193)
E/AndroidRuntime( 3941): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98)
E/AndroidRuntime( 3941): at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:158)
The path for the files directory is an odd one ("/nand") as it's internal memory although not the phone's own internal memory - but it is the path returned by getExternalStorageDirectory() for this device.
I can see three possible answers...
Although acceptable on v2.2, specifying a fully qualified path for DB name isn't recommended and will fail on earlier versions
Fully qualified paths are acceptable for SD card storage but the "/nand" path is being interpreted as 'internal' and only relative paths are acceptable in this case
Something else which I'm missing completely
If any or all of the above apply I'd appreciate it if somebody could help with how I should approach this.
Thanks.
You can use the SQLiteOpenHelper with a custom path if you provide a custom ContextClass and if you have write access in the target directory.
public class DatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 3;
.....
DatabaseHelper(final Context context, String databaseName) {
super(new DatabaseContext(context), databaseName, null, DATABASE_VERSION);
}
}
And here is the custom DatabaseContext class that does all the magic:
class DatabaseContext extends ContextWrapper {
private static final String DEBUG_CONTEXT = "DatabaseContext";
public DatabaseContext(Context base) {
super(base);
}
#Override
public File getDatabasePath(String name) {
File sdcard = Environment.getExternalStorageDirectory();
String dbfile = sdcard.getAbsolutePath() + File.separator+ "databases" + File.separator + name;
if (!dbfile.endsWith(".db")) {
dbfile += ".db" ;
}
File result = new File(dbfile);
if (!result.getParentFile().exists()) {
result.getParentFile().mkdirs();
}
if (Log.isLoggable(DEBUG_CONTEXT, Log.WARN)) {
Log.w(DEBUG_CONTEXT, "getDatabasePath(" + name + ") = " + result.getAbsolutePath());
}
return result;
}
/* this version is called for android devices >= api-11. thank to #damccull for fixing this. */
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
return openOrCreateDatabase(name,mode, factory);
}
/* this version is called for android devices < api-11 */
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);
// SQLiteDatabase result = super.openOrCreateDatabase(name, mode, factory);
if (Log.isLoggable(DEBUG_CONTEXT, Log.WARN)) {
Log.w(DEBUG_CONTEXT, "openOrCreateDatabase(" + name + ",,) = " + result.getPath());
}
return result;
}
}
Update june 2012:
how does this work (#barry question):
Normal android apps have their local database files relative to the app folder. By using a customer context with overwritten getDatabasePath() the database is now relative to a different directory on the sd card.
Update feb 2015:
After replacing my old android-2.2 device with a new android-4.4 device I found out that my solution didn't work anymore.
Thanks to #damccull-s answer I was able to fix it. I have updated this answer so this should be a working example again.
Update may 2017:
Statistics: This aproach is used in more than 200 github projects
Historically, you have not been able to use paths with SQLiteOpenHelper. It only worked on simple filenames. I had not realized that they relaxed that restriction in Android 2.2.
If you wish to use databases on the SD card, and you wish to support Android 2.1 and earlier, you cannot use SQLiteOpenHelper.
Sorry!
k3b's answer is awesome. It got me working. However, on devices using API level 11 or higher, you may see it stop working. This is because a new version of the openOrCreateDatabase() method was added. It now contains the following signature:
openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags, DatabaseErrorHandler errorHandler)
This seems to be the method called by default on some devices with this method available.
In order to make this method work on these devices, you need to make the following alterations:
First, edit your existing method so that it simply returns the result of a call to the new method.
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode,
CursorFactory factory) {
return openOrCreateDatabase(name, mode, factory, null);
}
Second, add the new override with the following code.
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) {
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name).getAbsolutePath(),null,errorHandler);
return result;
}
This code is very similar to k3b's code, but note that SQLiteDatabase.openOrCreateDatabase takes a String instead of a File, and I've used the version of it that allows for a DatabaseErrorHandler object.
user2371653's answere is very nice.
but I found a issue:
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode,
CursorFactory factory) {
return openOrCreateDatabase(name, mode, factory, null);
}
this may cause crasd, if install your app at android 2.x
so we can modify it like this
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode,
CursorFactory factory) {
return super.openOrCreateDatabase(getDatabasePath(name).getAbsolutePath(), mode, factory);
}
because android 2.x does not has the api
openOrCreateDatabase(String name, int mode,
CursorFactory factory, DatabaseErrorHandler errorHandler)
in my opinion I found a better solution on this side here (SQLite database on SD card) and wanted to inform you. Notice the entry in the constructor.
public class TestDB extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "usertest.db";
private static final int DATABASE_VERSION = 1;
public TestDB (Context context){
super(context, context.getExternalFilesDir(null).getAbsolutePath() + "/" + DATABASE_NAME, null, DATABASE_VERSION );
}
...
}
Quote from the user website:
"It will create the database in the app's folder on the sdcard: /sdcard/Android/data/[your_package_name]/files. In that way the database will be seen as part of the app by android and removed automatically if the user uninstalls the app."
"I my app I have a large database and it will in most cases not fit on old phones internal memory, e.g. HTC Desire. It runs great on the sdcard, and most apps are "moved to sdcard" themselves anyway so don't worry about the database not being accessible, because the app won't be accessible it self."
I have created database AddressBookMaster.db using Sqlite Browser and also create table AddressBook in mentioned database. But now problem is that I want to use this .db file in my application so where should I put this db file and how to fetch in application?
Is there any example or tutorial which can help to me?
You can put your Database file in the SDcard also. Use following code to use the Database from the SDCard.
File dbfile = new File("/sdcard/Your_db_File.db" );
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbfile, null);
The information contained in Using your own SQLite database in Android applications should have exactly what you are looking for.
You should note that while this is not really difficult it is not just a set of trivial commands that would be done outside of your application.
The information contained in "Using your own SQLite database in Android applications" is very userful for U and If U have db with more than 1024 bytes size then first split db into small parts and copy that all small dbs in your application dirrectory .
Use android-sqlite-asset-helper, to fill (initialize) the database of your app on installation
If You want to change the default folder of the database to sdcard:
public class MyApplication extends Application {
#Override
public void onCreate() {
SQLiteOpenHelper helper = ..
SQLiteDatabase db = helper.getWritableDatabase()
}
#Override
public File getDatabasePath(String name) {
return new File(getDatabasePathString(name));
}
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
return super.openOrCreateDatabase(getDatabasePathString(name), mode, factory);
}
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
return super.openOrCreateDatabase(getDatabasePathString(name), mode, factory, errorHandler);
}
/*Put the default folder to store database of your application / or activity here */
public String getDatabasePathString(String name) {
return getExternalCacheDir()+ "/" + name+".db"; /* /storage/emulated/0/Android/data/com.my.app/cache */
}
}