Android Learning Series (32)-cursor for app debugging Memory leakage

Source: Internet
Author: User
Tags stack trace

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 take some instances based on the problem.CodePaste it out and analyze it step by step. In specific scenarios, use an effective method to find out the root cause of the leakage and provide a solution.
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

 
// Sample codecursor 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:

Try {cursor c = querycursor (); int A = C. getint (1 );...... // if an error occurs, the following cursor. close () will not execute ...... c. close ();} catch (exception e ){}

The correct statement should be:

 
Cursor C; try {c = querycursor (); int A = C. getint (1 );...... // if an error occurs, the following cursor. close () will not execute // 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:

 
Mcursor = getcontentresolver (). query (content_uri, projection, null); madapter = new mycursoradapter (this, R. layout. list_item, mcursor); setlistadapter (madapter); // The mcursor cannot be disabled here. close (), // otherwise there will be no data 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 only requirement. It is seldom necessary to keep displaying the query results when the interface is not visible. If there is a problem, do not discuss it. Remember to turn it off.
At this time, we can usually turn off the cursor in the onstop () method (it also means you may need to re-query it in onresume () or onstart ).

 
@ Override protected void onstop () {super. onstop (); // mcursoradapter releases the previous cursor, which is equivalent to disabling 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:

/*** 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 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. */P Ublic 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 policydatasetchanged ();} else {mrowidcolumn =-1; mdatavalid = false; // configure y the observers about the lack of a data set policydatasetinvalidated ();} return oldcursor ;}

6. in practice, the cursor closure problem in asyncqueryhandler
asyncqueryhandler is a classic and typical example of analyzing cursor. It is not only a burst of blood, but it can be used in the opposite way, it is also very common to avoid it later.
asyncqueryhandler document reference address:
http://developer.android.com/reference/android/content/AsyncQueryHandler.html
the following code is a part of the source code of the MMS Information Home Page conversationlist in android2.3 system, you can see that the cursor is disabled correctly?

Private final class threadlistqueryhandler extends handler {public threadlistqueryhandler (contentresolver) {super (strong) ;}@ override protected void onquerycomplete (INT token, object cookie, cursor) {Switch) {Case thread_list_query_token: mlistadapter. changecursor (cursor); settitle (mtitle );...... break; Case have_locked_messages_token: Long th Readid = (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) ;}}@ 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 properly disabled 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/strictmodel (639): Java. lang. throwable: explicit termination method 'close' not callede/strictmode (639): At Dalvik. system. closeguard. open (closeguard. java: 184 )......

Google fixed this leak in android4.0 Jellybean. The related code is as follows:

Private final class threadlistqueryhandler extends handler {public threadlistqueryhandler (contentresolver) {super (strong) ;}@ override protected void onquerycomplete (INT token, object cookie, cursor) {Switch) {Case thread_list_query_token: mlistadapter. changecursor (cursor );...... break; Case unread_threads_query_token: // the newly added unread_threa. The ds_query_token molecule is similar to the have_locked_messages_token branch. The cursor promptly closes int COUNT = 0 in jellybean; 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); // 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) ;}}@ 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 articleArticleLater, we will not be afraid of asyncqueryhandler's cursor leakage. It may be able to solve many background strictmode cursor not close exceptions in 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!

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.