Because #CommonsWare suggested (in the comments to this answer) using the Android SQLiteAssetHelper library, I decided not to use Using your own SQLite database in Android applications method (a popular SO answer) to copy my pre-populated database from my assets folder to the app database folder.
Following the Android SQLiteAssetHelper directions, I set up my project like this:
Since I am using gradle, my database was in src/main/assets/databases/test.db.zip.
I used the .zip extension because the directions said
Earlier versions of this library required the database asset to be
compressed within a ZIP archive. This is no longer a requirement, but
is still supported. Applications still targeting Gingerbread (API 10)
or lower should continue to provide a compressed archive to ensure
large database files are not corrupted during the packaging process.
and I want to support earlier versions of android.
My database class is similar to the following:
public class MyDatabase extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "test.db.zip";
private static final int DATABASE_VERSION = 1;
public MyDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
Again, I used the .zip extension for the DATABASE_NAME value because the directions said
...you must provide...a SQLite database inside the databases folder
whose file name matches the database name you provide in code
(including the file extension, if any)
However, when I do SQLiteDatabase db = getReadableDatabase(); I get a NullPointerException. What is the problem?
These SO questions and answers are not the same issue:
NullPointerException with Android SQLiteAssetHelper
SQLiteAssetHelper:Couldn't open database for writing (will try read-only)
SQLiteAssetHelper NullPointerException only on some devices
I set up the question to make the answer somewhat obvious, but here it is:
Contrary to what the Android SQLiteAssetHelper directions make it sound like (at the time of this writing, anyway. hopefully they will clarity them in the future), the DATABASE_NAME value should not include the .zip extension. Change your code to
private static final String DATABASE_NAME = "test.db"; // no .zip extension
but keep src/main/assets/databases/test.db.zip as it is, and everything should work.
Related
I am trying to use SQLiteAssetHelper and it requires me to create database on PC and copy it to assets/databases/databasename.db. I do that, but when I check on the device, nor the database, nor the databases folder is there under my app's data folder.
I did this based on a video, and the guy there doesn't do anything else to make the copy work.
So, are the files supposed to be automatically copied over to device?
If not, how can I copy the files?
So, are the files supposed to be automatically copied over to device? If not, how can I copy the files?
Yes it will BUT only when an attempt is made to access the database. Just instantiating the class that extends SQLiteAssetHelper will not copy the database and thus if that is all you do then the database will not be visible.
The steps when using SQliteAssetHelper to take should be :-
Create your project.
Amend the build.gradle to include implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:+' in the dependencies section.
Create the assets folder and then the databases folder in the assets folder and
Copy the database file into the databases folder.
Create the Database Helper class that extends SQliteAssetHelper.
Instantiate the Database Helper
Access the database (this is when the database is copied).
If when doing step 5 you use, as an example (based upon the information in the question) :-
public class DatabaseHelper extends SQLiteAssetHelper {
public static final String DATABASENAME = "databasename.db"; //<<<<<<<<< MUST match file name in assets/databases
public static final int DATABASEVERSION = 1;
public DatabaseHelper(Context context) {
super(context, DATABASENAME, null, DATABASEVERSION);
this.getWritableDatabase(); //<<<<< will force database access and thus copy database from assets
}
}
Then when doing 6 (instantiating the database helper), then 7 is done because this.getWritableDatabase(); accesses (implicitly opens) the database.
e.g.
public class MainActivity extends AppCompatActivity {
DatabaseHelper mDBHelper; //<<<<< declares the database helper
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDBHelper = new DatabaseHelper(this); // instantiates the database helper
}
}
The procedure you're explaining requires root access to the app storage on the device which is impossible on a normal non-rooted device due to security considerations. For this, you need a rooted device OR you could use emulator in Android Studio, that's much easier and user friendly approach:
Open emulator from Android Studio -> Settings -> Memory -> Internal Storage -> Others
A pop-up window will open. Click explore. You will get access to your app's internal storage so you can manage the database folder as well.
P.S.: Also, you might just package your app with the database table you have, just as with any other file, simply place it under your src/main/assets folder and access from code in runtime. Code samples are available in this thread : Android - Copy assets to internal storage (suggested by #Ezaldeen sahb)
Can anyone can create a database from SD card using Jellybean API 18?
but i should call this path like /storage/sdcard1. there are some many tutorials but does not work. Actually im using SQLiteOpenHelper.Some people says that if you create database from SD card, you dont need SQLiteOpenHelper.
Solution: You just have to modify the constructor in order to change it's path:
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
MySQLiteOpenHelper(Context context) {
super(context, "/mnt/sdcard/mydb.db", null, 0);
}
}
You can also get the folder string path and change it accordingly as you require. Ex: "/storage/emulated/0/folder/mydb.db"
Don't forget to add Write and Read Storage Permissions.
Try it, Hope it's helpful.
quick background - I have no real programming knowledge so think complete beginner!
I used the below guide to load a database from the assets folder and it worked fine
http://www.javahelps.com/2015/04/import-and-use-external-database-in.html.
I now want to adjust this slightly so it loads a different SQLite database which I have created using DB Browser for SQLite but it is throwing the following error:
Missing databases/Exercisesthree.db file (or .zip, .gz archive) in assets, or target folder not writable
I have placed the new database (Excercisesthree.db.zip) in the same assets folder as the database that works (I have also left the unzipped file there too (Excercisesthree.db).
I altered the DatabaseOpenHelper class to open the Excercisesthree.db instead of the other db (code below) but for some reason it doesnt work. I suspect its something to do with the format of the database as I've hardly changed the code
package com.example.mat.externaldatabasedemo;
/**
* Created by Mat on 24-Jan-17.
*/
// http://www.javahelps.com/2015/04/import-and-use-external-database-in.html
import android.content.Context;
import com.readystatesoftware.sqliteasset.SQLiteAssetHelper;
public class DatabaseOpenHelper extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "Exercisesthree.db";
private static final int DATABASE_VERSION = 1;
public DatabaseOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
Here's a link to the database file:
https://1drv.ms/u/s!AupGvbDoJdp9i26l9oO5CcCGf2I0
This is the location of the database files:
C:\Users\Mat\AndroidStudioProjects\ExternalDatabaseDemo\app\src\main\assets\databases
Ok I've sorted it and confirm it was nothing to do with the Database format. In the end I created a new project and followed the original guide but changed it to the new database.
I think what must of happened is I'd changed some part of the code somewhere along the way and it stopped working. When testing it however it appeared to be working because the original databse had already been loaded onto the emulator and so it masked the fact that the database was no longer loading from the assets folder.
I would have like to have found out exactly what part of the code was missing and stopping it to work to understand a little more and report back but dont think I'll have time
I have developed an android app that accesses local sqlite database inside the app.
I have a question of how to upgrade the app. In this case, say if an application source code changes OR the database data/structure changes, how do I package these changes?
do i have to repackage complete app and make it available as a new app version ? or can I just package only the changed files into a much smaller build size and push it to market ?
In general, how is the application upgrade/update process carried out ?
If you're extending SQLiteOpenHelper (you should be), its constructor takes a version parameter, which is an integer value you should define in a constant like DATABASE_VERSION somewhere.
Override the onUpgrade method and implement the logic for upgrading your schema and/or data.
When you increment the DATABASE_VERSION constant (in an app update for instance), any existing installs that have a lower version will fire the code in onUpgrade.
Change DATABASE_VERSION of your db and next time you will start the app
the db will be recreated.
For both, you basically just export a new version of your application from Eclipse, and upload the new APK through your market account.
Application version changes are handled by a tag in the manifest: android:versionCode. Each time you export a updated APK, you need to increment the version code. versionName is just the label that's displayed to users in the market, so can be whatever you like.
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.package"
android:installLocation="auto"
android:versionCode="17"
android:versionName="0.74">
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="12" />
<uses-permission
android:name="android.permission.INTERNET"></uses-permission>
...
As for the database, the easiest way to do it is to sub-class SQLiteOpenHelper. Then, in your sub-class you can have something like this:
private class DBHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "mydatabase.db";
private static final int DATABASE_VERSION = 45;
public DBHelper(Context context)
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
...
The SQLiteOpenHelper handles opening the database, and updates when the application launches. In the above example, the database version is 45. If you increment that, and upload a new APK to the market, an onUpgrade() method is called, which allows you to update users old databases to your new version.
I'm looking to create a sqlite database on the sd card (don't want to use up the user's internal storage). I'm familiar with the OpenHelper pattern:
public DatabaseFoo(Context context) {
OpenHelper openHelper = new OpenHelper(context);
mDb = openHelper.getWritableDatabase();
}
private static class OpenHelper extends SQLiteOpenHelper {
public OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
...
so if we want to create on the sd card, I think instead we have to use:
public static SQLiteDatabase openOrCreateDatabase (String path,
SQLiteDatabase.CursorFactory factory);
But what is the "factory" argument supposed to be, what factory should be used?
Also a bit worried about what happens if the user removes the SD card while my app is in use..
Thanks
I haven't tried to do what you describe there, but presumably it could be done and might work -- with a few caveats. First, the external storage (SD card) is not secure, so any other application, or the user, could read/write to it. Second, as you noted, when it's unmounted the DB goes away.
Because of these disadvantages, you would probably be better off to try to use an internal storage database (the default), that is small and possibly includes pointers to external data (like images or files) -- that themselves can be on the external storage (and that have placeholders, or other handling, when the external storage is not available).
Still, if you want to try it, you might be better off override the getDatabasePath method of Context, such as with your own Application object, and then pass that into a regular SQLiteOpenHelper. Then you wouldn't have to worry about the cursor factory (which is optional, as the source confirms -- so just pass null if instead you want to go that route).
Do this in your SQLiteOpenHelper constructor:
DatabaseHelper(Context context) {
super(context, context.getExternalFilesDir(null).getAbsolutePath() + "/" + DATABASE_NAME, null, DATABASE_VERSION);
}
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.
Cursor factory is used to return an instance of your custom Cursor implementation.
Generally you just use SQLiteCursor, in which case null is passed as factory argument.
Then make your own flat database - most people have very little internal memory, and its annoying that they eat it up with huge database.
And as for the 'what if they remove the SD' scenario - if the user removes the card obviously its not going to work! Clearly. Just check you didn't get an error when trying to interact with the base, and if you did, just tell you user - problem solved.
public DataBaseHelper(final Context context) {
super(context, Environment.getExternalStorageDirectory()
+ File.separator+ MYDATABASE_NAME, null, MYDATABASE_VERSION);
}
Also Add permission in android Manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I would recommend against putting a database onto an SD card - you will significantly decrease the lifespan of the card, since it has a (large, but still existent) limit on the number of writes possible and databases require quite a few writes.