use External database in android - android

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 */
}
}

Related

SQLCipher Android is not throwing an exception with incorrect password

I am using SQLCipher v3.5.7 and observed an unexpected behavior from SQLiteDatabase with incorrect password.
I encrypted the database with "key1".
Closed the database connection.
Then I tried to open my database with "key2", the SQLiteDatabase is not throwing an exception. Instead, it is updating the old password (key1) to new password (key2). I verified this by opening the .db file in SQLiteBrowser.
Can somebody help me why it is behaving this way?
private static SQLiteCipherDatabaseHelper createDBConnection(Context context, String databasePath, final String key) throws SQLiteDatabaseException {
if (dbInstance == null) {
dbInstance = new SQLiteCipherDatabaseHelper(context, databasePath);
String path = context.getDatabasePath(databasePath).getPath();
File dbPathFile = new File(path);
if (!dbPathFile.exists()) {
dbPathFile.getParentFile().mkdirs();
}
setDatabaseWithDBEncryption(key);
}
return dbInstance;
}
private static void setDatabaseWithDBEncryption(String encryptionKey) throws SQLiteDatabaseException {
loadSQLCipherLibs();
try {
sqliteDatabase = SQLiteDatabase.openOrCreateDatabase(new File(context.getDatabasePath(databasePath).getPath()), encryptionKey, null);
} catch (Exception e) {
SyncLogger.getSharedInstance().logFatal("SQLiteCipherDatabaseHelper", "Failed to open or create database. Please provide a valid encryption key");
throw new SQLiteDatabaseException(SyncErrorCodes.EC_DB_SQLCIPHER_FAILED_TO_OPEN_OR_CREATE_DATABASE, SyncErrorDomains.ED_OFFLINE_OBJECTS, SyncErrorMessages.EM_DB_SQLCIPHER_FAILED_TO_OPEN_OR_CREATE_DATABASE, e);
}
}
Have you upgrade your db version ??
private static final int DATABASE_VERSION = 2;//from 1 to 2
private static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) // constructor
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
//Changes in db mentioned here
}
}
Are you actually populating the database with tables and data after keying it? It seems most likely that for some reason you are recreating the database each time you run the test. Have you verified that the actual database is encrypted by pulling it off the device and examining the file? Perhaps you are recreating a new database each time you run the test, in which case the new key would just be used.
It's worth noting that this behavior is covered in the SQLCipher for Android Test project.
https://github.com/sqlcipher/sqlcipher-android-tests/blob/master/src/main/java/net/zetetic/tests/InvalidPasswordTest.java
If you suspect an issue you can try running the test suite on your device, or create a new test case to verify the behavior with your own code.

SQLiteOpenHelper multiple in-memory databases

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.

moving from android sqlcipher to android sqlite database

From the last 3 days i am trying to upgrade my database to a higher version of SQLCipher library (v3.1.0). I did every step and followed a few tutorials too. But keep on getting the error "File is encrypted or not a Database". Now am trying to move to unencrypted database ie. simple sqlite database.
Do we have a way to move to encrypted database to un-encrypted database? Thanks in advance.
This is the code i am working on:
public MyDBAdapter(Context context) {
this.context = context;
File dbFile = context.getDatabasePath(DATABASE_NAME);
String dbPath = context.getDatabasePath(DATABASE_NAME).toString();
if (dbFile.exists()) {
try {
SQLiteDatabase.loadLibs(context.getApplicationContext());//load SqlCipher libraries
SQLiteDatabase db = getExistDataBaseFile(dbPath, KEY_PASSPHRASE_ENCRYPTION, dbFile);
if (version == 1) {
MigrateDatabaseFrom1xFormatToCurrentFormat(
dbFile, KEY_PASSPHRASE_ENCRYPTION);
}
System.out.println("Old Database found and updated.");
} catch (Exception e) {
System.out.println("No Old Database found");
}
}
this.dbhelper = new MyDBHelper(this.context, DATABASE_NAME, null,
DATABASE_Version);
db = dbhelper.getWritableDatabase(KEY_PASSPHRASE_ENCRYPTION);
}
private SQLiteDatabase getExistDataBaseFile(String FULL_DB_Path, String password, File dbFile) {// this function to open an Exist database
SQLiteDatabase.loadLibs(context.getApplicationContext());
SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
public void preKey(SQLiteDatabase database) {
System.out.println("-----Inside preKey");
}
public void postKey(SQLiteDatabase database) {
System.out.println("-----Inside postKey");
database.rawExecSQL("PRAGMA cipher_migrate;");
}
};
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(
dbFile, "Test123", null, hook); // Exception
return database;
}
If you are upgrading your SQLCipher library to the latest version, currently at 3.1.0, and your previous version was 2.x (as you mentioned in the comments above), you will need to upgrade the database file format as well. One of the big changes in the 3.x release was an increase in key derivation length, from 4,000 to 64,000. If you are using all of the standard SQLCipher configurations, upgrading the database format is straight forward. We have included a new PRAGMA call cipher_migrate that will perform this operation for you. You can execute this within the postKey event of the SQLiteDatabaseHook which is to be provided in your call to SQLiteDatabase.openOrCreateDatabase. An example of this can be found in the SQLCipher for Android test suite here.

