views:

79

answers:

3

I have a SQLiteDatabase data member that I initialize in onCreate and call .close() on in onPause(), onStop(), and onDestroy(). It is re-initialized in onResume(). It seems to run just fine but when I looked at the debugger it see this:

08-24 20:23:50.014: ERROR/Database(6767): Leak found
08-24 20:23:50.014: ERROR/Database(6767): java.lang.IllegalStateException: /data/data/com.hh.Timepunch/databases/times_database.db SQLiteDatabase created and never closed
08-24 20:23:50.014: ERROR/Database(6767):     at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1695)
08-24 20:23:50.014: ERROR/Database(6767):     at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:739)
08-24 20:23:50.014: ERROR/Database(6767):     at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:761)
08-24 20:23:50.014: ERROR/Database(6767):     at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:754)
08-24 20:23:50.014: ERROR/Database(6767):     at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:476)
08-24 20:23:50.014: ERROR/Database(6767):     at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193)
08-24 20:23:50.014: ERROR/Database(6767):     at com.hh.Timepunch.TimeSheetActivity.onCreate(TimeSheetActivity.java:72)
08-24 20:23:50.014: ERROR/Database(6767):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
08-24 20:23:50.014: ERROR/Database(6767):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
08-24 20:23:50.014: ERROR/Database(6767):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
08-24 20:23:50.014: ERROR/Database(6767):     at android.app.ActivityThread.access$2200(ActivityThread.java:119)
08-24 20:23:50.014: ERROR/Database(6767):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
08-24 20:23:50.014: ERROR/Database(6767):     at android.os.Handler.dispatchMessage(Handler.java:99)
08-24 20:23:50.014: ERROR/Database(6767):     at android.os.Looper.loop(Looper.java:123)
08-24 20:23:50.014: ERROR/Database(6767):     at android.app.ActivityThread.main(ActivityThread.java:4363)
08-24 20:23:50.014: ERROR/Database(6767):     at java.lang.reflect.Method.invokeNative(Native Method)
08-24 20:23:50.014: ERROR/Database(6767):     at java.lang.reflect.Method.invoke(Method.java:521)
08-24 20:23:50.014: ERROR/Database(6767):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
08-24 20:23:50.014: ERROR/Database(6767):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
08-24 20:23:50.014: ERROR/Database(6767):     at dalvik.system.NativeStart.main(Native Method)
08-24 20:23:50.054: ERROR/Database(6767): Leak found
08-24 20:23:50.054: ERROR/Database(6767): java.lang.IllegalStateException: /data/data/com.hh.Timepunch/databases/times_database.db SQLiteDatabase created and never closed
08-24 20:23:50.054: ERROR/Database(6767):     at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1695)
08-24 20:23:50.054: ERROR/Database(6767):     at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:739)
08-24 20:23:50.054: ERROR/Database(6767):     at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:761)
08-24 20:23:50.054: ERROR/Database(6767):     at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:754)
08-24 20:23:50.054: ERROR/Database(6767):     at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:476)
08-24 20:23:50.054: ERROR/Database(6767):     at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193)
08-24 20:23:50.054: ERROR/Database(6767):     at com.hh.Timepunch.TimepunchActivity.onCreate(TimepunchActivity.java:60)
08-24 20:23:50.054: ERROR/Database(6767):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
08-24 20:23:50.054: ERROR/Database(6767):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
08-24 20:23:50.054: ERROR/Database(6767):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
08-24 20:23:50.054: ERROR/Database(6767):     at android.app.ActivityThread.access$2200(ActivityThread.java:119)
08-24 20:23:50.054: ERROR/Database(6767):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
08-24 20:23:50.054: ERROR/Database(6767):     at android.os.Handler.dispatchMessage(Handler.java:99)
08-24 20:23:50.054: ERROR/Database(6767):     at android.os.Looper.loop(Looper.java:123)
08-24 20:23:50.054: ERROR/Database(6767):     at android.app.ActivityThread.main(ActivityThread.java:4363)
08-24 20:23:50.054: ERROR/Database(6767):     at java.lang.reflect.Method.invokeNative(Native Method)
08-24 20:23:50.054: ERROR/Database(6767):     at java.lang.reflect.Method.invoke(Method.java:521)
08-24 20:23:50.054: ERROR/Database(6767):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
08-24 20:23:50.054: ERROR/Database(6767):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
08-24 20:23:50.054: ERROR/Database(6767):     at dalvik.system.NativeStart.main(Native Method)
08-24 20:23:50.199: ERROR/libs3c2drender(1912): int S3c2DRender::DoG2D(unsigned int, s3c_img*, s3c_rect*, unsigned int, s3c_img*, s3c_rect*, int, int)::S3C_G2D_ROTATOR_18176 fail

One of the places I get this error is when going away from this activity:

public class TimepunchActivity extends Activity {

