Junaid
Junaid

Reputation: 7860

Close was never explictly called on Database

This question has been asked a plethora of times, I think my situation is a bit different.

Earlier I was declaring my db object at class level:

DBAdapter dbHelper; 

in onCreate:

dbHelper = new DBAdapter(this);

I had closed the DB in on destroy:

@Override
    public void onDestroy(){
        super.onDestroy();
        if (dbHelper != null) 
        {
            dbHelper.close();
        }

    }

However it raised an error each time I wrote to the DB. After the close was never explicitly called on DB It also had some exception about DB lock.

The Problem

I have an async task, in this I update three tables for each record one after the other. ( The Operations are indeed huge) But now I shifted the code of DB init to my async task. on pre execute I init the DB, then I close the DB in onPostExecute. However the problem remains, what can be a solution of this problem? Below is the code of my async task:

private class MagicCall extends AsyncTask<Void, String, String> {

        int years; 
        long secon; 
        long min; 
        int hours;
        int mon; 
        int days;
        int weeks;
        String CONTACT_ID,CONTACT_NAME,CONTACT_IMAGE_URI; 

        ProgressDialog Asycdialog = new ProgressDialog(LoaderClass.this);

        @Override
        protected void onPreExecute() {
            dbHelper = new DBAdapter(getApplicationContext());
            dbHelper.open();
            //Init the LoaderDialog 
            Asycdialog.setMessage("Working");
            Asycdialog.getWindow().setGravity(Gravity.CENTER_VERTICAL);
            Asycdialog.getWindow().setGravity(Gravity.CENTER_HORIZONTAL);
            Asycdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            Asycdialog.setCancelable(false);
            //Dialog Show
            Asycdialog.show();
            super.onPreExecute();
        }

        protected void onPostExecute(String result) {
            // hide the dialog
            dbHelper.close();
            Asycdialog.dismiss();
            startActivity(new Intent(LoaderClass.this, MainActivity.class));
            finish();
            super.onPostExecute(result);
        }

