Android App debugging Memory leakage-Cursor

Source: Internet
Author: User

I recently handled some memory leaks in my work. In this process, I found some basic problems, instead of causing memory leaks, such as static variables, cursor shutdown, threads, timer, anti-Registration, bitmap, etc. I have made some statistics and summarized. Of course, these problems are more general. Next I will post some instance code based on the problem, perform step-by-step analysis to find out the root cause of the leakage and provide a solution in specific scenarios.
Now, starting from the cursor shutdown problem, everyone knows that cursor is to be closed, but on the contrary, people often forget to close it, because the real application scenario may not be idealized and simple.
1. Close the idealized cursor Copy codeThe Code is as follows: // Sample Code
Cursor cursor = db. query ();
List <String> list = convertToList (cursor );
Cursor. close ();

This is the simplest use case of cursor. If the cursor is not closed, I think it may cause a flood of saliva and a buzz.
However, this may not be the case in actual scenarios. The cursor here may not be closed. There are at least two possibilities.
2. Possibility of Cursor being disabled
(1) An exception occurred before cursor. close.
(2). cursor needs to be used again. It cannot be closed immediately and will not be closed later.

3. An exception occurred before Cursor. close ().
This is easy to understand and should also be a common problem for beginners. Examples are as follows:Copy codeThe Code is as follows: try {
Cursor c = queryCursor ();
Int a = c. getInt (1 );
......
// If an error occurs, the subsequent cursor. close () will not be executed.
......
C. close ();
} Catch (Exception e ){
}

The correct statement should be:Copy codeThe Code is as follows: Cursor c;
Try {
C = queryCursor ();
Int a = c. getInt (1 );
......
// If an error occurs, the subsequent cursor. close () will not be executed.
// C. close ();
} Catch (Exception e ){
} Finally {
If (c! = Null ){
C. close ();
}
}

It's easy, but keep in mind.
4. Cursor needs to be used again and cannot be closed immediately
Is this the case? What should I do?
The answer is: CursorAdapter is a typical example.
CursorAdapter example:Copy codeThe Code is as follows: mCursor = getContentResolver (). query (CONTENT_URI, PROJECTION,
Null, null, null );
MAdapter = new MyCursorAdapter (this, R. layout. list_item, mCursor );
SetListAdapter (mAdapter );
// The mCursor. close () cannot be disabled here (),
// Otherwise, no data exists in the list.

5. When should this Cursor be closed?
This is a question that can be well answered or cannot be answered, that is, it is disabled when the Cursor is no longer in use.
For example,
In the preceding query, if you enter or resume each time, the query will be executed again.
In general, this is also the requirement. I rarely see the query results continuously when I don't see the interface. If so, I will not discuss it. Remember to turn it off.
At this time, we can usually turn off the cursor in the onStop () method.Copy codeThe Code is as follows: @ Override
Protected void onStop (){
Super. onStop ();
// MCursorAdapter releases the previous cursor, which is equivalent to disabling the cursor
MCursorAdapter. changeCursor (null );
}

I have attached the source code of the changeCursor () method of CursorAdapter to make it clearer, so that you do not have to worry about the changeCursor (null) method:Copy codeThe Code is as follows :/**
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* Closed.
*
* @ Param cursor The new cursor to be used
*/
Public void changeCursor (Cursor cursor ){
Cursor old = swapCursor (cursor );
If (old! = Null ){
Old. close ();
}
}

/**
* Swap in a new Cursor, returning the old Cursor. Unlike
* {@ Link # changeCursor (Cursor)}, the returned old Cursor is <em> not </em>
* Closed.
*
* @ Param newCursor The new cursor to be used.
* @ Return Returns the previusly set Cursor, or null if there wasa not one.
* If the given new Cursor is the same instance is the previusly set
* Cursor, null is also returned.
*/
Public Cursor swapCursor (Cursor newCursor ){
If (newCursor = mCursor ){
Return null;
}
Cursor oldCursor = mCursor;
If (oldCursor! = Null ){
If (mChangeObserver! = Null) oldCursor. unregisterContentObserver (mChangeObserver );
If (mDataSetObserver! = Null) oldCursor. unregisterDataSetObserver (mDataSetObserver );
}
MCursor = newCursor;
If (newCursor! = Null ){
If (mChangeObserver! = Null) newCursor. registerContentObserver (mChangeObserver );
If (mDataSetObserver! = Null) newCursor. registerDataSetObserver (mDataSetObserver );
MRowIDColumn = newCursor. getColumnIndexOrThrow ("_ id ");
MDataValid = true;
// Configure y the observers about the new cursor
NotifyDataSetChanged ();
} Else {
MRowIDColumn =-1;
MDataValid = false;
// Configure y the observers about the lack of a data set
NotifyDataSetInvalidated ();
}
Return oldCursor;
}

