Où dois-je appeler close () sur le code?
LogCat renvoie cette erreur:
close () n'a jamais été explicitement appelé dans la base de données Android.database.sqlite.DatabaseObjectNotClosedException: l'application n'a pas fermé le curseur ou l'objet de base de données ouvert ici
L'erreur est la suivante:
> 12-16 17:24:50.886: ERROR/Database(10982): close() was never explicitly called on database '/data/data/com.psyhclo/databases/calls.db'
12-16 17:24:50.886: ERROR/Database(10982): Android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
12-16 17:24:50.886: ERROR/Database(10982): at Android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.Java:1827)
12-16 17:24:50.886: ERROR/Database(10982): at Android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.Java:820)
12-16 17:24:50.886: ERROR/Database(10982): at Android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.Java:854)
12-16 17:24:50.886: ERROR/Database(10982): at Android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.Java:847)
12-16 17:24:50.886: ERROR/Database(10982): at Android.app.ContextImpl.openOrCreateDatabase(ContextImpl.Java:541)
12-16 17:24:50.886: ERROR/Database(10982): at Android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.Java:203)
12-16 17:24:50.886: ERROR/Database(10982): at Android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.Java:118)
12-16 17:24:50.886: ERROR/Database(10982): at com.psyhclo.CallDataHelper.<init>(CallDataHelper.Java:27)
12-16 17:24:50.886: ERROR/Database(10982): at com.psyhclo.RatedCalls.fillList(RatedCalls.Java:66)
12-16 17:24:50.886: ERROR/Database(10982): at com.psyhclo.RatedCalls.access$0(RatedCalls.Java:63)
12-16 17:24:50.886: ERROR/Database(10982): at com.psyhclo.RatedCalls$RatedCallsContentObserver.onChange(RatedCalls.Java:58)
12-16 17:24:50.886: ERROR/Database(10982): at Android.database.ContentObserver$NotificationRunnable.run(ContentObserver.Java:43)
12-16 17:24:50.886: ERROR/Database(10982): at Android.os.Handler.handleCallback(Handler.Java:587)
12-16 17:24:50.886: ERROR/Database(10982): at Android.os.Handler.dispatchMessage(Handler.Java:92)
12-16 17:24:50.886: ERROR/Database(10982): at Android.os.Looper.loop(Looper.Java:123)
12-16 17:24:50.886: ERROR/Database(10982): at Android.app.ActivityThread.main(ActivityThread.Java:3647)
12-16 17:24:50.886: ERROR/Database(10982): at Java.lang.reflect.Method.invokeNative(Native Method)
12-16 17:24:50.886: ERROR/Database(10982): at Java.lang.reflect.Method.invoke(Method.Java:507)
12-16 17:24:50.886: ERROR/Database(10982): at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:839)
12-16 17:24:50.886: ERROR/Database(10982): at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:597)
12-16 17:24:50.886: ERROR/Database(10982): at dalvik.system.NativeStart.main(Native Method)
voici mon code.
L'activité:
public class RatedCalls extends ListActivity {
private static final String LOG_TAG = "RATEDCALLSOBSERVER";
private Handler handler = new Handler();
private SQLiteDatabase db;
private CallDataHelper cdh;
StringBuilder sb = new StringBuilder();
OpenHelper openHelper = new OpenHelper(RatedCalls.this);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerContentObservers();
Log.d("FILLLIST", "calling from onCreate()");
}
class RatedCallsContentObserver extends ContentObserver {
public RatedCallsContentObserver(Handler h) {
super(h);
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(boolean selfChange) {
Log.d(LOG_TAG, "RatedCallsContentObserver.onChange( " + selfChange
+ ")");
super.onChange(selfChange);
fillList();
onStop();
}
}
private void fillList() {
db = openHelper.getWritableDatabase();
cdh = new CallDataHelper(this);
String lastDate = cdh.selectDate();
Cursor cursor = getContentResolver().query(
Android.provider.CallLog.Calls.CONTENT_URI, null,
Android.provider.CallLog.Calls.DATE + " < ?",
new String[] { lastDate },
Android.provider.CallLog.Calls.DATE + " DESC ");
Log.d("FILLLIST", "Calling from filllist");
startManagingCursor(cursor);
int numberColumnId = cursor
.getColumnIndex(Android.provider.CallLog.Calls.NUMBER);
int durationId = cursor
.getColumnIndex(Android.provider.CallLog.Calls.DURATION);
int contactNameId = cursor
.getColumnIndex(Android.provider.CallLog.Calls.CACHED_NAME);
int numTypeId = cursor
.getColumnIndex(Android.provider.CallLog.Calls.CACHED_NUMBER_TYPE);
Date dt = new Date();
int hours = dt.getHours();
int minutes = dt.getMinutes();
int seconds = dt.getSeconds();
String currTime = hours + ":" + minutes + ":" + seconds;
SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
Date date = new Date();
ArrayList<String> callList = new ArrayList<String>();
cursor.moveToFirst();
String contactNumber = cursor.getString(numberColumnId);
String contactName = cursor.getString(contactNameId);
String duration = cursor.getString(durationId);
// String callDate = DateFormat.getDateInstance().format(dateId);
String numType = cursor.getString(numTypeId);
ContentValues values = new ContentValues();
values.put("contact_id", 1);
values.put("contact_name", contactName);
values.put("number_type", numType);
values.put("contact_number", contactNumber);
values.put("duration", duration);
values.put("date", dateFormat.format(date));
values.put("current_time", currTime);
values.put("cont", 1);
getBaseContext().getContentResolver().notifyChange(
Android.provider.CallLog.Calls.CONTENT_URI,
new RatedCallsContentObserver(handler));
db.insert(CallDataHelper.TABLE_NAME, null, values);
Toast.makeText(getBaseContext(), "Inserted!", Toast.LENGTH_LONG);
callList.add("Contact Number: " + contactNumber + "\nContact Name: "
+ contactName + "\nDuration: " + duration + "\nDate: "
+ dateFormat.format(date));
setListAdapter(new ArrayAdapter<String>(this, R.layout.listitem,
callList));
ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new Android.widget.AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(getApplicationContext(),
((TextView) view).getText(), Toast.LENGTH_SHORT).show();
}
});
}
public void registerContentObservers() {
this.getApplicationContext()
.getContentResolver()
.registerContentObserver(
Android.provider.CallLog.Calls.CONTENT_URI, true,
new RatedCallsContentObserver(handler));
}
public void unregisterContentObservers() {
this.getApplicationContext()
.getContentResolver()
.unregisterContentObserver(
new RatedCallsContentObserver(handler));
}
}
Et le DatabaseHelper
public class CallDataHelper {
private static final String DATABASE_NAME = "calls.db";
private static final int DATABASE_VERSION = 1;
protected static final String TABLE_NAME = "contact_data";
private Context context;
private SQLiteDatabase db;
public CallDataHelper(Context context) {
this.context = context;
OpenHelper openHelper = new OpenHelper(this.context);
this.db = openHelper.getWritableDatabase();
}
public boolean insert(Integer cId, String cName, String numType,
String cNum, String dur, String date, String currTime) {
this.db.execSQL("insert into "
+ TABLE_NAME
+ " (contact_id, contact_name, number_type, contact_number, duration, date, current_time, cont) "
+ " values( " + cId + ", " + cName + ", " + numType + ", "
+ cNum + ", " + dur + ", " + date + ", " + currTime + ", 1)");
return true;
}
public void update(String Word) {
this.db.execSQL("UPDATE words SET cont = cont + 1 WHERE (Word= ?)",
new String[] { Word });
}
public void deleteAll() {
this.db.delete(TABLE_NAME, null, null);
}
public String selectDate() {
String date = "";
Cursor cursor = this.db.query(TABLE_NAME, new String[] { "date" },
"id = ?", new String[] { "max(id)" }, null, null, null);
if (cursor.moveToFirst()) {
do {
date = cursor.getString(0);
} while (cursor.moveToNext());
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
return date;
} else {
return "";
}
}
public List<String> selectAll() {
List<String> list = new ArrayList<String>();
Cursor cursor = this.db.query(TABLE_NAME, new String[] { "Word" },
null, null, null, null, "cont desc");
if (cursor.moveToFirst()) {
do {
list.add(cursor.getString(0).toUpperCase());
} while (cursor.moveToNext());
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
return list;
}
public static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "
+ TABLE_NAME
+ "(id INTEGER PRIMARY KEY AUTOINCREMENT, contact_id INTEGER, contact_name VARCHAR(50), number_type VARCHAR(50), contact_number VARCHAR(50), duration TIME, date DATE, current_time TIME, cont INTEGER)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w("RatedCalls Database",
"Upgrading database, this will drop tables and recreate.");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}
_ ___ (Modifié) __ _ ____
C'est ce que je reçois maintenant. Lorsque j'effectue un nouvel appel, le Logcat le renvoie.
12-16 18:55:27.365: ERROR/AndroidRuntime(767): FATAL EXCEPTION: main
12-16 18:55:27.365: ERROR/AndroidRuntime(767): Java.lang.IllegalStateException: database not open
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.Java:1230)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.Java:1189)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.Java:1271)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at com.psyhclo.CallDataHelper.selectDate(CallDataHelper.Java:61)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at com.psyhclo.RatedCalls.fillList(RatedCalls.Java:98)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at com.psyhclo.RatedCalls.access$0(RatedCalls.Java:96)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at com.psyhclo.RatedCalls$RatedCallsContentObserver.onChange(RatedCalls.Java:91)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Android.database.ContentObserver$NotificationRunnable.run(ContentObserver.Java:43)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Android.os.Handler.handleCallback(Handler.Java:587)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Android.os.Handler.dispatchMessage(Handler.Java:92)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Android.os.Looper.loop(Looper.Java:123)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Android.app.ActivityThread.main(ActivityThread.Java:3647)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Java.lang.reflect.Method.invokeNative(Native Method)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at Java.lang.reflect.Method.invoke(Method.Java:507)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:839)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:597)
12-16 18:55:27.365: ERROR/AndroidRuntime(767): at dalvik.system.NativeStart.main(Native Method)
Et c'est mon activité:
public class RatedCalls extends ListActivity {
private static final String LOG_TAG = "RATEDCALLSOBSERVER";
private Handler handler = new Handler();
private SQLiteDatabase db;
private CallDataHelper cdh;
StringBuilder sb = new StringBuilder();
OpenHelper openHelper = new OpenHelper(RatedCalls.this);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerContentObservers();
Log.d("FILLLIST", "calling from onCreate()");
cdh = new CallDataHelper(this);
db = openHelper.getWritableDatabase();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (openHelper != null) {
openHelper.close();
}
if (cdh != null) {
cdh.close();
}
}
@Override
protected void onPause() {
super.onPause();
if (openHelper != null) {
openHelper.close();
}
if (cdh != null) {
cdh.close();
}
}
@Override
public void onResume() {
super.onResume();
openOrCreateDatabase("calls.db", SQLiteDatabase.CREATE_IF_NECESSARY,
null);
}
class RatedCallsContentObserver extends ContentObserver {
public RatedCallsContentObserver(Handler h) {
super(h);
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(boolean selfChange) {
Log.d(LOG_TAG, "RatedCallsContentObserver.onChange( " + selfChange
+ ")");
super.onChange(selfChange);
fillList();
}
}
private void fillList() {
String lastDate = cdh.selectDate();
Cursor cursor = getContentResolver().query(
Android.provider.CallLog.Calls.CONTENT_URI, null,
Android.provider.CallLog.Calls.DATE + " < ?",
new String[] { lastDate },
Android.provider.CallLog.Calls.DATE + " DESC ");
Log.d("FILLLIST", "Calling from filllist");
startManagingCursor(cursor);
int numberColumnId = cursor
.getColumnIndex(Android.provider.CallLog.Calls.NUMBER);
int durationId = cursor
.getColumnIndex(Android.provider.CallLog.Calls.DURATION);
int contactNameId = cursor
.getColumnIndex(Android.provider.CallLog.Calls.CACHED_NAME);
int numTypeId = cursor
.getColumnIndex(Android.provider.CallLog.Calls.CACHED_NUMBER_TYPE);
Date dt = new Date();
int hours = dt.getHours();
int minutes = dt.getMinutes();
int seconds = dt.getSeconds();
String currTime = hours + ":" + minutes + ":" + seconds;
SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
Date date = new Date();
ArrayList<String> callList = new ArrayList<String>();
cursor.moveToFirst();
String contactNumber = cursor.getString(numberColumnId);
String contactName = cursor.getString(contactNameId);
String duration = cursor.getString(durationId);
// String callDate = DateFormat.getDateInstance().format(dateId);
String numType = cursor.getString(numTypeId);
stopManagingCursor(cursor);
ContentValues values = new ContentValues();
values.put("contact_id", 1);
values.put("contact_name", contactName);
values.put("number_type", numType);
values.put("contact_number", contactNumber);
values.put("duration", duration);
values.put("date", dateFormat.format(date));
values.put("current_time", currTime);
values.put("cont", 1);
getBaseContext().getContentResolver().notifyChange(
Android.provider.CallLog.Calls.CONTENT_URI,
new RatedCallsContentObserver(handler));
db.insert(CallDataHelper.TABLE_NAME, null, values);
Toast.makeText(getBaseContext(), "Inserted!", Toast.LENGTH_LONG);
callList.add("Contact Number: " + contactNumber + "\nContact Name: "
+ contactName + "\nDuration: " + duration + "\nDate: "
+ dateFormat.format(date));
setListAdapter(new ArrayAdapter<String>(this, R.layout.listitem,
callList));
ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new Android.widget.AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(getApplicationContext(),
((TextView) view).getText(), Toast.LENGTH_SHORT).show();
}
});
}
public void registerContentObservers() {
this.getApplicationContext()
.getContentResolver()
.registerContentObserver(
Android.provider.CallLog.Calls.CONTENT_URI, true,
new RatedCallsContentObserver(handler));
}
public void unregisterContentObservers() {
this.getApplicationContext()
.getContentResolver()
.unregisterContentObserver(
new RatedCallsContentObserver(handler));
}
}
Je pense que le problème est que vous devez fermer la base de données lorsque votre activité est détruite. Essayez d'ajouter ceci à votre activité:
@Override
protected void onDestroy() {
super.onDestroy();
if (openHelper != null) {
openHelper.close();
}
if (cdh != null) {
cdh.close();
}
}
et ajoutez ceci à votre classe CallDataHelper:
public void close() {
// NOTE: openHelper must now be a member of CallDataHelper;
// you currently have it as a local in your constructor
if (openHelper != null) {
openHelper.close();
}
}
EDIT 2: Modifiez également le constructeur CallDataHelper en:
// Declare openHelper as a member variable
OpenHelper openHelper = null;
public CallDataHelper(Context context) {
this.context = context;
openHelper = new OpenHelper(this.context);
this.db = openHelper.getWritableDatabase();
}
Cela devrait fermer correctement la connexion à la base de données qui a été créée par vos deux instances OpenHelper
.
( A MODIFIÉ pour refléter le fait que vous utilisez deux instances OpenHelper.)
Comprendre littéralement que la base de données n'est pas normalement fermée, la réelle parce que l'instanciation répétée de votre base de données, ou vous connecter vous avez été configuré, et vous et essayez d'ouvrir une autre connexion il n'y aura aucune exception. La solution est donc, assurez-vous que vous ouvrez uniquement une connexion.
Je soupçonne que fillList()
est appelée plusieurs fois au cours d'une RatedCalls
"session". Ainsi, chaque fois que vous créez une nouvelle instance de Cursor
et que vous appelez startManagingCursor()
dessus. Ce faisant, vous accumulez une liste de curseurs - ArrayList<ManagedCursor>
Pour être précis qui est déclarée dans ListActivity
. Normalement, ils (les curseurs qui sont gérés) seront fermés automatiquement dans ListActivity.onDestroy()
. Pourriez-vous confirmer que votre RatedCalls
passe réellement onDestroy()
.
Remplacez-vous en quelque sorte la ListActivity.onDestroy()
dans RatedCalls
. Si oui, appelez-vous super.onDestroy()
(vous devez l'appeler)?
Je n'ai pas le but d'appeler onStop()
dans RatedCallsContentObserver.onChange()
après fillList();
. Je crois que les rappels du cycle de vie des activités devraient être appelés par le système d'exploitation niquement. Alors peut-être qu'en appelant manuellement un rappel du cycle de vie vous empêchez en quelque sorte que onDestroy()
soit appelé par OS (juste une supposition)?
MISE À JOUR:
Dan Breslau a raison - c'est db
objet qui n'est pas fermé. Désolé pour avoir induit en erreur.