        @Override
        protected String doInBackground(Void... args) {


            // 
            // Dear maintainer:
            // 
            // Once you are done trying to 'optimise' this routine,
            // and have realized what a terrible mistake that was,
            // please increment the following counter as a warning
            // to the next guy:
            // 
            // total_hours_wasted_here = 0;
            //
            int lines = 0;
            try{
                BufferedReader reader;
                if(FLAG ==1){
                    reader = new BufferedReader(new FileReader("/sdcard/BirthdayReminders/fileone.txt"));
                }else{
                    reader = new BufferedReader(new FileReader("/sdcard/BirthdayReminders/output.txt"));
                }
                while (reader.readLine() != null) lines++;
                reader.close();
            }catch(Exception e){

            }



            dbHelper.NotificationDrop(); 
            int i=0;
            File toRead = null;
            try{
                if(FLAG ==1){
                    toRead=new File("/sdcard/BirthdayReminders/fileone.txt");
                }else{
                    toRead=new File("/sdcard/BirthdayReminders/output.txt");
                }
                FileInputStream fis=new FileInputStream(toRead);

                Scanner sc=new Scanner(fis);

                //read data from file line by line:
                String currentLine;
                while(sc.hasNextLine()){


                    currentLine=sc.nextLine();
                    //now tokenize the currentLine:
                    StringTokenizer st=new StringTokenizer(currentLine,"=",false);
                    //put tokens ot currentLine in map
                    //  mapInFile.put(st.nextToken(),st.nextToken());

                    String dateStr = st.nextToken();
                    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
                    Date date = format.parse(dateStr);
                    java.sql.Date dx = new java.sql.Date(date.getTime());
                    Date key = dx;
                    String dateToInsert = String.valueOf(dx); 
                    // *********
                    String listStr = st.nextToken();

                    String cut = listStr.substring(1, listStr.length() - 1);

                    String[] array = cut.split(",");
                    CONTACT_ID = (array[0].trim());

                    CONTACT_NAME = toTitleCase(array[1].trim());

                    if(array[2].contains(".jp"))
                        array[2] = array[2].replace(".jp", ".jpg").trim();
                    CONTACT_IMAGE_URI = (array[2].trim());


                    if (isCancelled()) {
                        break;
                    }
                    //  int k = Sx.size();


                    String progress = ("" + Character.toUpperCase(CONTACT_NAME.charAt(0)) + CONTACT_NAME.substring(1) + "\n"+i + " of " + lines + " Contacts"); // Progress displayed here. 



                    years = getDiffYear(key);               // For years elapsed
                    secon = seconds(key);                   // for seconds elapsed
                    min = seconds(key) / 60;                // For minutes elapsed
                    hours = (int) (seconds(key) / 60) / 60; // For hours elapsed
                    mon = months(String.valueOf(key));                      // for months elapsed

                    days = daysElapsed(key);                // Days elapsed
                    weeks = daysElapsed(key) / 7;           // For weeks

                    //===============================================================================================================
                    if (dateToInsert.contains("0001-") == true){ //Special Case, we added 0001 to Birthdays Which Have NO Year field. 
                        //===========================================================================================================

                        dbHelper.insert(dateToInsert, CONTACT_NAME, "","", CONTACT_IMAGE_URI, "", "", "", CONTACT_ID, "", ""); // All other fields will be empty, because we don't have a Year. 
                        int PRIMARY_ID = dbHelper.getPrimaryId();
                        String FOREIGN_KEY = dbHelper.getHighestID(PRIMARY_ID); 


                        //=====================================================================================================
                        //In this case we are only interested in fetching the year alert for next birthday of this contact -->
                        //=====================================================================================================

                        intCal.yearsToNotify(years, dateToInsert); 
                        int yearsSpecial = intCal.getYearsRegular();
                        Date dateYearsReg = intCal.getYearsRegDate();

                        dbHelper.insertNotifications(5, convertDate(dateYearsReg), 0, yearsSpecial,FOREIGN_KEY,PRIMARY_ID); 

                    }
                    //=========================================================================
                    //Case when all the Date fields exist and we set up notifications  --->
                    //=========================================================================
                    else if(dateToInsert != "null" && dateToInsert.contains("0001-") != true){

                        dbHelper.insert(dateToInsert, CONTACT_NAME, String.valueOf(days), String.valueOf(hours), CONTACT_IMAGE_URI, String.valueOf(min),String.valueOf(mon), String.valueOf(secon), CONTACT_ID, String.valueOf(weeks), String.valueOf(years));

                        int PRIMARY_ID = dbHelper.getPrimaryId(); // Fetch the PrimaryId (_id) of the above inserted row, its the Foreign key for Notification and SpecialNotifications Table. 
                        String FOREIGN_KEY = dbHelper.getHighestID(PRIMARY_ID); // Same as above, but fetches the Name field of the last inserted row. 



                        //=========================================================================
                        //**Database Insertions Notifications Table/ SpecialNotifications Table**
                        //=========================================================================



                        //=======================================================================================//
                        //Regular intervals DB Insertions: 
                        //======================================================================================//
                        //Notification Types:
                        //1 for months
                        //2 for weeks
                        //3 for days
                        //4 for minutes
                        //5 for years
                        //6 for seconds
                        //7 for hours
                        //======================================================================================//

                        //==============================
                        //For Months 
                        //==============================
                        intCal.monthsNotify(mon, dateToInsert);
                        int monSpecial =  intCal.getMonthRegular(); 
                        Date dateMonReg = intCal.getMonRegDate(); 


                        dbHelper.insertNotifications(1, convertDate(dateMonReg), 0, monSpecial,FOREIGN_KEY,PRIMARY_ID);


                        //===============================
                        //For Weeks 
                        //===============================
                        intCal.weeksToNotify(weeks,dateToInsert); 
                        int weekSpecial = intCal.getWeekRegular();
                        Date dateWeekReg =intCal.getWeekRegDate(); 

                        dbHelper.insertNotifications(2, convertDate(dateWeekReg), 0, weekSpecial,FOREIGN_KEY,PRIMARY_ID);


                        //===============================
                        //For Days
                        //===============================
                        intCal.daysToNotify(days, dateToInsert); 
                        int daysSpecial= intCal.getDaysRegular();  
                        Date dateDaysReg = intCal.getDaysRegDate(); 

                        dbHelper.insertNotifications(3, convertDate(dateDaysReg), 0, daysSpecial,FOREIGN_KEY,PRIMARY_ID);


                        //===============================
                        //For minutes
                        //===============================
                        intCal.minutesToNotify(min,dateToInsert);
                        long minutesSpecial= intCal.getMinutesRegular();
                        Date dateMinsReg = intCal.getMinutesRegDate(); 

                        dbHelper.insertNotifications(4, convertDate(dateMinsReg), 0,(int) minutesSpecial,FOREIGN_KEY,PRIMARY_ID);


                        //==============================
                        //For Years
                        //==============================
                        intCal.yearsToNotify(years, dateToInsert); 
                        int yearsSpecial = intCal.getYearsRegular();
                        Date dateYearsReg = intCal.getYearsRegDate();

                        dbHelper.insertNotifications(5, convertDate(dateYearsReg), 0, yearsSpecial,FOREIGN_KEY,PRIMARY_ID);


                        //=============================
                        //For Seconds
                        //=============================
                        intCal.secondsToNotify(secon, dateToInsert);
                        long secondsSpecial= intCal.getSecondsRegular(); 
                        Date dateSecondsReg = intCal.getSecondsRegDate(); 

                        dbHelper.insertNotifications(6, convertDate(dateSecondsReg), 0, secondsSpecial,FOREIGN_KEY,PRIMARY_ID);


                        //=============================
                        //For Hours
                        //=============================
                        intCal.hoursToNotify(hours, dateToInsert); 
                        int hoursSpecial= intCal.getHoursRegular();
                        Date dateHoursReg= intCal.getHoursRegDate(); 

                        dbHelper.insertNotifications(7, convertDate(dateHoursReg), 0, hoursSpecial,FOREIGN_KEY,PRIMARY_ID);



                        //============================================================================================//
                        //Special Intervals
                        //=============================================================================================================//
                        //Notification Types:
                        //1 for months
                        //2 for weeks
                        //3 for days
                        //4 for minutes
                        //5 for years
                        //6 for seconds
                        //7 for hours
                        //For Years
                        intCal.specialIntervalYears(years, dateToInsert); 
                        int yearsOnceSpecial =intCal.getYearsSpecial();
                        Date dateYearsSpecial = intCal.getYearsSpDate(); 
                        dbHelper.insertSpecialNotifications(5, convertDate(dateYearsSpecial), yearsOnceSpecial,FOREIGN_KEY,PRIMARY_ID);


                        //For Months
                        intCal.specialIntervalMonths(mon,dateToInsert); 
                        int monthsOnceSpecial= intCal.getMonthsSpecial();
                        Date dateMonthsSpecial = intCal.getMonthsSpDate();
                        dbHelper.insertSpecialNotifications(1, convertDate(dateMonthsSpecial), monthsOnceSpecial,FOREIGN_KEY,PRIMARY_ID);


                        //For Weeks
                        intCal.specialIntervalsWeeks(weeks,dateToInsert); 
                        int weeksOnceSpecial= intCal.getWeeksSpecial(); 
                        Date dateWeeksSpecial = intCal.getWeeksSpDate(); 
                        dbHelper.insertSpecialNotifications(2, convertDate(dateWeeksSpecial), weeksOnceSpecial,FOREIGN_KEY,PRIMARY_ID);

                        //For Days
                        intCal.specialIntervalsDays(days, dateToInsert); 
                        int daysOnceSpecial= intCal.getDaysSpecial(); 
                        Date dateDaysSpecial = intCal.getDaysSpDate(); 
                        dbHelper.insertSpecialNotifications(3, convertDate(dateDaysSpecial), daysOnceSpecial,FOREIGN_KEY,PRIMARY_ID);

                        //For Hours
                        intCal.specialIntervalsHours(hours,dateToInsert); 
                        int hoursOnceSpecial= intCal.getHoursSpecial();  
                        Date dateHoursSpecial = intCal.getHoursSpDate(); 
                        dbHelper.insertSpecialNotifications(7, convertDate(dateHoursSpecial), hoursOnceSpecial,FOREIGN_KEY,PRIMARY_ID);

                        //For Minutes
                        intCal.specialIntervalMinutes(min,dateToInsert); 
                        long minutesOnceSpecial= intCal.getMinutesSpecial(); 
                        Date dateMinutesSpecial= intCal.getMinutesSpDate(); 
                        dbHelper.insertSpecialNotifications(4, convertDate(dateMinutesSpecial), (int)minutesOnceSpecial,FOREIGN_KEY,PRIMARY_ID);

                        //For Seconds
                        intCal.specialIntervalsSeconds(secon,dateToInsert); 
                        long secondsOnceSpecial= intCal.getSecondsSpecial(); 
                        Date dateSecondsSpecial= intCal.getSecondsSpDate(); 
                        dbHelper.insertSpecialNotifications(6, convertDate(dateSecondsSpecial), secondsOnceSpecial,FOREIGN_KEY,PRIMARY_ID); 

                    }
                    publishProgress(progress);
                    Asycdialog.setMax(lines);
                    Asycdialog.incrementProgressBy(1);
                    i++;
                }

            }catch (Exception e){

            } 
            try{
                writeToSD(); 
            }catch (Exception e){
                System.out.println(""+e);
            }


            return "";

        }