        Calendar timeNow = Calendar.getInstance();
        SQLiteDatabase timesDatabase;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.timepunch);

            //Create/open and configure database (if necessary)
            timesDatabase = openOrCreateDatabase(
                    "times_database.db",
                    SQLiteDatabase.CREATE_IF_NECESSARY,
                    null);
            timesDatabase.setLocale(Locale.getDefault());
            timesDatabase.setLockingEnabled(false);
            timesDatabase.setVersion(3);

            //lenient is set to true so incrementing minute 59 auto increments hour, etc
            timeNow.setLenient(true);

            //Create table (if necessary)
            final String dbCommand = "CREATE TABLE IF NOT EXISTS Timepunches (punch INTEGER PRIMARY KEY);";
            timesDatabase.execSQL(dbCommand);

            updateLastInPunch();

            Button punchButton = (Button)findViewById(R.id.punch_button);
            punchButton.setHapticFeedbackEnabled(true);
            punchButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    //Insert data
                    ContentValues punchValue = new ContentValues();
                    punchValue.put("punch", timeNow.getTimeInMillis());
                    long newPunchID = timesDatabase.insert("Timepunches", null, punchValue);
                    punchValue.clear();

                    //if punch already exists
                    if (newPunchID < 0) {
                        AlertDialog.Builder builder = new AlertDialog.Builder(TimepunchActivity.this);
                        builder.setTitle("Whoops")
                            .setMessage("I've already recorded a punch for this time and day.")
                            .setCancelable(true)
                            .setNeutralButton("Oh", new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    dialog.cancel();
                                }
                            })
                            .show();
                    }
                    else {
                    }
                } //end setOnClick()
            }); //end click listener for punchButton
        } //end onCreate()



        @Override
        public void onResume() {
            super.onResume();
            checkFirstTimer();

            //Open and configure database
            timesDatabase = openOrCreateDatabase(
                    "times_database.db",
                    SQLiteDatabase.CREATE_IF_NECESSARY,
                    null);
            timesDatabase.setLocale(Locale.getDefault());
            timesDatabase.setLockingEnabled(false);
            timesDatabase.setVersion(3);


            //Update date object for current time
            timeNow.setTimeInMillis(System.currentTimeMillis());
            timeNow.set(Calendar.SECOND, 0);
            timeNow.set(Calendar.MILLISECOND, 0);
            updateClock((TextView)findViewById(R.id.current_time_textbox), timeNow);

            updateLastInPunch();
        } //end onResume()


        @Override
        public void onPause() {
            super.onPause();
            timesDatabase.close();
        } //end onResume()

        @Override
        public void onStop() {
            super.onStop();
            timesDatabase.close();
        } //end onResume()

        @Override
        public void onDestroy() {
            super.onDestroy();
            timesDatabase.close();
        } //end onResume()



        //Shows last in-punch that has no out-punch
        // if there is none it hides the section
        public void updateLastInPunch() {
            Cursor punchCursor = timesDatabase.query("Timepunches", null, null, null, null, null, "punch ASC;");

            TextView inPunchLabel = (TextView)findViewById(R.id.in_punch_label_textview);
            TextView inPunchTV = (TextView)findViewById(R.id.in_punch_time_textview);

            //odd count means there is a missing punch (assumed to be latest in punch)
            // therefore show last in punch
            if ((punchCursor.getCount()%2) != 0) {
                punchCursor.moveToLast();
                Calendar lastIn = Calendar.getInstance();
                lastIn.setTimeInMillis(punchCursor.getLong(0));

                updateClock((TextView)findViewById(R.id.in_punch_time_textview), lastIn);

                inPunchLabel.setVisibility(View.VISIBLE);
                inPunchLabel.setAnimation(fadeInAnimation);
                inPunchLabel.startAnimation(fadeInAnimation);

                inPunchTV.setVisibility(View.VISIBLE);
                inPunchTV.setAnimation(fadeInAnimation);
                inPunchTV.startAnimation(fadeInAnimation);
            }
            else {
                //only fade when "turning off" last punch display
                // (don't fade away when returning to screen)
                if (inPunchLabel.getVisibility() == View.VISIBLE) {
                    inPunchLabel.setAnimation(fadeOutAnimation);
                    inPunchLabel.startAnimation(fadeOutAnimation);
                    inPunchLabel.setVisibility(View.INVISIBLE);

                    inPunchTV.setAnimation(fadeOutAnimation);
                    inPunchTV.startAnimation(fadeOutAnimation);
                    inPunchTV.setVisibility(View.INVISIBLE);
                }
            }
            punchCursor.close();
        } //end getLastInPunch()
    } //end TimepunchActivity

The odd thing is, though, is that this error isn't persistent.

Do I have to do more then call .close()?

+2  A: 

The problem is that you're creating/opening it in both onCreate and then onResume, so you're doing it twice. Likewise, you're closing it twice, so it'll presumably crash.

In general - why don't you use SQLiteOpenHelper? This will make managing your database a lot easier.

EboMike
Thank you. I thought I was re-initializing the object in onResume. So should I explicitly close it before re-initializing it? When I started seeing this error I only had onPause; I didn't even have onStop or onDestroy until I got frustrated and added them.I didn't use SQLiteOpenHelper because I figure since my needs of the database are so basic (basically it's a simple array; one column of longs) I may as well do it in the activity and not make another class. Is this faulty reasoning?
hermy
Again, I recommend SQLiteOpenHelper to cut down the amount of work by a lot. In general, I would only deal with the database in onResume and then close it in onPause. If you HAVE to use the database in onCreate, then do it there and close it in onDestroy - always be symmetrical. Refer to the link cdonner sent. If you get to onResume, you're guaranteed to already have gone through onCreate.
EboMike
My design was bad - having the open in onCreate and onResume. I ditched the onPause and onStop methods and just kept the onDestroy and only create/open the DB in onCreate. Seems to work fine now. However, I will go back and examine the SQLiteOpenHelper as you suggested. Thanks for the help.
hermy
A: 

"Debugging with printf."

You could use some logging statements before each Open and Close (and other operations) to see what's happening.

dash-tom-bang
+1  A: 

In support of EboMike's point, look at the Android Activity lifecycle and the problem with your code will be obvious.

cdonner
Thank you for pointing me to the reference page. I've read that many times before though (though not in it's entirety) and I was aware of those things. My design was bad - having the open in onCreate and onResume. I ditched the onPause and onStop methods and just kept the onDestroy and only create/open the DB in onCreate. Seems to work fine now. Thank you.
hermy