11 Ağustos 2011 Perşembe

Caused by: java.lang.IllegalStateException: (conn# 0) already closed - close() was never explicitly called on database

Merhaba,

Başlıkta 2 farklı hata görmektesiniz. SqliteOpenHelper'dan extend ettiğiniz db class'ındaki tüm sql işlemlerinde cursor'ı kapatmış olsanız dahi -ki bu bizzat benim yaşadığım problemdi- close() was never explicitly called on database hatasını alabilirsiniz. İlginç olan şey debug ederken bu hatayla karşılaşmamanız ve hatta hata geliyor olsa bile (logcat'ten gözlemleyebilirsiniz) uygulamanın çalışmaya devam ediyor olması. Bu problemi çözmek için cursor.close() işlemi sonrası db'yi de close ettiğimde ise doğal olarak tekrar tekrar db'yi açmamız gerekliliğiyle karşılaştım. Sonuç olarak bunların ikisini de bertaraf edecek bir çözümü sizinle paylaşacağım. Genellikle onDestroy eventinde cursor'ın ve/veya db'nin close edilmesi tavsiye edilmiş. Ancak gitmekte olduğunuz class her zaman bir activity olmayabilir. Ben hem daha farklı hem de hepimizin neredeyse her uygulamada kullandığı çakma bir factory pattern ile bu soruna çözüm getirebileceğimizi göstereceğim.

Anlaşılmayan nokta olursa yorum yazabilirsiniz.

Aşağıda ürünler ile ilgili db işlemlerini gerçekleştiren bir class'ımız var. Herhangi bir aktivitide yapılan bir işlemde ürün bilgisi getirilecek varsayıyoruz.

public class UrunMgr {
    private static UrunMgr instance;
    Context context;
    private SQLiteDatabase db;

    // constructor
    private UrunMgr(Context context) {
        this.context = context;
        DBHelper helper = new DBHelper(context);
        this.db = helper.getWritableDatabase();
    }

    public static UrunMgr getInstance(Context context) {
        if(instance == null) {
            instance = new UrunMgr(context);
        }
        return instance;
    }

    public Urun getUrunByTip(String urunTipi)
        Urun oUrun;
        Cursor cursor;
        try {
        cursor = db.query(MyConstants.TBL_URUNLER, new String[] {
"_id", "Urun", "Ad", "Ord"}, "Urun = '" + urunTipi + "'", null, null, null,
null, null);

        cursor.moveToFirst();
        if (!cursor.isAfterLast()) {
            do {
                oUrun = populateList(cursor);
            } while (cursor.moveToNext());
        } else {
            oUrun = null;
        }
        cursor.close();
        } catch (SQLException e) {
            Log.e("UrunMgr - getUrunByTip() db hatası alındı: ", e.toString()); e.printStackTrace();
        }

        return oUrun;
    }
}


cok basit anlamda başka bir activity'den UrunMgr class'ı içindeki herhangi bir methodu call etmek için;

Urun urun = UrunMgr.getInstance(this).getUrunByTip("1");

Yukarıdaki örnekte, singleton'a benzer bir yapı ile (çakma factory pattern dedik) constructor'ın private'a çekildiğini, bu sayede tüm çoklama işlemlerinin getInstance methodu üzerinden yapıldığını, dolayısıyla tekrar tekrar UrunMgr class'ının yaratılmadığını (multi-thread hariç) basitçe anlatmaya çalıştım. Dediğim gibi anlaşılmayan bir nokta olursa yorum bölümünden ulaşabilirsiniz, iyi çalışmalar.

Hiç yorum yok:

Yorum Gönder