        protected void onProgressUpdate(String... values) {

            super.onProgressUpdate(values);
            Asycdialog.setMessage("" + values[0]);
        }


    }

Here is the log:

01-15 09:22:36.215: E/SQLiteDatabase(728): close() was never explicitly called on database '/data/data/com.exa.birthdayrem/databases/Bdr' 
01-15 09:22:36.215: E/SQLiteDatabase(728): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1943)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1007)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:986)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1051)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:770)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:157)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at com.exa.birthdayrem.DBAdapter.<init>(DBAdapter.java:110)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at com.exa.birthdayrem.LoaderClass$MagicCall.onPreExecute(LoaderClass.java:378)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:561)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.os.AsyncTask.execute(AsyncTask.java:511)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at com.exa.birthdayrem.LoaderClass.onLoadFinished(LoaderClass.java:225)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at com.exa.birthdayrem.LoaderClass.onLoadFinished(LoaderClass.java:1)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:433)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.app.LoaderManagerImpl$LoaderInfo.onLoadComplete(LoaderManager.java:405)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.content.Loader.deliverResult(Loader.java:110)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.content.CursorLoader.deliverResult(CursorLoader.java:88)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.content.CursorLoader.deliverResult(CursorLoader.java:42)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.content.AsyncTaskLoader.dispatchOnLoadComplete(AsyncTaskLoader.java:236)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.content.AsyncTaskLoader$LoadTask.onPostExecute(AsyncTaskLoader.java:76)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.os.AsyncTask.finish(AsyncTask.java:602)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.os.AsyncTask.access$600(AsyncTask.java:156)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:615)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.os.Handler.dispatchMessage(Handler.java:99)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.os.Looper.loop(Looper.java:137)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at android.app.ActivityThread.main(ActivityThread.java:4340)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at java.lang.reflect.Method.invokeNative(Native Method)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at java.lang.reflect.Method.invoke(Method.java:511)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
01-15 09:22:36.215: E/SQLiteDatabase(728):  at dalvik.system.NativeStart.main(Native Method)
01-15 09:22:36.215: E/System(728): Uncaught exception thrown by finalizer
01-15 09:22:36.235: E/System(728): java.lang.IllegalStateException: Don't have database lock!
01-15 09:22:36.235: E/System(728):  at android.database.sqlite.SQLiteDatabase.verifyLockOwner(SQLiteDatabase.java:2090)
01-15 09:22:36.235: E/System(728):  at android.database.sqlite.SQLiteDatabase$1.entryRemoved(SQLiteDatabase.java:2182)
01-15 09:22:36.235: E/System(728):  at android.database.sqlite.SQLiteDatabase$1.entryRemoved(SQLiteDatabase.java:2178)
01-15 09:22:36.235: E/System(728):  at android.util.LruCache.trimToSize(LruCache.java:197)
01-15 09:22:36.235: E/System(728):  at android.util.LruCache.evictAll(LruCache.java:285)
01-15 09:22:36.235: E/System(728):  at android.database.sqlite.SQLiteDatabase.deallocCachedSqlStatements(SQLiteDatabase.java:2143)
01-15 09:22:36.235: E/System(728):  at android.database.sqlite.SQLiteDatabase.closeClosable(SQLiteDatabase.java:1126)
01-15 09:22:36.235: E/System(728):  at android.database.sqlite.SQLiteDatabase.finalize(SQLiteDatabase.java:1914)
01-15 09:22:36.235: E/System(728):  at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:182)
01-15 09:22:36.235: E/System(728):  at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:168)
01-15 09:22:36.235: E/System(728):  at java.lang.Thread.run(Thread.java:856)

The open method in DBAdapter:

public DBAdapter open() throws SQLException {
    DBHelper = new DatabaseHelper(mCtx);
    mDb = DBHelper.getWritableDatabase();
    return this;

}

Upvotes: 1

Views: 115

Answers (2)

eski
eski

Reputation: 7915

It is simplest to keep the database in a global context, either as a static or in your Application class (accessed by getApplicationContext()), that way it will always be open while your application is running.

You should also make sure that all the helper methods you have using the database are properly synchronized.

You never actually need to close the database, see this post by one of the engineers:

https://groups.google.com/forum/#!msg/android-developers/nopkaw4UZ9U/cPfPL3uW7nQJ

"Given that design, keeping anything open for the entire duration of a process's life and never closing it is simply not a leak. It will be cleaned up when the process is cleaned up."

Upvotes: 1

stinepike
stinepike

Reputation: 54672

I usually close the db in a finally block for every methods. like following

try {
    //open db
    // perform tasks
} catch (Exception e) {
    // handle exceptions
} finally {
    // close db
}

Upvotes: 1

Related Questions