I have a problem with my Android application. I'm using code bellow to opening SQLite database in AsyncTask. Everything works fine but when I try to close database in onStop() or onDestroy method, it's never closed.
Code for creating and opening database:
public class SQLiteDB extends SQLiteOpenHelper{
private final Context context;
private SQLiteDatabase sqliteDatabase = null;
public SQLiteDB(Context context, String DBName) {
super(context, DBConstant.DB_NAME, null, context.getResources().getInteger(ppredota.android.navigation.view.activities.R.string.database_version));
this.context = context;
}
public void createDB() throws IOException{
if(existDB()){
this.getReadableDatabase();
this.close();
}
else {
this.getWritableDatabase();
try {
copyDB();
this.close();
}
catch (Exception e) {
throw new Error("Chyba pri kopirovani databaze");
}
}
}
private boolean existDB() {
SQLiteDatabase checkDatabase = null;
try{
String fullPath = DBConstant.DB_PATH + DBConstant.DB_NAME;
checkDatabase = SQLiteDatabase.openDatabase(fullPath, null, SQLiteDatabase.OPEN_READWRITE);
}
catch (SQLiteException sqle) {
Log.i("existDB()", "Databaze nelze otevrit, neexistuje");
}
if(checkDatabase == null){
Log.i("existDB", "Databaze jeste neexistuje...");
return false;
}
else{
Log.i("existDB", "Databaze uz existuje...");
checkDatabase.close();
return true;
}
}
private void copyDB() throws IOException {
InputStream inDBStream = context.getAssets().open(DBConstant.DB_NAME);
String newDBPath = DBConstant.DB_PATH + DBConstant.DB_NAME;
OutputStream outDBStream = new FileOutputStream(newDBPath);
Log.i("copyDB", "Otevren outputstream s cestou k nove databazi");
byte[] buffer = new byte[1024];
int length;
while ((length = inDBStream.read(buffer))>0){
outDBStream.write(buffer, 0, length);
}
outDBStream.flush();
outDBStream.close();
inDBStream.close();
}
public void openDB() throws SQLException {
String fullPath = DBConstant.DB_PATH + DBConstant.DB_NAME;
if(sqliteDatabase!=null){
if(sqliteDatabase.isOpen()){
Log.i("openDB()", "Databaze je jiz otevrena");
}
else{
sqliteDatabase = SQLiteDatabase.openDatabase(fullPath, null, SQLiteDatabase.OPEN_READONLY);
Log.i("openDB()", "Databaze" + sqliteDatabase.getPath() + "otevrena");
}
}
else{
sqliteDatabase = SQLiteDatabase.openDatabase(fullPath, null, SQLiteDatabase.OPEN_READONLY);
if(sqliteDatabase.isOpen()){
Log.i("openDB()", "Databaze otevrena");
}
}
}
#Override
public void close() {
if(sqliteDatabase!=null){
sqliteDatabase.close();
Log.i("close()", "Databaze zavrena");
}
super.close();
}
public SQLiteDatabase getSQLiteDatabase() {
if(sqliteDatabase==null){
Log.i("getSQLiteDatabase()","Problem, vraci sqliteDatabase = null");
}
else{
Log.i("getSQLiteDatabase()","instance sqliteDatabase vracena bez problemu");
}
return sqliteDatabase;
}
AssyncTask class:
public class OpenDatabaseTask extends AsyncTask {
private Context context;
private SQLiteDB sqliteDB;
public OpenDatabaseTask(Context context,SQLiteDB sqliteDB) {
this.context = context;
this.sqliteDB = sqliteDB;
}
#Override
protected Void doInBackground(Void... params) {
publishProgress();
try {
sqliteDB.createDB();
} catch (IOException e) {
e.printStackTrace();
}
sqliteDB.openDB();
return null;
}
#Override
protected void onProgressUpdate(Void...unused){
Log.i(OpenDatabaseTask.class.toString(), "Spusteno vlakno");
}
}
and Activity (only important part):
private SQLiteDB sqliteDB;
private SQLiteData sqliteData;
private OpenDatabaseTask openDatabaseTask;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.navigatemenu);
sqliteDB = new SQLiteDB(getApplicationContext(), sourceDatabaseName);
openDatabaseTask = new OpenDatabaseTask(getApplicationContext(), sqliteDB);
openDatabaseTask.execute();
protected void onDestroy(){
super.onDestroy();
Log.i("onDestroy()", "Pokus o zavreni databaze");
//here is the problem, database never closed
sqliteDB.close();
}
protected void onStop(){
super.onStop();
Log.i("onStop()", "Pokus o zavreni databaze");
//here is the problem, database never closed
sqliteDB.close();
}
protected void onPause(){
super.onPause();
}
protected void onResume(){
super.onResume();
//Log.i("onResume()", "Pokus o otevreni databaze");
}
}
So when I try use a close() method to closing database, sqliteDatabase is always null and database is never closed. So database is still opened and after calling onDestroy exception occurs.
Thank you for your time and sorry for my english, I'm czech :)
Just a guess (assuming you want to close on destroy)
close the database before calling the super
protected void onDestroy(){
// close befor super is called
sqliteDB.close();
super.onDestroy();
Log.i("onDestroy()", "Pokus o zavreni databaze");
// sqliteDB.close(); // super.onDestroy may already has destroyed the DB
}
Note:
Assuming that the database only exists and is open while the activity is visible you should open the database in onResume() and close it in onPause().
If the database should be open when the code is loaded into memory create it in onCreate and close it in onDestroy
In your example you open it in onCreate and close it in onStop. Problem: when the activity becomes visible for the 2nd time the db is closed.
For details see android activity documentation and look into the Application-Lifecycle at the button.
I have an app with heavy db interaction. I didnt user helper. My db is opened lots of times, in UI thread and in background thread too, but never ever closed. Had no problems so far, dont know if it is the right way to do...
Related
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 have problem which many have and I think, that I tried all solutions, but I have not found the right solution yet.
My existing database "base.sqlite3" is in "assets" folder, contains three tables.
When I want to do query, appears error, that table is not there.
(In code are possible syntax errors, cause I translated code)
public class Sqlite extends SQLiteOpenHelper {
private final Context myContext;
public SQLiteDatabase base;
private static String path ="/data/data/" + "com.example.myexample" + "/databases/";
private static String name = "base.sqlite3";
private static String p = path + name;
public Sqlite(Context context){
super(context, ime, null, 1);
this.myContext = context;
createDatabase();
}
#Override
public void onCreate(SQLiteDatabase base) {}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
#Override
public synchronized void close()
{
if(base != null)
base.close();
super.close();
}
public void createDatabase()
{
boolean exist1 = checkDatabase();
if(exist1){}
else
{
base = this.getReadableDatabase();
base.close();
copyDatabase();
}
}
private boolean checkDatabase()
{
SQLiteDatabase check = null;
try
{
check = SQLiteDatabase.openDatabase(p, null, SQLiteDatabase.OPEN_READONLY);
}
catch(SQLiteException e)
{ }
if(check != null)
{
check.close();
}
return check != null ? true : false;
}
private void copyDatabase()
{
InputStream dat = null;
try {
dat = myContext.getAssets().open(name);
} catch (IOException e) {
e.printStackTrace();
}
OutputStream dat2 = null;
try {
dat2 = new FileOutputStream(p);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] buffer = new byte[1024];
int length;
try {
while ((length = dat.read(buffer))>0)
{
dat2.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
try {
dat2.flush();
dat2.close();
dat.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void openDatabase()
{
base = SQLiteDatabase.openDatabase(p, null, SQLiteDatabase.OPEN_READONLY);
}
public Cursor SelectSomething(String sql)
{
base = SQLiteDatabase.openDatabase(p, null, SQLiteDatabase.OPEN_READONLY);
Cursor cursor = base.rawQuery(sql,null);
return cursor;
}
}
Thank you so much for all help!
As already was stated in the comment to this answer, the code from
this article is
" old, outdated, dreadful (concatenation to create file paths?), and problematic",
and it appears you are not the first to encounter problems with it.
Also, in the same comment to the same answer, it is proposed to use SQLiteAssetHelper. Consider trying it.
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
I'm developing an android 3.1 application.
This question is not specific for Android, it is about how to design a class that access a database. I asked here because my code is for Android.
I have a class, DBManager, to work with Sqlite database. This is a part of its implementation:
public class DBManager
{
// Variable to hold the database instance
private SQLiteDatabase db;
// Database open/upgrade helper
private DatabaseHelper dbHelper;
public DBManager(Context _context)
{
Log.v("DBManager", "constructor");
dbHelper = new DatabaseHelper(_context, SqlConstants.DATABASE_NAME, null, SqlConstants.DATABASE_VERSION);
}
public DBManager open() throws SQLException
{
Log.v("DBManager", "open");
db = dbHelper.getWritableDatabase();
return this;
}
public void close()
{
Log.v("DBManager", "close");
db.close();
}
...
/**
* Query all forms available locally.
* #return A list with all forms (form.name and form.FormId) available on local db
* or null if there was a problem.
*/
public ArrayList<Form> getAllForms()
{
Log.v("DBManager", "getAllForms");
ArrayList<Form> list = null;
Cursor c = null;
try
{
c = this.getAllFormsCursor();
if (c != null)
{
int formNameIndex = c.getColumnIndex(SqlConstants.COLUMN_FORM_NAME);
int formIdIndex = c.getColumnIndex(SqlConstants.COLUMN_FORM_ID);
c.moveToFirst();
if (c.getCount() > 0)
{
list = new ArrayList<Form>(c.getCount());
do
{
Form f = new Form();
f.Name = c.getString(formNameIndex);
f.FormId = c.getString(formIdIndex);
list.add(f);
}
while (c.moveToNext());
}
}
}
catch (Exception e)
{
e.printStackTrace();
list = null;
}
finally
{
if (c != null)
c.close();
}
return list;
}
private Cursor getAllFormsCursor()
{
Log.v("DBManager", "getAllFormsCursor");
return db.query(SqlConstants.TABLE_FORM,
new String[] {
SqlConstants.COLUMN_FORM_ID,
SqlConstants.COLUMN_FORM_NAME}, null, null, null, null, null);
}
}
And this is an AsyncTask that uses DBManager:
private class DbFormListAsyncTask extends AsyncTask<Void, Void, ArrayList<Form>>
{
private Context mContext;
private ProgressDialog loadingDialog;
private DBManager dbMan;
DbFormListAsyncTask(Context context)
{
this.mContext = context;
loadingDialog = new ProgressDialog(mContext);
loadingDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
loadingDialog.setMessage("Retriving forms. Please wait...");
loadingDialog.setCancelable(false);
loadingDialog.show();
}
#Override
protected ArrayList<Form> doInBackground(Void... arg0)
{
dbMan = new DBManager(mContext);
dbMan.open();
return dbMan.getAllForms();
}
protected void onPostExecute(ArrayList<Form> forms)
{
if (forms != null)
{
ListActivity act = (ListActivity) mContext;
act.setListAdapter(new AvaFormAdapter(act, R.layout.ava_list_item, forms));
}
else
{
TextView errorMsg = (TextView)
((FormsListActivity) mContext).findViewById(R.id.formErrorMsg);
errorMsg.setText("Problem getting forms. Please try again later.");
}
loadingDialog.dismiss();
if (dbMan != null)
dbMan.close();
}
}
As you can see I have to:
Create DBManager instance.
Open database with dbMan.open()
Call dbMan.getAllForms()
Close database with dbMan.close() on onPostExecute.
I thought that I could add db.open() and db.close() on dbMan.getAllForms() to avoid calling it every time I use dbMan.getAllForms().
What do you think about this? What is the best approach?
I would put it inside getAllForms() or do something like that
protected ArrayList<Form> doInBackground(Void... arg0)
{
dbMan = new DBManager(mContext);
dbMan.open();
ArrayList<Form> resutl = dbMan.getAllForms();
dbMan.close();
return result;
}
Since you don't need the db connection after you have the result you can close it right away.
Edit: if you run that AsyncTask several times then opening/closing will introduce unnecessary overhead. In that case you may want to instanciate the dbManager from your Activity (maybe open() in the constructor of DbManager) and close it once you leave your activity. Then pass Dbmanager to the AsyncTask.
Make your database helper class a singleton, and don't explicitly close the SQLiteDatabase. It will be closed and flushed when your app's process exits.
I'm creating an application. I'm getting this error:
11-08 13:46:24.665: ERROR/Database(443):
java.lang.IllegalStateException:
/data/data/com.testproj/databases/Testdb SQLiteDatabase created and
never closed
I can't seem to find the reason for this, as it somethimes shows me the error, sometimes not. Here is my code:
public class SQLiteAssistant extends SQLiteOpenHelper {
public SQLiteAssistant(Context context){
super(context, DB_NAME, null, DB_VERSION_NUMBER);
this.myContext = context;
}
public void openDataBase() throws SQLException{
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);
}
public void closeDataBase() {
if(this.myDataBase != null) {
if(this.myDataBase.isOpen())
this.myDataBase.close();
}
}
}
}
In another class, I have these queries:
public class Db{
private static SQLiteAssistant sqlite;
public static String getSomeString(Context ctx) {
sqlite = new SQLiteAssistant(ctx);
sqlite.openDataBase();
Cursor cursor = sqlite.myDataBase.rawQuery("SELECT someColumn from SomeTable",null);
if (cursor != null) {
if (cursor.getCount()==1) {
if(cursor.moveToFirst()) {
String testString = cursor.getString(cursor.getColumnIndex("someColumn"));
cursor.close();
sqlite.closeDataBase();
sqlite.close();
return testString
}
}
}
sqlite.closeDataBase();
sqlite.close();
return null;
}
}
My problem is when I start a new activity in which I get an AsyncTask. This task gets data from a web service and accesses the database for the String. Here is the AsyncTask:
protected class BackTask extends AsyncTask<Context, String, String> {
#Override
protected String doInBackground(Context... params) {
try{
//get requeste data from the database
//access the web service
return result;
} catch (Exception e) {
return null;
}
return null;
}
}
If I let the activity take its course, everything goes fine. If I don't and quickly press the back button, I get the error. Any suggestion on how to solve this problem?
Am not sure you're using SQLiteOpenHelper properly... you don't need that myDataBase field, the idea is that it manages your database connection for you. Don't subclass in that way... unless you're doing things in onCreate() etc that aren't posted here it looks like you can just use SQLiteOpenHelper directly, i.e.:
SQLiteOpenHelper sqlite = new SQLiteOpenHelper(ctx, DB_PATH+DB_NAME, null,
DB_VERSION_NUMBER);
Assuming that ending the activity should also stop your background task, I'd recommend calling AsyncTask.cancel(true) from your Activity.onPause(). Ensure the database is cleaned up from onCancelled().
And if your background task is the only thing reading the database then make it own the SQLiteOpenHelper instance. It's easy to get into trouble with static data, so it's best avoided IMHO. I'd do something like this:
protected class BackTask extends AsyncTask<String, Integer, String>
{
private SQLiteOpenHelper sqlite;
public void BackTask(Context ctx) {
sqlite = new SQLiteOpenHelper(ctx, DB_PATH+DB_NAME, null,
DB_VERSION_NUMBER);
}
#Override
protected String doInBackground(String... params)
{
try {
//get requeste data from the database
//access the web service
return result;
} catch (Exception e) {
}
return null;
}
#Override
protected void onCancelled() {
sqlite.close();
}
#Override
protected void onPostExecute(String result)
sqlite.close();
// Update UI here
}
}
I think this part :
cursor.close();
sqlite.closeDataBase();
sqlite.close();
must be in a finally close like
Try{
//Do something
}
catch(){
//Catch exception
}
finally{
//Close cursor or/and eventually close database if you don't need it in the future
}
Also don't forget to close database in onDestroy method .
onCreate(Bundle b){
//create database instance
}
onDestroy{
//close db
}