소스를 디버깅하다 보면 분명 SQLiteCursor의 finalize 메소드 코드에 중단점(break point)를 걸지 않았음에도 불구하고 자꾸 브레이크가 걸리는 경우가 있습니다.


저는 처음에 이 현상의 원인이 프레임워크 라이브러리 빌드시 어떤 식으로든 중단점 정보가 잘못 포함되어 걸리는 줄로 짐작하고 대수롭지 않게 생각했습니다. 근데 그게 아니더군요. 구글신에게 물어보니 아래와 같은 답변이 나왔습니다.

The implication is that you haven't used cursor.close() and left an 
"open" cursor around when your activity/service was destroyed.   You 
either need to do this manually, or make sure you're using a managed
cursor. 

If you're not using a managed cursor, you should wrap every cursor 
creation with a try/finally expression. 

그러니까 코드 어딘가에 close 되지 않은 커서가 있는 것이니 명시적으로 finally절에서 close해줘야 한다는 겁니다. 불행히도 close 되지 않은 커서가 어느 클래스의 어느 메소드에서 open 됐는지는 디버깅 창의 스택트레이스에 나와있지 않습니다. 

하지만 한가지 힌트는 있습니다. SQLiteCursor 객체의 mQuery 멤버변수에는 해당 커서가 실행했던 쿼리 정보가 담겨 있습니다. 이 쿼리를 Variables 창에서 확인할 수 있습니다. 이제 close되지 않는 커서가 실행했던 쿼리가 무엇인지 알았으니 대충 소스의 어느 부분에서 open된 건지 찾아낼 실마리를 얻은 겁니다.


* 내용추가(2010/08/12)
cursor가 어느 클래스의 어느 메소드에서 open 된 것인지 로그캣에 출력할 수 있습니다. SQLiteCursor.finalize()의 아래 코드에 방법이 나옵니다.

if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
    Log.d(TAG, message + "\nThis cursor was created in:");
    for (StackTraceElement ste : mStackTraceElements) {
        Log.d(TAG, "      " + ste);
    }
}

즉 SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION 이 true이면 스택트레이스를 DEBUG 레벨로 로깅해줍니다. 
DEBUG_ACTIVE_CURSOR_FINALIZATION 을 true로 설정하는 방법은 Log.isLoggable() 메소드에 나오는 아래 설명을 참조하여,
Checks to see whether or not a log for the specified tag is loggable at the specified level. The default level of any tag is set to INFO. This means that any level above and including INFO will be logged. Before you make any calls to a logging method you should check to see if your tag should be logged. You can change the default level by setting a system property: 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>' Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPRESS will turn off all logging for your tag. You can also create a local.prop file that with the following in it: 'log.tag.<YOUR_LOG_TAG>=<LEVEL>' and place that in /data/local.prop.
다음처럼 명령을 날립니다.

$ adb shell
# setprop log.tag.SQLiteCursorClosing VERBOSE

이제 로그캣에 닫히지 않은 커서의 스택트레이스가 나올 것입니다. (그러나 저는 안나오더군요. 왜 안나오는지 모르겠습니다.ㅠㅠ)

이 방법을 응용하여 SQLite가 실행하는 모든 쿼리를 로그캣에 찍을 수 있습니다.
android.database,sqlite.SQLiteDebug 클래스의 DEBUG_SQL_STATEMENTS 옵션을 한번 볼까요.

    /**
     * Controls the printing of SQL statements as they are executed.
     */
    public static final boolean DEBUG_SQL_STATEMENTS =
            Log.isLoggable("SQLiteStatements", Log.VERBOSE);

아하 "SQLiteStatements" 군요. 아래의 명령어를 실행해주면 모든 쿼리가 로그캣에 디버그 레벨로 로깅됩니다.

$ adb shell
# setprop log.tag.SQLiteStatements VERBOSE

Posted by 에코지오
,