SQLite Connection leaked although everything closed
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
SQLiteOpenHelperDeclare 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 withnew 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);
}
}
Sqlite connection Leaked in android studio despite everything is closed
There are multiple things you did wrong in the getQuestionMode
method. Those being:
- closing the db connection after reading the first item
- closing cursor outside finally block
Also, I would avoid this line if (c == null) return null;
.
I would recommend using singleton pattern to access data.
To fix your current issue try this:
public List<Question> getQuestionMode(String mode) {
List<Question> listQuestion = new ArrayList<>();
SQLiteDatabase db = this.getWritableDatabase();
Cursor c = null;
int limit = 0;
if (mode.equals(Common.MODE.EASY.toString()))
limit = 20;
else if (mode.equals(Common.MODE.MEDIUM.toString()))
limit = 20;
else if (mode.equals(Common.MODE.HARD.toString()))
limit = 30;
else if (mode.equals(Common.MODE.HARDEST.toString()))
limit = 30;
try {
c = db.rawQuery(String.format("SELECT * FROM Question ORDER BY Random() LIMIT %d", limit), null);
if(c.moveToFirst()) {
do {
int Id = c.getInt(c.getColumnIndex("ID"));
//int cid= c.getInt(c.getColumnIndex("C_ID"));
String Qus = c.getString(c.getColumnIndex("Qus"));
String AnswerA = c.getString(c.getColumnIndex("AnswerA"));
String AnswerB = c.getString(c.getColumnIndex("AnswerB"));
String AnswerC = c.getString(c.getColumnIndex("AnswerC"));
String AnswerD = c.getString(c.getColumnIndex("AnswerD"));
String CorrectAnswer = c.getString(c.getColumnIndex("CorrectAnswer"));
Question question = new Question(Id, Qus, AnswerA, AnswerB, AnswerC, AnswerD, CorrectAnswer);
listQuestion.add(question);
}
while (c.moveToNext());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(null != c)
c.close();
if(null != db)
db.close();
}
return listQuestion;
}
Update:
You still have an open database in openDataBase()
method. Also add finally block to checkDataBase()
.
You could try rewriting your helper to this(See the example!).
Unable to prevent SQLiteConnection object leakage
You say you looked into many questions online, but you definitely didn't look into this or this. It literally cropped up 5 minutes into searching.
Anyways, if I were you, to resolve this issue, I would include a finally
clause when closing the connection. This means that you would be declaring your database helper as a static instance variable and use the Abstract Factory pattern to guarantee the singleton property.
Your warning is happening because you are not ensuring 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.
Here is the code:
public ArrayList<ItemSubject> loadDataSubject() {
ArrayList<ItemSubject> arrayList = new ArrayList<>();
String select = "select * from " + TABLE_SUBJECT;
Cursor cursor = getData(select);
if (cursor != null && cursor.getCount() > 0) {
try {
cursor.moveToFirst();
for (int i = 0; i < cursor.getCount(); i++) {
String id = cursor.getString(cursor.getColumnIndex(TAG_ID));
String course = cursor.getString(cursor.getColumnIndex(TAG_COURSE_NAME));
....
ItemSubject objItem = new ItemSubject(id, courseId,...);
arrayList.add(objItem);
cursor.moveToNext();
}
finally {
if (cursor != null)
cursor.close();
}
}
return arrayList;
}
But since you say that my only contribution to your question (which had already been answered in other posts) is that of refining your lack of elementary English, then I am not that sure if you can accept this as an adequate answer.
SQLite Android Leaked
you are not closing db_writer
Regardless of not using it, you still open it at the intialisation.
this.db_writer = bd.getWritableDatabase();
It is recommended to close it also, else the connection is leaked.
this.db_writer.close();
Better yet would be to write a helper method that you can call:
public void CloseDb()
{
if(db_writer.isOpen())
db_writer.close();
if(db_reader.isOpen())
db_reader.close();
}
You can then call CloseDb()
where appropriate (still need to close cursors as per normal).
This method checks its open before closing, so does not try to close it twice.
You do not normally close a Databasehelper
from within itself though. Usually once you have finished using it in an from an activity
. (Call CloseDb()
from your activity once you have finished, aka, onPause
or onDestroy
.)
Android SQLite leaked
Each Cursor
should be closed when you're finished with it. The traditional way to do this is:
Cursor cursor = db.query(...);
try {
// read data from the cursor in here
} finally {
cursor.close();
}
But now, with try-with-resources, it can be much more concise:
try (Cursor cursor = db.query(...)) {
// read data from the cursor in here
}
Related Topics
How to Read All The Coming Notifications in Android
How to Pin a Certificate with Square Okhttp
Using Android Google Maps V2 with Custom View or Imageview
What's "Requestcode" Used for on Pendingintent
Change The Background Color of a Pop-Up Dialog
How to Make Facebook's App New Menu on Android
Extract Notification Text from Parcelable, Contentview or Contentintent
How to Bring a Background Task to The Front Below Honeycomb
What Is Use of Cursor in Android Development
Firebase - How to Delete Many Entries at Once
Use of Context to Start Another Activity
How Surfaceholder Callbacks Are Related to Activity Lifecycle
Security "Crypto" Provider Deprecated in Android N
How to Set Up Gradle and Android Studio to Do Release Build
How to Replace a Fragment on Button Click of That Fragment
Best Way to Show a Loading/Progress Indicator