SQLite database created on Android and edited with Firefox SQlite manager

I initially created an SQLite database on Windows and then had problems accessing it within Android.
Subsequently I created a database on Android and then copied it out. At this point it only had the android_metadata table in it.
I then imported some data via CSV and added it back into my project. The DbHelper class in my project copies the database into /data/data/my.project/databases/.
Now, when I run a raw query from this database, if I try to access the table imported by CSV, I get an error saying that the table doesn't exist. If I try to access the android_metadata table which I created on Android then there is no error.
The database in my assets definitely has the table in that I wish to copy over to the /data/data/example.project/databases folder and the copy routine is definitely called - I've checked with the log output.
Now, if I comment out the copy code, a database is automatically created which contains the android_metadata table in there and it is ~3 KB.
When the copy code is live the database is created as ~8 KB. This is the size of the database in the assets, so it appears that it has been successfully copied. However, when I pull that database back to my desktop from DDMS it is ~8 KB, but it doesn't contain the table which is in the one in the assets folder. If I manually copy directly from desktop into /data/data... then the database works (but this will not be possible with a market app).
Here is my copy code for copying the database:
public void createDatabase() throws IOException {
Log.i(TAG, "createDatabase called");
InputStream assetsDB = mContext.getAssets().open(DATABASE_NAME);
OutputStream dbOut = new FileOutputStream(DATABASE_PATH);
Log.i(TAG, DATABASE_PATH);
Log.i(TAG, assetsDB.toString());
byte[] buffer = new byte[1024];
int length;
while ((length = assetsDB.read(buffer))>0) {
Log.i(TAG, "WritingDB block" + length);
dbOut.write(buffer, 0, length);
}
dbOut.flush();
dbOut.close();
assetsDB.close();
}
How can I fix this problem?
I've rectified this using another example which doesn't override onCreate with the database copy code and handles the copying of the database on its own. I don't really understand why it doesn't work when calling the onCreate method.
Have you seen Using your own SQLite database in Android applications?
This page is a good source for the topic. But, there is a little problem. Actually, it is not a problem and explained how to fix in the page. Look at comments.
If your database is sort of largish or smallish (> 1 MB, < 100 KB (I am not sure about these values)). It seems that it is compressed and that causes confusion in the Android read on the InputStream. The trick is to rename your asset to a file that the packager will not try to compress. Renaming the database file from xxx to xxx.mp3 or xxx.txt or something like that does the trick.
If I clearly understand you, I had the same problem with loading an SQLite database from other sources (I used to the Firefox SQLite manger too).
I want to read a temporary database from the assets folder at startup and fill my application database with test data, and I usually get this error.
I need to put this code before loading my test database:
final SQLiteDatabase db = getReadableDatabase();
db.close();
My database helper class:
public DataBaseHelper(Context context) {
super(context,
DataBaseHelper.DATABASE_NAME,
null,
DataBaseHelper.DATABASE_VERSION);
this.context = context;
// Temporary copy test database
loadMockDataBase();
dataBase = getWritableDatabase();
}
#Override
public final synchronized void close() {
if (dataBase != null) {
dataBase.close();
}
super.close();
}
private void loadMockDataBase() {
final SQLiteDatabase db = getReadableDatabase();
db.close();
try {
copyDataBase();
}
catch (final IOException e) {
Log.d(SystemConfiguration.LOG_TAG, e.getMessage());
}
}

SQLiteOpenHelper problem with fully qualified DB path name

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."

Categories

Resources