я читал очень об этой ошибке, но я решил спросить вас снова;-).
Мы разработали приложение для Android и получение ошибки, что курсор или databased не были закрыты, когда мы открываем представление, позвольте применению открыться за ночь и пункт нажатия в списке деятельности в следующий день.
Файл журнала:
[12-28 8:48:19.499 8736:0x2221 E/Database]
близко() явно никогда не обращался база данных
'/data/data/de. DMMobile/databases/dmmobile'
android.database.sqlite. DatabaseObjectNotClosedException: Применение
не закрыл курсор или объект базы данных, который был открыт здесь
в
android.database.sqlite. SQLiteDatabase. (SQLiteDatabase.java:1855)
в
android.database.sqlite. SQLiteDatabase.openDatabase (SQLiteDatabase.java:824)
в
android.database.sqlite. SQLiteDatabase.openOrCreateDatabase (SQLiteDatabase.java:862)
в
android.database.sqlite. SQLiteDatabase.openOrCreateDatabase (SQLiteDatabase.java:855)
в android.app. ContextImpl.openOrCreateDatabase(ContextImpl.java:575)
в
android.content. ContextWrapper.openOrCreateDatabase(ContextWrapper.java:203)
в
android.database.sqlite. SQLiteOpenHelper.getWritableDatabase (SQLiteOpenHelper.java:118)
в dm. Мобильный. Database$OpenHelper. Запрос (Database.java:2229)
в dm. Мобильный. Данные. BaseEntity.fetchData(BaseEntity.java:156)
в dm. Мобильный. Данные. BaseEntity. (BaseEntity.java:45)
в
dm. Мобильный. Данные. TourenplanEinsatzLeistung. (TourenplanEinsatzLeistung.java:36)
в dm. Мобильный. Данные. DataAccess. UpdateLeistung (DataAccess.java:2429)
в
dm. Мобильный. UI.TourenplanEinsatzLeistungListActivity.saveChanges (TourenplanEinsatzLeistungListActivity.java:167)
в
dm. Мобильный. UI.TourenplanEinsatzLeistungListActivity.onBackPressed (TourenplanEinsatzLeistungListActivity.java:295)
в android.app. Activity.onKeyUp (Деятельность java:1898)
в android.view. KeyEvent.dispatch(KeyEvent.java:1287)
в android.app. Activity.dispatchKeyEvent (Деятельность java:2078)
в
com.android.internal.policy.impl. PhoneWindow$DecorView.dispatchKeyEvent (PhoneWindow.java:1683)
в
android.view. ViewRoot.deliverKeyEventToViewHierarchy(ViewRoot.java:2583)
в android.view. ViewRoot.handleFinishedEvent(ViewRoot.java:2558)
в android.view. ViewRoot.handleMessage(ViewRoot.java:1890)
в android.os. Handler.dispatchMessage (Укладчик java:99)
в android.os. Looper.loop (Выполняющий мертвую петлю летчик java:130)
в android.app. ActivityThread.main(ActivityThread.java:3701)
в java.lang.reflect. Method.invokeNative (Собственный метод)
в java.lang.reflect. Method.invoke (Метод java:507)
в
com.android.internal.os. ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:866)
в com.android.internal.os. ZygoteInit.main(ZygoteInit.java:624)
в dalvik.system. NativeStart.main (Собственный метод)
Деятельность UI:
package dm.Mobile.UI;
import java.util.ArrayList;
import java.util.Date;
import de.DMMobile.R;
import dm.Mobile.Helper;
import dm.Mobile.Data.DataAccess;
import dm.Mobile.Data.DataAccessHelper;
import dm.Mobile.Data.KlientInfo;
import dm.Mobile.Data.ListItem;
import dm.Mobile.Data.Tour;
import dm.Mobile.Data.Tourenplan;
import dm.Mobile.Data.TourenplanEinsatz;
import dm.Mobile.Enum.MessageBoxButtons;
import dm.Mobile.Enum.MessageBoxIcon;
import Model.AppContext;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class TourenplanEinsatzListActivity extends ListActivity
{
Context context;
private CustomAdapter mAdapter;
private String key = "";
private String Title = "";
private boolean initialEinsatzCheck = true;
private int OID = -1;
private boolean Readonly = false;
private boolean OneTouch = false;
private int selectedTemplateIndex = 1;//Übernimmt Steuerung, welches Layout geladen werden soll
private String einsatzId = null;
private boolean extendedView = false;
public Object anzeigeRuestzeiten = null;
@Override
protected void onDestroy()
{
DataAccess.closeDatabase(getApplicationContext());
super.onDestroy();
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
AppContext.set(getApplicationContext());
context = this;
key = this.getIntent().getStringExtra("Key");
OID = this.getIntent().getIntExtra("OID", -1);
Readonly = this.getIntent().getBooleanExtra("Readonly", false);
OneTouch = false;
mAdapter = new CustomAdapter();
setListAdapter(mAdapter);
}
private void showInfoMessage(ListItem klient)
{
String message = "";
boolean textExisting = false;
if(hasBirthday(klient))
{
String klientName = klient.GetItemDescription();
String alter = getBirthdayAge(klient);
String geburtstagMessage = "Geburtstag: " + "\r\n" + klientName + " (" + alter + " Jahre)";
message = geburtstagMessage + "\r\n";
textExisting = true;
}
if(!(klient.GetItemDescription4() == null) && !klient.GetItemDescription4().trim().equals(""))
{
if(textExisting)
message += "\r\n";
String infoMessage = "Einsatzinfo: " + "\r\n" + klient.GetItemDescription4().trim();
message += infoMessage;
textExisting = true;
}
MessageBox.Show(context, "Einsatzinfo", message, null, MessageBoxButtons.OK, MessageBoxIcon.Info, null, null);
}
private Boolean hasBirthday(ListItem klient)
{
try
{
Date birthday = klient.getExtDateTime2();
Date today = DataAccessHelper.GetCurrentDateTime();
if(birthday.getDate() == today.getDate() && birthday.getMonth() == today.getMonth())
return true;
else
return false;
}
catch(Exception ex)
{
return false;
}
}
/*
* Liefert das Alter am Geburtstag
* Funktioniert nur am Geburstag des Klienten!!! (da nur Differenz der Jahreszahl berechnet wird)
*/
private String getBirthdayAge(ListItem klient)
{
Date birthday = klient.getExtDateTime2();
Date today = DataAccessHelper.GetCurrentDateTime();
int alter = today.getYear() - birthday.getYear();
return Integer.toString(alter);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
//TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
mAdapter.setViewSelected(position);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
if(Readonly)
inflater.inflate(R.menu.tourenplaneinsatzmenureadonly, menu);
else
inflater.inflate(R.menu.tourenplaneinsatzmenu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//Handle item selection
switch (item.getItemId()) {
case R.id.neuerEinsatz:
onClickNeuerEinsatz();
return true;
case R.id.sonstigeZeiten:
onClickSonstigeZeiten();
return true;
case R.id.patientenliste:
onClickPatientenListe();
return true;
case R.id.mitarbeiterliste:
onClickMitarbeiterListe();
return true;
case R.id.tourUebernehmen:
onClickTourUebernehmen();
return true;
}
return true;
}
private void onClickNeuerEinsatz()
{
Intent intent = new Intent();
intent.putExtra("AtopId", DataAccess.CurrentTour);
intent.putExtra("EinsatzRequired", true);
intent.setClass(context, KlientListActivity.class);
this.startActivityForResult(intent, 1);
}
private void onClickSonstigeZeiten()
{
Intent intent = new Intent();
intent.setClass(context, SonstigeZeitenListActivity.class);
this.startActivityForResult(intent, 2);
}
private void onClickPatientenListe()
{
Intent intent = new Intent();
intent.setClass(context, KlientListActivity.class);
this.startActivity(intent);
}
private void onClickMitarbeiterListe()
{
Intent intent = new Intent();
intent.setClass(context, MitarbeiterListActivity.class);
this.startActivity(intent);
}
private void onClickTourUebernehmen()
{
Intent intent = new Intent();
intent.setClass(context, TourenplanAdoptionEinsatzListActivity.class);
this.startActivityForResult(intent, 5);
}
public void onClickButton1(View v)
{
onClickInfo();
}
private boolean Anmelden(Object einsatzId)
{
DialogInterface.OnClickListener dlgResult = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//TODO Auto-generated method stub
if (which == DialogInterface.BUTTON_POSITIVE) {
Anmelden2();
}
}
};
boolean result = false;
this.einsatzId = einsatzId.toString();
ListItem item = mAdapter.getSelectedItem();
if (item != null)
{
if (!item.ExtBoolValue && !Readonly)
{
if (item.GetImageResourceId3() > 0)
MessageBox.Show(this, "Anmelden", "Klient ist nicht in Pflege!\n\nEinsatz bei Klient " + item.GetItemDescription() + " beginnen?", null, MessageBoxButtons.YesNo, MessageBoxIcon.GeriatricsClose, dlgResult, this.einsatzId);
else
MessageBox.Show(this, "Anmelden", "Einsatz bei Klient " + item.GetItemDescription() + " beginnen?", null, MessageBoxButtons.YesNo, MessageBoxIcon.Question, dlgResult, this.einsatzId);
}
else
Anmelden2(this.einsatzId);
}
return result;
}
private void Anmelden2()
{
//Überladung nötig, da in innerer Methode im onClickListener kein direkter Verweis auf Variable einsatzId möglich ist
Anmelden2(this.einsatzId);
}
private void Anmelden2(Object einsatzId)
{
boolean result = false;
TourenplanEinsatz einsatz = DataAccess.GetTourenplanEinsatz(context, einsatzId.toString());
if (einsatz != null)
{
Intent intent = new Intent();
intent.putExtra("ApepId", einsatz.ApepId);
if (Readonly || einsatz.ApepErledigt)
intent.putExtra("Readonly", true);
else
{
intent.putExtra("Readonly", false);
DataAccess.StartEinsatz(context, einsatz.ApepId);
}
intent.setClass(context, TourenplanEinsatzLeistungListActivity.class);
this.startActivityForResult(intent, 0);
}
}
public void onClickButton2(View v)
{
Button button = (Button) v;
//In onClickAnmelden wird auch der Fall "Ansicht" berücksichtigt (Apeperledigt = true)
onClickAnmelden();
}
private void onClickAnsicht()
{
}
private void onClickAnmelden()
{
ListItem item = mAdapter.getSelectedItem();
Anmelden(item.GetItemId());
}
private void onClickInfo()
{
//Klientinfo aufrufen
ListItem item = mAdapter.getSelectedItem();
Intent intent = new Intent(this, KlientInfoActivity.class);
intent.putExtra("skliId", item.GetKey2());
intent.putExtra("apepId", item.GetItemId());
this.startActivity(intent);
}
private void onClickAnrufen()
{
ListItem item = mAdapter.getSelectedItem();
KlientInfo info = DataAccess.GetKlientInfo(context, item.GetKey2());
Helper.MakeCall(context, findViewById(android.R.id.content).getRootView(),info.GetXadrTelefon().trim());
}
private void onClickKontakte()
{
ListItem item = mAdapter.getSelectedItem();
Intent intentKontakt = new Intent(this, KlientKontaktActivity.class);
intentKontakt.putExtra("skliId", item.GetKey2());
this.startActivity(intentKontakt);
}
private void onClickLeistungen()
{
ListItem item = mAdapter.getSelectedItem();
Intent intentLeistung = new Intent(this, TourenplanEinsatzLeistungListActivity.class);
intentLeistung.putExtra("ApepId", item.GetItemId());
intentLeistung.putExtra("ReadOnly", true);
this.startActivity(intentLeistung);
}
public void onClickImage2(View v)
{
ListItem item = (ListItem) v.getTag();
//Falls Einsatzinfo hinterlegt wurde
if(item.GetItemDescription4() != null && !item.GetItemDescription4().equals("") || hasBirthday(item))
{
showInfoMessage(item);
//MessageBox.Show(context, "Einsatzinfo", item.GetItemDescription4().trim(), null, MessageBoxButtons.OK, MessageBoxIcon.Info, null, null);
}
}
public void onClickImage1(View v)
{
ListItem currentItem = mAdapter.getSelectedItem();
//Bild "Pfeil nach unten" wurde gedrückt: Kontextmenü einblenden
//Anzeige-Text patchen
String AnmeldenAnsicht = "Anmelden";
if (Readonly || currentItem.ExtBoolValue)//Falls readonly oder aktueller Einsatz erledigt
AnmeldenAnsicht = "Ansicht";
final CharSequence[] items = { AnmeldenAnsicht, "Info", "Anrufen", "Kontakte", "Leistungen" };
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(currentItem.GetItemText());
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (item)
{
case 0:
onClickAnmelden();
break;
case 1:
onClickInfo();
break;
case 2:
onClickAnrufen();
break;
case 3:
onClickKontakte();
break;
case 4:
onClickLeistungen();
break;
}
}
});
AlertDialog alert = builder.create();
alert.show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
//TODO Auto-generated method stub
if (resultCode == RESULT_OK)
{
switch (requestCode)
{
case 0:
String id = data.getStringExtra("ApepId");
if(!(id == null || id == ""))
{
//Einsatz beenden
ListItem item = mAdapter.getSelectedItem();
DataAccess.FinishEinsatz(context, id);
if(item != null)
{
item.SetButton1Text("Ansicht");
item.ExtBoolValue = true;
}
//Nächsten Eintrag aus der Liste selektieren
mAdapter.selectNextItem();
mAdapter.datenBinden();
mAdapter.notifyDataSetChanged();
}
break;
case 1:
String apepId = "";
String klientSkliId = "";
int apepEinsatz = 0;
if (data != null)
{
klientSkliId = data.getStringExtra("KlientSkliId");
apepEinsatz = data.getIntExtra("apepEinsatz", 1);
if (!klientSkliId.equals(""))
apepId = DataAccess.AddNewEinsatz(context, key, klientSkliId, apepEinsatz);
if (apepId != null)
{
Intent intent = new Intent();
intent.putExtra("ApepId", apepId);
intent.putExtra("Readonly", false);
intent.setClass(this, TourenplanEinsatzLeistungListActivity.class);
startActivityForResult(intent, 0);
}
}
break;
case 2:
if (data != null)
{
klientSkliId = data.getStringExtra("KlientSkliId");
apepEinsatz = data.getIntExtra("apepEinsatz", 1);
ArrayList leistungen = new ArrayList();
leistungen.add(data.getStringExtra("LeistungSleiId"));
String apepId2 = "";
if (!klientSkliId.equals(""))
apepId2 = DataAccess.AddNewEinsatz(context, DataAccess.CurrentTour, klientSkliId, apepEinsatz);
DataAccess.AddLeistungen(context, apepId2, leistungen);
Intent intent = new Intent();
intent.putExtra("ApepId", apepId2);
intent.putExtra("Readonly", false);
intent.setClass(context, TourenplanEinsatzLeistungListActivity.class);
startActivityForResult(intent, 0);
/*mAdapter.datenBinden();
mAdapter.notifyDataSetChanged();*/
}
break;
case 5:
//Tour übernehmen
if(data != null)
{
ArrayList apepIds = data.getStringArrayListExtra("Selected");
DataAccess.EinsaetzeAssignTo(context, apepIds);
mAdapter.datenBinden();
mAdapter.notifyDataSetChanged();
}
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private class CustomAdapter extends BaseAdapter
{
private LayoutInflater mInflater;
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;
private ArrayList mData = new ArrayList();
public CustomAdapter()
{
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
datenBinden();
}
public void datenBinden()
{
ArrayList dataSource = DataAccess.GetTourenplanEinsaetze(context, key);//Key = atopid
Tourenplan tourenplan = DataAccess.GetTourenplan(context, key);
anzeigeRuestzeiten = DataAccess.GetParameter(context, "DMMOBILE", "ANZEIGERUESTZEITEN");
if (tourenplan != null)
{
Tour tour = DataAccess.GetTour(context, tourenplan.TourAtouId);
if (tour != null)
{
Title = "Einsätze - " + tour.AtouBezeichnung;
setTitle(Title);
}
}
//Prüfen, ob Einsätze vorhanden sind
if (dataSource.size() > 0 || !initialEinsatzCheck)
mData = dataSource;
else
{
OpenOptionsMenu();
initialEinsatzCheck = false;
}
Object setting = DataAccess.GetParameter(context, "DMMOBILE", "TOURENPLANEINSATZLISTACTIVITYTYPE");
if (setting != null && Double.parseDouble(setting.toString()) > 0)
{
extendedView = true;
/*if (Double.parseDouble(setting.toString()) > 0)
{
selectedTemplateIndex = 3;
}*/
}
else
extendedView = false;
//selectedTemplateIndex = 1;
if (dataSource.size() < 1)
OpenOptionsMenu();
}
private void OpenOptionsMenu()
{
//TODO: Optionsmenü programmatisch öffnen
}
public void addItem(final ListItem item) {
mData.add(item);
//notifyDataSetChanged();
}
public int getCount() {
//TODO Auto-generated method stub
return mData.size();
}
public Object getItem(int position) {
return mData.get(position);
}
public ListItem getSelectedItem()
{
if(mData.size() > FLAG_SELECTED && FLAG_SELECTED >= 0)
return mData.get(FLAG_SELECTED);
else
return null;
}
public long getItemId(int arg0) {
//TODO Auto-generated method stub
return 0;
}
int FLAG_SELECTED = 0;
public void setViewSelected(int position) {
//Flag merken
this.FLAG_SELECTED = position;
//Refresh erzwingen
this.notifyDataSetChanged();
}
public void selectNextItem()
{
//Nächsten Eintrag aus der Liste - sofern vorhanden - selektieren
if(mData.size() - 1 > this.FLAG_SELECTED)
{
this.FLAG_SELECTED++;
}
}
private Boolean ShowKlientNummer()
{
return DataAccess.ParameterIsSet(getApplicationContext(), "TOURENPLANEINSATZLISTNUMMER");
}
public View getView(int position, View convertView, ViewGroup parent)
{
TextView view = null;
if(this.FLAG_SELECTED == position)
{
if(extendedView)
convertView = mInflater.inflate(R.layout.tourenplaneinsatzrowlayoutselectedex, null);
else
convertView = mInflater.inflate(R.layout.tourenplaneinsatzrowlayoutselected, null);
}
else
convertView = mInflater.inflate(R.layout.tourenplaneinsatzrowlayout, null);
//Datensatz auslesen
ListItem item = mData.get(position);
if(item.getExtIntValue() > 0)//Einsatz ist Rüstzeit
{
if(anzeigeRuestzeiten != null && Integer.parseInt(anzeigeRuestzeiten.toString()) > 0)//Parameter ist auf Leistung gesetzt?
{
//Erste Leistung ermitteln
ArrayList list = DataAccess.GetEinsatzLeistungen(context, item.GetItemId(), true);
if(list.size() > 0)
{
ListItem leistung = list.get(0);
//HH:MM, Leistung
item.SetItemText(item.getExtStringValue() + ", " + leistung.GetItemText());
//Übrige Felder leer lassen
item.SetItemDescription("");
item.SetInfoField1("");
item.SetInfoField3("");
item.SetItemStrasse("");
item.SetItemDescription2("");
item.SetItemDescription3("");
}
}
}
//Text der Row setzen (unselected/selected)
view = (TextView)convertView.findViewById(R.id.text1);
view.setText(item.GetItemText());
view = (TextView)convertView.findViewById(R.id.text2);
String klientName = item.GetItemDescription();
//Klientennummer hinter Name anzeigen, falls entsprechender Parameter gesetzt + Name nicht leer
if(ShowKlientNummer() && !klientName.trim().equals(""))
klientName += " (" + item.getExtStringValue2() + ")";
view.setText(klientName);
if(this.FLAG_SELECTED == position)
{
//Nur selected-Layout
TextView textview3 = (TextView) convertView.findViewById(R.id.text3);
TextView textview4 = (TextView) convertView.findViewById(R.id.text4);
TextView textview5 = (TextView) convertView.findViewById(R.id.text5);
if(extendedView)
{
//Zusätzliche Textfelder einbinden
TextView textview6 = (TextView) convertView.findViewById(R.id.text6);
TextView textview7 = (TextView) convertView.findViewById(R.id.text7);
//Im Falle von Rüstzeiten immer leer lassen!
if(item.getExtIntValue() > 0 && anzeigeRuestzeiten != null && Integer.parseInt(anzeigeRuestzeiten.toString()) > 0)
{
textview3.setText("");
textview4.setText("");
}
else
{
textview3.setText(item.GetInfoField3() + " Minute(n) Einsatzdauer");
textview4.setText("Schlüssel: " + item.GetInfoField1());
}
textview5.setText(item.GetItemStrasse());
textview6.setText(item.GetItemDescription2());
textview7.setText(item.GetItemDescription3());
}
else
{
textview3.setText(item.GetItemStrasse());
textview4.setText(item.GetItemDescription2());
textview5.setText(item.GetItemDescription3());
}
//Nur falls selektiert
//Linken Button beschriften
Button button2 = (Button) convertView.findViewById(R.id.button2);
if (Readonly || item.ExtBoolValue)
item.SetButton1Text("Ansicht");
else
item.SetButton1Text("Anmelden");
button2.setText(item.GetButton1Text());
button2.setTextColor(Helper.getButtonTextColor());
//Rechten Button beschriften
Button button1 = (Button) convertView.findViewById(R.id.button1);
button1.setText("Info");
button1.setTextColor(Helper.getButtonTextColor());
if (item.GetImageResourceId3() > 0)
{
ImageView einsatz_checked = (ImageView) convertView.findViewById(R.id.image1);
einsatz_checked.setImageResource(item.GetImageResourceId3());
}
}
else
{
//Falls Einsatz bereits erledigt: Haken setzen
if(item.ExtBoolValue)
{
ImageView einsatz_checked = (ImageView) convertView.findViewById(R.id.image1);
einsatz_checked.setImageResource(R.drawable.symbol_check);
}
else if (item.GetImageResourceId3() > 0) {
ImageView einsatz_checked = (ImageView) convertView.findViewById(R.id.image1);
einsatz_checked.setImageResource(item.GetImageResourceId3());
}
}
ImageView info = (ImageView) convertView.findViewById(R.id.image2);
info.setTag(item);//ListItem als Tag an Icon speichern
//Falls in Einsatzinfo (ApepKartei) etwas steht
if(item.GetItemDescription4() != null && item.GetItemDescription4().trim() != "" || hasBirthday(item))
{
info.setImageResource(R.drawable.symbol_information_2);
}
else
{
info.setClickable(false);//Nicht anklickbar, damit ListItem beim Klick in leere Fläche selektiert wird
}
return convertView;
}
}
}
Мы используем класс для DataAccess, который использует SQLiteOpenHelper. Курсоры, которые являются открытыми в классе DataAccess, будут закрыты (в классе DataAccess) прежде, чем возвратить ценности.
Я прибавил разрушение closeDatabase метода, который закрывает соединение с базой данных. Возможно, отвергание - onDestroy неправильное место для этого?
Спасибо за парней помощи.
Didikong