I'm trying to change my DatabaseInterface implementation by opening it in MyApplication extends Application and basically never closing it (I did some research and a Google engineer recommends it, see the linked question Best place to close database connection). The modifications I have made are quite minor but now the system throws me the following error when opening the database:
E/SQLiteDatabase(15690): android.database.sqlite.SQLiteException: not an error (code 0): Could not open the database in read/write mode.
Here is relevant code
MyApplication.java
#Override
public void onCreate() {
super.onCreate();
try {
DatabaseInterface.open();
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
DatabaseInterface.java
public class DatabaseInterface extends SQLiteOpenHelper {
private final static String DB_PATH = Constants.DB_PATH;
private final static String DB_NAME = Constants.DB_NAME;
private static SQLiteDatabase mDatabase;
private Context mContext;
private static DatabaseInterface mInstance;
private DatabaseInterface(Context context) {
super(context, DB_NAME, null, 1);
this.mContext = context;
}
public static DatabaseInterface getInstance(Context context) {
if (mInstance == null) {
mInstance = new DatabaseInterface(context);
}
return mInstance;
}
public static void open() throws SQLException, IOException {
//Open the database
String path = DB_PATH + DB_NAME;
mDatabase = SQLiteDatabase.openOrCreateDatabase(new File(path), null);
}
}
Ok this was caused by me forgetting to add
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
to my AndroidManifest.xml as my database reside on the phone's external storage.
Related
I'm trying to implement some tests in my application. One thing that I want to test is writing a java object to my db, then retrieving it and asserting the the object that comes out of the db matches the object that went in.
Here's my MySQLiteHelper application code:
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.concurrent.atomic.AtomicInteger;
class MySQLiteHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "unittesttester.db";
private static final int DATABASE_VERSION = 8;
private static final String LOG_TAG = MySQLiteHelper.class.getSimpleName();
private static final int WEATHER_STALENESS_PERIOD_MS = 60 * 5 * 1000; //5 minutes
private AtomicInteger mOpenCounter = new AtomicInteger();
private static MySQLiteHelper mInstance = null;
private SQLiteDatabase db;
private Context mContext;
public static MySQLiteHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new MySQLiteHelper(context.getApplicationContext());
}
return mInstance;
}
private MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(WeatherTable.CREATE_TABLE_WEATHER);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion <= DATABASE_VERSION) {
onCreate(db);
}
}
private synchronized SQLiteDatabase openDatabase() {
final int i = mOpenCounter.incrementAndGet();
if (i == 1) {
db = getWritableDatabase();
}
return db;
}
private synchronized void closeDatabase() {
final int i = mOpenCounter.decrementAndGet();
if (i == 0) {
db.close();
}
}
private void truncateWeatherTable() {
db = openDatabase();
db.delete(WeatherTable.TABLE_WEATHER, null, null);
closeDatabase();
}
public void deleteAndInsertWeather(Weather weather) {
db = openDatabase();
db.beginTransaction();
try {
truncateWeatherTable();
insertWeather(weather);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
closeDatabase();
}
}
private void insertWeather(Weather weather) {
db = openDatabase();
db.insert(WeatherTable.TABLE_WEATHER, null, makeWeatherCv(weather));
closeDatabase();
}
public Weather getWeather() {
db = openDatabase();
String sql = "SELECT * FROM " + WeatherTable.TABLE_WEATHER;
Cursor c = null;
Weather weather = null;
try {
c = db.rawQuery(sql, null);
if (c.moveToFirst()) {
weather = makeWeather(c);
//If sample too old return null
if (System.currentTimeMillis() - weather.getTimestamp() > WEATHER_STALENESS_PERIOD_MS) {
weather = null;
truncateWeatherTable();
}
}
} finally {
if (c != null) {
c.close();
}
closeDatabase();
}
return weather;
}
private Weather makeWeather(Cursor c) {
Weather weather = new Weather();
weather.setTimestamp(c.getLong(c.getColumnIndex(WeatherTable.COLUMN_TIMESTAMP)));
weather.setElevation(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_ELEVATION)));
weather.setTemperature(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_TEMPERATURE)));
weather.setDusk(c.getInt(c.getColumnIndex(WeatherTable.COLUMN_DUSK)));
weather.setNighttime(c.getInt(c.getColumnIndex(WeatherTable.COLUMN_NIGHTTIME)));
weather.setGravity(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_GRAVITY)));
weather.setDaytime(c.getInt(c.getColumnIndex(WeatherTable.COLUMN_DAYTIME)));
weather.setHumidity(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_HUMIDITY)));
weather.setPressure(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_PRESSURE)));
weather.setOkta(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_OKTA)));
weather.setDawn(c.getInt(c.getColumnIndex(WeatherTable.COLUMN_DAWN)));
return weather;
}
private ContentValues makeWeatherCv(Weather weather) {
ContentValues contentValues = new ContentValues();
contentValues.put(WeatherTable.COLUMN_TIMESTAMP, weather.getTimestamp());
contentValues.put(WeatherTable.COLUMN_TEMPERATURE, weather.getElevation());
contentValues.put(WeatherTable.COLUMN_TEMPERATURE, weather.getTemperature());
contentValues.put(WeatherTable.COLUMN_DUSK, weather.getDusk());
contentValues.put(WeatherTable.COLUMN_NIGHTTIME, weather.getNighttime());
contentValues.put(WeatherTable.COLUMN_GRAVITY, weather.getGravity());
contentValues.put(WeatherTable.COLUMN_DAYTIME, weather.getDaytime());
contentValues.put(WeatherTable.COLUMN_HUMIDITY, weather.getHumidity());
contentValues.put(WeatherTable.COLUMN_PRESSURE, weather.getPressure());
contentValues.put(WeatherTable.COLUMN_OKTA, weather.getOkta());
contentValues.put(WeatherTable.COLUMN_DAWN, weather.getDawn());
return contentValues;
}
}
Here's my test class for the class above:
import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext;
import org.junit.Test;
import static org.mockito.Mockito.*;
public class MySQLiteHelperTest extends AndroidTestCase {
private MySQLiteHelper db;
private Weather mockedWeather = mock(Weather.class);
#Override
public void setUp() throws Exception {
super.setUp();
context = new MockContext();
setContext(context);
assertNotNull(context);
RenamingDelegatingContext renamingContext = new RenamingDelegatingContext(getContext(), "test_");
db = MySQLiteHelper.getInstance(renamingContext);
assertNotNull(db);
when(mockedWeather.getDawn()).thenReturn(0);
when(mockedWeather.getDaytime()).thenReturn(1);
when(mockedWeather.getDusk()).thenReturn(2);
when(mockedWeather.getElevation()).thenReturn(3.0);
when(mockedWeather.getGravity()).thenReturn(4.0);
when(mockedWeather.getHumidity()).thenReturn(5.0);
when(mockedWeather.getNighttime()).thenReturn(6);
when(mockedWeather.getOkta()).thenReturn(7.0);
when(mockedWeather.getPressure()).thenReturn(8.0);
when(mockedWeather.getTemperature()).thenReturn(9.0);
when(mockedWeather.getTimestamp()).thenReturn(10L);
}
#Override
public void tearDown() throws Exception {
super.tearDown();
}
public void testGetInstance() throws Exception {
}
public void testOnCreate() throws Exception {
}
public void testOnUpgrade() throws Exception {
}
#Test
public void testDeleteAndInsertWeather() throws Exception {
db.deleteAndInsertWeather(mockedWeather);
Weather actualWeather = db.getWeather();
assertEquals(mockedWeather.getDawn(), actualWeather.getDawn());
assertEquals(mockedWeather.getDaytime(), actualWeather.getDaytime());
assertEquals(mockedWeather.getDusk(), actualWeather.getDusk());
assertEquals(mockedWeather.getElevation(), actualWeather.getElevation());
assertEquals(mockedWeather.getGravity(), actualWeather.getGravity());
assertEquals(mockedWeather.getHumidity(), actualWeather.getHumidity());
assertEquals(mockedWeather.getNighttime(), actualWeather.getNighttime());
assertEquals(mockedWeather.getOkta(), actualWeather.getOkta());
assertEquals(mockedWeather.getPressure(), actualWeather.getPressure());
assertEquals(mockedWeather.getTemperature(), actualWeather.getTemperature());
assertEquals(mockedWeather.getTimestamp(), actualWeather.getTimestamp());
}
public void testDeleteWeather() throws Exception {
}
public void testInsertWeather() throws Exception {
}
public void testGetWeather() throws Exception {
}
public void testWeatherMakeCv() throws Exception {
}
}
When I run the test I am getting a NPE during my test. It seems to occur when the MySQLiteHelper class has its db = getWritableDatabase() line. getWriteableDatabase() is a public method from the base class.
I don't think I understand why this test results in an NPE. In my test I call the static method, MySQLiteHelper.getInstance(Context context) which should initialize the class. It is my assumption that calling getInstance will provide me with a fully initialized instance of MySQLiteHelper. Why does this not seem to be happening?
EDIT:
The problem I have now is that when getWritableDatabase() is called it returns null instead of an instance of SQLiteDatabase.
I ended completing my goals of unit testing my sqlite database. The problem seemed to be that I needed to use the build artifact called Android Instrumentation Test instead of the Unit Test build artifact.
I setup a test class in my app/src/androidTest/java directory. The test class extended InstrumentationTestCase.
When I setup my database I use the context provided by getInstrumentation().getTargetContext(). This was important because originally I tried to use getInstrumentation().getContext() and I found that that would always result in a SQLiteCantOpenDatabaseException.
So it seemed my problems occurred because:
1) I wasn't using the correct test artifact
2) I wasn't using the correct test base class
3) I wasn't getting the context correctly
AndroidTestCase#getContext() returns whatever Context you've set with setContext() and you haven't set anything, so a null is returned`.
Using a null context with SQLiteOpenHelper will NPE when the database is being opened e.g. with getWritableDatabase().
See Getting context in AndroidTestCase or InstrumentationTestCase in Android Studio's Unit Test feature for more details on how to set up a Contex in test cases.
Currently I am creating the greenDAO DB connection in a class (which opens the connection in every static method) and using it wherever I need it. But I am not sure if it's the best way of doing it.
Can anyone suggest a better way of doing it?
My Code:
import com.knowlarity.sr.db.dao.DaoMaster;
import com.knowlarity.sr.db.dao.DaoMaster.DevOpenHelper;
import com.knowlarity.sr.db.dao.DaoSession;
import com.knowlarity.sr.db.dao.IEntity;
public class DbUtils {
private static Object lockCallRecord =new Object();
private DbUtils(){};
public static boolean saveEntity(Context context , IEntity entity){
boolean t=false;
DevOpenHelper helper=null;
SQLiteDatabase db=null;
DaoMaster daoMaster=null;
DaoSession daoSession =null;
try{
helper = new DaoMaster.DevOpenHelper(context, IConstant.DB_STRING, null);
db = helper.getReadableDatabase();
daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
//Some business logic here for fetching and inserting the data.
}catch (Exception e){
Log.e("saveEntity", e.getStackTrace().toString());
}finally{
if(daoSession!=null)daoSession.clear();
daoMaster=null;
if(db.isOpen())db.close();
helper.close();
}
return t;
}
Your approach causes the database to be loaded very often which is not necessary and may slow down your app significantly.
Open the database once and store it somewhere and request it from there if needed.
Personally I use a global DaoSession and local DaoSessions. The local DaoSessions get used where nothing should remain in the session cache (i.e. persisting a new object into the database, that is likely to be used only very infrequent or performing some queries which will load a lot of entities that are unlikely to be reused again).
Keep in mind that updating entities in a local DaoSession is a bad idea if you use the entity in your global session as well. If you do this the cached entity in your global session won't be updated and you will get wrong results unless you clear the cache of the global session!
Thus the safest way is to either just use one DaoSession or new DaoSessions all the time and to not use a global and local sessions!!!
A custom application class is a good place, but any other class will also be ok.
This is how I do it:
class DBHelper:
private SQLiteDatabase _db = null;
private DaoSession _session = null;
private DaoMaster getMaster() {
if (_db == null) {
_db = getDatabase(DB_NAME, false);
}
return new DaoMaster(_db);
}
public DaoSession getSession(boolean newSession) {
if (newSession) {
return getMaster().newSession();
}
if (_session == null) {
_session = getMaster().newSession();
}
return _session;
}
private synchronized SQLiteDatabase getDatabase(String name, boolean readOnly) {
String s = "getDB(" + name + ",readonly=" + (readOnly ? "true" : "false") + ")";
try {
readOnly = false;
Log.i(TAG, s);
SQLiteOpenHelper helper = new MyOpenHelper(context, name, null);
if (readOnly) {
return helper.getReadableDatabase();
} else {
return helper.getWritableDatabase();
}
} catch (Exception ex) {
Log.e(TAG, s, ex);
return null;
} catch (Error err) {
Log.e(TAG, s, err);
return null;
}
}
private class MyOpenHelper extends DaoMaster.OpenHelper {
public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
#Override
public void onCreate(SQLiteDatabase db) {
Log.i(TAG, "Create DB-Schema (version "+Integer.toString(DaoMaster.SCHEMA_VERSION)+")");
super.onCreate(db);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i(TAG, "Update DB-Schema to version: "+Integer.toString(oldVersion)+"->"+Integer.toString(newVersion));
switch (oldVersion) {
case 1:
db.execSQL(SQL_UPGRADE_1To2);
case 2:
db.execSQL(SQL_UPGRADE_2To3);
break;
default:
break;
}
}
}
In application class:
private static MyApplication _INSTANCE = null;
public static MyApplication getInstance() {
return _INSTANCE;
}
#Override
public void onCreate() {
_INSTANCE = this;
// ...
}
private DBHelper _dbHelper = new DBHelper();
public static DaoSession getNewSession() {
return getInstance()._dbHelper.getSession(true);
}
public static DaoSession getSession() {
return getInstance()._dbHelper.getSession(false);
}
Of course you can also store the DaoMaster instead of the DB itself. This will reduce some small overhead.
I'm using a Singleton-like Application class and static methods to avoid casting the application (((MyApplication)getApplication())) every time I use some of the common methods (like accessing the DB).
I would recommend to create your database in your Application class. Then you can create a method to return the DaoSession to get access to the database in other Activities.
How is it that some dictionaries such as merriam dictionary (Offline dictionary) when the application was installed , the words are there instantly, and time is not required to insert a list of words and definition into the database? I am a beginner and is currently developing an android application that consist of about 30K words and it will take around 15+ minutes for it to insert all the data into the database before the user can search for that particular data. And I am looking for a method that can fix this. Could someone please tell me a way to do it ?
Thank you
My guess is that these apps are using an already SQLite database with all the data they need already populated.
You can import populated databases to your app with something like this :
public class DataBaseAdapter {
String DB_NAME = "DBNAME.db";
String DIR = "/data/data/packageName/databases/";
String DB_PATH = DIR + DB_NAME;
private DataBaseHelper mDbHelper;
private SQLiteDatabase db;
private Context context;
public DataBaseAdapter(Context context) {
this.context = context;
mDbHelper = new DataBaseHelper(this.context);
}
class DataBaseHelper extends SQLiteOpenHelper {
private boolean createDatabase = false;
#SuppressWarnings("unused")
private boolean upgradeDatabase = false;
Context context;
public DataBaseHelper(Context context) {
super(context, DB_NAME, null, 1);
this.context = context;
}
public void initializeDataBase() {
getWritableDatabase();
if (createDatabase) {
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
private void copyDataBase() throws IOException {
InputStream input = context.getAssets().open(DB_NAME);
OutputStream output = new FileOutputStream(DB_PATH);
byte[] buffer = new byte[1024];
int length;
try {
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
}
finally {
try {
if (output != null) {
try {
output.flush();
} finally {
output.close();
}
}
} finally {
if (input != null) {
input.close();
}
}
}
getWritableDatabase().close();
}
public void onCreate(SQLiteDatabase db) {
createDatabase = true;
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
upgradeDatabase = true;
}
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
}
public DataBaseAdapter open() {
mDbHelper.initializeDataBase();
if (db == null)
db = mDbHelper.getWritableDatabase();
return this;
}
public void close() {
db.close();
}
}
you can then add methods to get data from database and this class can be used in your activity by calling open then the method to get data then close.
Your application should include a pre-populated database for offline access with it's install. That will avoid each user having to run the INSERT step on their device.
Is there a particular reason you need to run the INSERTS post-install?
I'm trying to get a pattern that doesn't fail for a multithreaded access to my sqlite database. Also, what is driving me nuts is that I can't reproduce the issue.
I have an app which uses a DB, but also Android Accounts and Android sync to sync my app's data. My guess is that when the two happen a the same time, it crashes. I'm getting a lot of errors like:
* android.database.sqlite.SQLiteDatabaseLockedException: database is locked
* android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
* android.database.sqlite.SQLiteDatabaseLockedException: error code 5: database is locked
* android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5): , while compiling: PRAGMA journal_mode
* android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 778)
* android.database.sqlite.SQLiteException: Failed to change locale for db '/data/data/net.bicou.redmine/databases/redmine.db' to 'en_US'. \n Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
Maybe not all of them are related to the same root cause, however I'm kind of lost.
What I have is:
an abstract base class, DbAdapter, that is extended by subclasses which want to manage a single table
a class that manages the SQLite database, called DbManager, which contains a Lock
Right now the users have a version of the DbManager that is not a singleton. I'm planning to make DbManager a singleton, so that all threads share the same object. This shouldn't be a problem, because as far as I have understood/seen, the background sync and app share the same process.
Here are the classes (only the relevant parts):
public abstract class DbAdapter {
Context mContext;
protected DbManager mDbManager;
SQLiteDatabase mDb;
public static final String KEY_ROWID = "_id";
public DbAdapter(final Context ctx) {
mContext = ctx;
}
public DbAdapter(final DbAdapter other) {
mContext = other.mContext;
mDb = other.mDb;
mDbManager = other.mDbManager; // removed with singleton version
}
public synchronized DbAdapter open() throws SQLException {
if (mDb != null) {
return this;
}
mDbManager = new DbManager(mContext); // currently in production
mDbManager = DbManager.instance(mContext); // currently investigating this singleton solution
try {
mDb = mDbManager.getWritableDatabase();
} catch (final SQLException e) {
L.e("Unable to open DB, trying again in 1 second", e);
try {
Thread.sleep(1000);
} catch (final InterruptedException e1) {
L.e("Could not wait 1 second " + e1);
}
mDb = mDbManager.getWritableDatabase();// This may crash
}
return this;
}
public synchronized void close() {
mDbManager.close();
mDbManager = null;
mDb = null;
}
}
A class that needs to handle a database table will extend DbAdapter, and implement methods such as select, insert, delete, etc.
Here's the DB manager:
public class DbManager extends SQLiteOpenHelper {
private static final String DB_FILE = "db";
private static final int DB_VERSION = 15;
Context mContext;
Lock mLock = new ReentrantLock();
// Currently in prod
public DbManager(final Context context) {
super(context, DB_FILE, null, DB_VERSION);
mContext = context;
}
// singleton version will make this constructor private and add:
private static DbManager mInstance;
public static synchronized DbManager instance(Context context) {
if (instance == null) {
instance = new DbManager(context);
}
return instance;
}
#Override
public SQLiteDatabase getWritableDatabase() {
mLock.lock();
return super.getWritableDatabase();
}
#Override
public void close() {
super.close();
mLock.unlock();
}
#Override
public void onCreate(final SQLiteDatabase db) {
// ...
}
#Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
// ...
}
private void createTables(final SQLiteDatabase db, final String[] statements) {
for (final String sql : statements) {
try {
db.execSQL(sql);
} catch (final Exception e) {
L.e("Unable to create table: " + sql, e);
}
}
}
}
OK, now, the questions.
Is my lock properly implemented? I'm really new to this, I don't know if the ReentrantLock is a good choice, and if I'm locking/unlocking at the right moment
Are my synchronized method properly implemented? I mean, I have placed the synchronized keyword around methods that I don't want interrupted by concurrent threads. Is this right? Can you advice on my synchronized use?
How can I reproduce the issue? I have created a test that uses 3 threads that make concurrent read/write access to the DB, and use some Thread.sleep to ensure that the db open/close from each thread overlap, but it doesn't crash. This is really bugging me, I don't think there is a lot of people that have the issue, so I don't know how to reproduce.
Is my DbAdapter + DbManager technical choice a good idea? Is there a better pattern?
Is it a good idea to make DbManager a singleton?
For multiple threads accessing, it is advisable to use the singleton pattern.
Such a way, successive calls to the same database will be seamlessly serialised.
However, it's not impossible to have some NullPointerExceptions on inserts. So, to expand your "Thread.sleep" logic, you could use this code:
#Override
public SQLiteDatabase getWritableDatabase() {
while (true) {
try {
return super.getWritableDatabase();
} catch (SQLiteDatabaseLockedException e) {
System.err.println(e);
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.err.println(e);
}
}
}
This question already has answers here:
Simple export and import of a SQLite database on Android
(5 answers)
Closed 9 years ago.
It is possible to use an already created database sqlite in android? I already created database in sqlite in mozilla ad-ons. How should I use it in my android application? Anyone can help me??
First, to use a database, in general, in android, you should extend the SQLiteOpenHelper class. This class is the one responsible for creating your database (and upgrading) when needed from a sql script you provide in your implementation.
So the trick is, you need to override the behavior of the SQLiteOpenHelper to copy your database file from the assets folder instead of create your database.
in this blog post, i explain in details the process of overriding this behavior. but here is the final code.
use the Repository class as you would use SQLiteOpenHelper normally.
public class Repository extends SQLiteOpenHelper {
private static final int VERSION = 1;
private static final String DATABASE_NAME = "data.sqlite";
private static File DATABASE_FILE;
// This is an indicator if we need to copy the
// database file.
private boolean mInvalidDatabaseFile = false;
private boolean mIsUpgraded = false;
private Context mContext;
/**
* number of users of the database connection.
* */
private int mOpenConnections = 0;
private static Repository mInstance;
synchronized static public Repository getInstance(Context context) {
if (mInstance == null) {
mInstance = new Repository(context.getApplicationContext());
}
return mInstance;
}
private Repository(Context context) {
super(context, DATABASE_NAME, null, VERSION);
this.mContext = context;
SQLiteDatabase db = null;
try {
db = getReadableDatabase();
if (db != null) {
db.close();
}
DATABASE_FILE = context.getDatabasePath(DATABASE_NAME);
if (mInvalidDatabaseFile) {
copyDatabase();
}
if (mIsUpgraded) {
doUpgrade();
}
} catch (SQLiteException e) {
} finally {
if (db != null && db.isOpen()) {
db.close();
}
}
}
#Override
public void onCreate(SQLiteDatabase db) {
mInvalidDatabaseFile = true;
}
#Override
public void onUpgrade(SQLiteDatabase database,
int old_version, int new_version) {
mInvalidDatabaseFile = true;
mIsUpgraded = true;
}
/**
* called if a database upgrade is needed
*/
private void doUpgrade() {
// implement the database upgrade here.
}
#Override
public synchronized void onOpen(SQLiteDatabase db) {
super.onOpen(db);
// increment the number of users of the database connection.
mOpenConnections++;
if (!db.isReadOnly()) {
// Enable foreign key constraints
db.execSQL("PRAGMA foreign_keys=ON;");
}
}
/**
* implementation to avoid closing the database connection while it is in
* use by others.
*/
#Override
public synchronized void close() {
mOpenConnections--;
if (mOpenConnections == 0) {
super.close();
}
}
private void copyDatabase() {
AssetManager assetManager = mContext.getResources().getAssets();
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(DATABASE_NAME);
out = new FileOutputStream(DATABASE_FILE);
byte[] buffer = new byte[1024];
int read = 0;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
} catch (IOException e) {
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {}
}
}
setDatabaseVersion();
mInvalidDatabaseFile = false;
}
private void setDatabaseVersion() {
SQLiteDatabase db = null;
try {
db = SQLiteDatabase.openDatabase(DATABASE_FILE.getAbsolutePath(), null,
SQLiteDatabase.OPEN_READWRITE);
db.execSQL("PRAGMA user_version = " + VERSION);
} catch (SQLiteException e ) {
} finally {
if (db != null && db.isOpen()) {
db.close();
}
}
}
}
All you need to do is put the sqlite database in your assets folder, then when your app starts the first time, copy the database over to the SDCard.
Here is a great description of how to do this.
Android uses internal databases for SQLite. If you want to use an external SQLite database (or any other external database) you're going to need to use something like an HHTP proxy. Here's a link that provides more info: https://stackoverflow.com/a/4124829/1852466