6. Cursor disabling in AsyncQueryHandler
AsyncQueryHandler is a classic and typical analysis example of Cursor. It is common to avoid this problem in the future.
Reference address of AsyncQueryHandler document:
Http://developer.android.com/reference/android/content/AsyncQueryHandler.html
The following code is part of the source code of ConversationList on the Mms homepage of Android2.3. Can you see If Cursor is disabled correctly?Copy codeThe Code is as follows: private final class ThreadListQueryHandler extends AsyncQueryHandler {
Public ThreadListQueryHandler (ContentResolver contentResolver ){
Super (contentResolver );
}

@ Override
Protected void onQueryComplete (int token, Object cookie, Cursor cursor ){
Switch (token ){
Case THREAD_LIST_QUERY_TOKEN:
MListAdapter. changeCursor (cursor );
SetTitle (mTitle );
......
Break;

Case HAVE_LOCKED_MESSAGES_TOKEN:
Long threadId = (Long) cookie;
ConfirmDeleteThreadDialog (new DeleteThreadListener (threadId, mQueryHandler,
ConversationList. this), threadId =-1,
Cursor! = Null & cursor. getCount ()> 0,
ConversationList. this );
Break;

Default:
Log. e (TAG, "onQueryComplete called with unknown token" + token );
}
}
}

Copy codeThe Code is as follows: @ Override
Protected void onStop (){
Super. onStop ();

MListAdapter. changeCursor (null );
}

Do you think there is a problem?
There are two main points::
(1) is the Cursor of the THREAD_LIST_QUERY_TOKEN branch properly disabled?
(2) is the Cursor of the HAVE_LOCKED_MESSAGES_TOKEN branch properly disabled?
According to the previous analysis, the answer is:
(1 ). the Cursor of the THREAD_LIST_QUERY_TOKEN branch is passed to the mListAdapter, while the mListAdapter uses changeCursor (null) in onStop. When the user leaves the current Activity, the Cursor is correctly released and will not be leaked.
(2 ). HAVE_LOCKED_MESSAGES_TOKEN: The Cursor of the branch (that is, the cursor parameter). It is only used as a condition for judgment. It is no longer used after it is used, but it is not disabled. Therefore, the cursor is leaked, under the StrictMode monitoring, this error will be thrown as long as it runs to this place:

E/StrictMode (639): A resource was acquired at attached stack trace but never released. See java. io. Closeable for information on avoiding resource leaks.
E/StrictMode (639): java. lang. Throwable: Explicit termination method 'close' not called
E/StrictMode (639): at dalvik. system. CloseGuard. open (CloseGuard. java: 184)
......

Google fixed this leak in Android.0 JellyBean. The related code is as follows:Copy codeThe Code is as follows: private final class ThreadListQueryHandler extends ConversationQueryHandler {
Public ThreadListQueryHandler (ContentResolver contentResolver ){
Super (contentResolver );
}

@ Override
Protected void onQueryComplete (int token, Object cookie, Cursor cursor ){
Switch (token ){
Case THREAD_LIST_QUERY_TOKEN:
MListAdapter. changeCursor (cursor );

......

Break;

Case UNREAD_THREADS_QUERY_TOKEN:
// The newly added UNREAD_THREADS_QUERY_TOKEN and HAVE_LOCKED_MESSAGES_TOKEN branches are similar. The cursor is immediately disabled in jellybean.
Int count = 0;
If (cursor! = Null ){
Count = cursor. getCount ();
Cursor. close ();
}
MUnreadConvCount. setText (count> 0? Integer. toString (count): null );
Break;

Case HAVE_LOCKED_MESSAGES_TOKEN:
@ SuppressWarnings ("unchecked ")
Collection <Long> threadIds = (Collection <Long>) cookie;
ConfirmDeleteThreadDialog (new DeleteThreadListener (threadIds, mQueryHandler,
ConversationList. this), threadIds,
Cursor! = Null & cursor. getCount ()> 0,
ConversationList. this );
// The cursor in the HAVE_LOCKED_MESSAGES_TOKEN branch is promptly disabled in jellybean.
If (cursor! = Null ){
Cursor. close ();
}
Break;

Default:
Log. e (TAG, "onQueryComplete called with unknown token" + token );
}
}
}

Copy codeThe Code is as follows: @ Override
Protected void onStop (){
Super. onStop ();
MListAdapter. changeCursor (null );
}

Did you underestimate AsyncQueryHandler? Google had some such code in earlier versions, not to mention that we did not pay attention to it. In fact, many examples of using AsyncQueryHandler on the Internet all made this mistake, after reading this article, I am no longer afraid of AsyncQueryHandler's cursor leakage. It may also solve many exceptions in the background strictmode's cursor not close of your application.

7. Summary
Although I think there are still many cases where cursor is not closed, the fundamental problem is to close cursor in a timely and correct manner.
Memory Leak cursor is a summary of my work experience. It is helpful for me to understand it and make complicated problems essential and simple!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.