I'm having serious problems trying to get SQLite working with Android. I keep getting errors such as "unknown error (code 14): Could not open database".
One method I found that gets rid of this error temporarily is to delete the database and then create one. There mightn't even be a database but calling deleteDatabase() seems to fix the problem.
File DB_PATH = getApplicationContext().getDatabasePath("test.db");
DB_PATH.mkdirs();
DatabaseHelper dbh = DatabaseHelper.getHelper(this);
SQLiteDatabase db;
SQLiteDatabase.deleteDatabase(DB_PATH);
db = dbh.getWritableDatabase();
DatabaseHelper
public DatabaseHelper(Context ctx) {
super(ctx, ctx.getApplicationContext().getDatabasePath("test.db").getPath(), null, 10);
this.context = ctx;
}
public static synchronized DatabaseHelper getHelper(Context context) {
if (instance == null) {
instance = new DatabaseHelper(context);
}
return instance;
}
What I'm wondering is why would deleting the database make a difference and any advice in general would be appreciated?
Try using this template
public class MyDBHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "myDatabase.db";
private static final int SCHEMA = 1;
private static volatile MyDBHelper sInstance;
private MyDBHelper(Context context) {
super(context, DATABASE_NAME, null, SCHEMA);
}
public static MyDBHelper getInstance(Context context) {
if (sInstance == null) {
synchronized (MyDBHelper.class) {
if (sInstance == null) {
sInstance = new MyDBHelper(context.getApplicationContext());
}
}
}
return sInstance;
}
#Override
public void onCreate(SQLiteDatabase db) {
// create your tables here
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// handle db upgrade or leave blank
}
}
Related
I have a static, singleton instance of a SQLiteOpenHelper implementation.
public class MyDbHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 5;
public static final String DATABASE_NAME = "myapp.db";
public static SQL_CREATE_MY_TABLE ="some correct table creation sql";
public static SQL_DELETE_MY_TABLE="some correct table delete sql";
...
public MyDbHelper (Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_MY_TABLE);
db.execSQL(SQL_CREATE_OTHER_TABLE);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(SQL_DELETE_MY_TABLE);
db.execSQL(SQL_DELETE_OTHER_TABLE);
onCreate(db);
}
}
I have another wrapper over the helper as:
public class MyAppDb {
private static MyDbHelper mydbHelper;
public static MyDbHelper getDbHelper(){
if(null == mydbHelper){
mydbHelper= new MyDbHelper(ctx);
return mydbHelper;
}else{
return mydbHelper;
}
}
}
I am using the mydbHelper from this getDbHelper() in my main Ui code(at various places), as well as in a BroadcastReceiver of an Alarmmanager that executes every 5 mins, and in another system BroadcastReceiver.
Sample usage:
SQLiteDatabase db = MyAppDb.getDbHelper().getReadableDatabase();
Intermittently, I get SQLiteDatabaseLockedException and SQLiteException on the the db implementations from the above mydbHelper.
It seems as though although static, there are two different instances of the MyDbHelper occuring at the same time.
What is happening? Can a static object have a separate instance created from BroadCastReceiver?
Or I am doing it wrong?
I am trying to better understand what it means to open an Sqlite database on a background thread in Android. Right now I am using a static/singleton pattern for my database via my class DatabaseHelper, so I only need to open it once, but I want to open it using good practice and understand why I shouldn't open it directly from within my Activity directly (or within the helper's constructor, for example).
My class:
public class DatabaseHelper extends SQLiteOpenHelper {
private static volatile SQLiteDatabase mDatabase;
private static DatabaseHelper mInstance = null;
private static Context mContext;
// ...
public static synchronized DatabaseHelper getInstance(Context context) {
/**
* use the application context as suggested by CommonsWare.
* this will ensure that you don't accidentally leak an Activity's
* context (see this article for more information:
* http://android-developers.blogspot.nl/2009/01/avoiding-memory-leaks.html)
*/
if (mInstance == null) {
mInstance = new DatabaseHelper(context.getApplicationContext());
}
return mInstance;
}
private DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
mContext = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_CREATE_SOME_TABLE); //some SQL expression
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(DB_ALTER);
}
public void open() throws SQLException {
mDatabase = getWritableDatabase();
}
public void close() {
mDatabase.close();
}
public boolean isOpen() {
return mDatabase.isOpen();
}
//below this would be various CRUD functions operating on mDatabase
// ...
// ...
}
Is it correct to say that you should do something like this:
DatabaseHelper mDatabaseHelper = DatabaseHelper.getInstance(this);
Thread thread = new Thread("OpenDbThread") {
public void run(){
mDatabaseHelper.open();
}
};
thread.start();
inside an Activity somewhere?
You're correct that the code you wrote would open the database on a background thread. However, you wouldn't actually the database was opened until thread.isAlive() returned false (or mDatabase.isOpen() returned true). Alternatively, you could make your Activity listen for a callback from your Thread.
I found many stuff like close the connection and close the cursor, but I do all this stuff. Still the SQLite connection leaks and I get a warning like this:
A SQLiteConnection object for database was leaked!
I have a database manager this, which I call in my activities with the following code:
DatabaseManager dbm = new DatabaseManager(this);
The code of my database manager class follows now:
public class DatabaseManager {
private static final int DATABASE_VERSION = 9;
private static final String DATABASE_NAME = "MyApp";
private Context context = null;
private DatabaseHelper dbHelper = null;
private SQLiteDatabase db = null;
public static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
//create database tables
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//destroy and recreate them
}
}
public DatabaseManager(Context ctx) {
this.context = ctx;
}
private DatabaseManager open() throws SQLException {
dbHelper = new DatabaseHelper(context);
db = dbHelper.getWritableDatabase();
if (!db.isReadOnly()) {
db.execSQL("PRAGMA foreign_keys = ON;");
}
return this;
}
private void close() {
dbHelper.close();
}
}
When I call a database method, I do the following thing:
public Object getData() {
open();
//... database operations take place ...
close();
return data;
}
But as I said, I still get this SQLite connection leaked warning.
What am I doing wrong?
The bolded font in the citation corresponds to this part in your code:
private DatabaseManager open() throws SQLException {
dbHelper = new DatabaseHelper(context);
db = dbHelper.getWritableDatabase();
from: http://www.androiddesignpatterns.com/2012/05/correctly-managing-your-sqlite-database.html
Approach #1: Use an Abstract Factory to Instantiate the
SQLiteOpenHelper
Declare your database helper as a static instance variable and use the
Abstract Factory pattern to guarantee the singleton property. The
sample code below should give you a good idea on how to go about
designing the DatabaseHelper class correctly.
The static factory getInstance method ensures that only one
DatabaseHelper will ever exist at any given time. If the mInstance
object has not been initialized, one will be created. If one has
already been created then it will simply be returned.
You should
not initialize your helper object using with new DatabaseHelper(context).
Instead, always use
DatabaseHelper.getInstance(context), as it guarantees that only one
database helper will exist across the entire application's lifecycle.
public static class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper mInstance = null;
private static final String DATABASE_NAME = "database_name";
private static final String DATABASE_TABLE = "table_name";
private static final int DATABASE_VERSION = 1;
public static DatabaseHelper getInstance(Context ctx) {
// Use the application context, which will ensure that you
// don't accidentally leak an Activity's context.
// See this article for more information: http://bit.ly/6LRzfx
if (mInstance == null) {
mInstance = new DatabaseHelper(ctx.getApplicationContext());
}
return mInstance;
}
/**
* Constructor should be private to prevent direct instantiation.
* make call to static factory method "getInstance()" instead.
*/
private DatabaseHelper(Context ctx) {
super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
}
}
The complete example of the above-accepted answer:
It may help someone.
Helper Class:
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "sample.db";
private static final int DATABASE_VERSION = 1;
private static DatabaseHelper mInstance;
private DatabaseHelper(#Nullable Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public static synchronized DatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new DatabaseHelper(context.getApplicationContext());
}
return mInstance;
}
#Override
public void onCreate(SQLiteDatabase db) {
// create table stuff
}
#Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
// drop table stuff
onCreate(db);
}
}
Activity:
SQLiteDatabase database = DatabaseHelper.getInstance(getApplicationContext()).getWritableDatabase();
Cursor cursor = database.query("query");
if (cursor != null) {
while (cursor.moveToNext()) {
// stuff
}
cursor.close();
database.close();
}
private void method() {
Cursor cursor = query();
if (flag == false) { // WRONG: return before close()
return;
}
cursor.close();
}
Good practice should be like this:
private void method() {
Cursor cursor = null;
try {
cursor = query();
} finally {
if (cursor != null)
cursor.close(); // RIGHT: ensure resource is always recovered
}
}
i yet really grasp this whole context thing we found a lot in android programming. so i tried creating a function to drop all my tables, and here's a my partial code:
public class DBAdapter {
private static class DbHelper extends SQLiteOpenHelper {
private boolean databaseCreated = false;
public DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void deleteTables(){
Log.d("DBAdapter","dlm drop tables pre");
this.sqlDatabase.execSQL("DROP TABLE IF EXISTS ["+TABLE_TV+"];");
this.sqlDatabase.execSQL("DROP TABLE IF EXISTS ["+TABLE_CAMERA+"];");
this.sqlDatabase.execSQL("DROP TABLE IF EXISTS ["+TABLE_GPS+"];");
}
}
}
and the part where i'm going to call the function deleteTables
public class UpdateDatabase {
public void updateTable(String table,JSONObject jsonObject){
DBAdapter db = new DBAdapter(this);
db.deleteTables();
}
}
but of course it will return an error, since DBAdapter expects a context. public class UpdateDatabase is not an activity class. Calling DbAdapter db = new DBAdapter(this) from activity class will work just find. So how do I find any fix for this problem?
thanks
You can add a constructor to UpdateDatabase that takes a Context and stores it so that it is available to be used by updateTable. Something like this:
public class UpdateDatabase {
private final Context mContext;
public UpdateDatabase(Context context){
mContext = context;
}
public void updateTable(String table,JSONObject jsonObject){
DBAdapter db = new DBAdapter(mContext);
db.deleteTables();
}
}
Now, whenever you do new UpdateDatabase() you will need to do new UpdateDatabase(..context..) instead. If you are doing this from an Activity, then you can do new UpdateDatabase(this).
hi look this code..
public class DbManager
{
// the Activity or Application that is creating an object from this class.
Context context;
CustomSQLiteOpenHelper helper;
// a reference to the database used by this application/object
protected SQLiteDatabase db;
private static DbManager INSTANCE;
// These constants are specific to the database.
protected final String DB_NAME = "yourDB";
protected final int DB_VERSION = 1;
public DbManager(Context context)
{
this.context = context;
// create or open the database
helper = new CustomSQLiteOpenHelper(context);
this.db = helper.getWritableDatabase();
}
public static DbManager getInstance(Context context){
if(INSTANCE == null)INSTANCE = new DbManager(context);
return INSTANCE;
}
public void db_Close()
{
if(helper!=null){
helper.close();
}
this.db.close();
}
private class CustomSQLiteOpenHelper extends SQLiteOpenHelper
{
public CustomSQLiteOpenHelper(Context context)
{
super(context, DB_NAME, null, DB_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db)
{
// This string is used to create the database.
// execute the query string to the database.
//db.execSQL(newTableQueryString);
Log.i("DataBaseManager", "Create Table");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
// NOTHING TO DO HERE. THIS IS THE ORIGINAL DATABASE VERSION.
// OTHERWISE, YOU WOULD SPECIFIY HOW TO UPGRADE THE DATABASE.
}
}
}
// Inherit the DbManager Class
public class DataCollection extends DbManager {
public DataCollection(Context context){
super(context);
}
public void deleteTable(String TABLE_NAME){
try {db.execSQL("DROP TABLE "+TABLE_NAME);}//.delete(TABLE_NAME, null, null);}
catch (Exception e){
Log.e("DB ERROR", e.toString());
e.printStackTrace();
}
}
I created a DBTest class SQLiteOpenHelper
Then I called this from my main UI.
It crashes when I hit the DB.GetReadableDatabase () and the log is no help, just says null pointer, but I don't know where to look.
Everything works if I use
SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
Same database
Here is the Class for the helper and below the error:
public class DBTest extends SQLiteOpenHelper {
private static String DB_NAME = "DB";
private SQLiteDatabase myDataBase;
private final Context myContext;
public DBTest(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
Below is where the error happens:
public class Main extends Activity
{
DataBaseHelper db = new DataBaseHelper(null);
static SQLiteDatabase Db;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SQLiteDatabase Db;
DBTest db1 = new DBTest(null);
Db = db1.getReadableDatabase(); <<< blow up here
}
}
DBTest db1 = new DBTest(this.getApplicationContext());
Context can't be null