Recently handled some memory leaks in the work, in this process I found some basic problems instead of causing memory leaks, such as static variables, cursor shutdown, thread, timer, reverse registration, bitmap and so on, I have a little statistics and summed up, of course, These questions are more general, then I will be based on the problem, some examples of code to post, step-by-step analysis, in the specific scenario, with effective methods to find out the root causes of leakage, and to give a solution.
Now, starting with the problem of cursor shutdown, everyone knows that cursor is going to shut down, but often on the contrary, people often forget to close, because the real application scenario may not be idealized.
1. Idealized cursor closure
Copy Code code as follows:
Sample Code
Cursor Cursor = Db.query ();
list<string> list = converttolist (cursor);
Cursor.close ();
This is the simplest cursor to use the scene, if the cursor here is not closed, I think may cause thousands of saliva, a curse sound.
But the actual scenario may not be the case, where the cursor may not be closed, at least in the following two possible ways.
2. Possibility of cursor not closed
(1). An exception occurred before Cursor.close ().
(2). Cursor need to continue to use, not immediately shut down, forget to close the back.
3. An exception occurred before Cursor.close ()
This is easy to understand, it should also be the beginning of beginners encounter common problems, examples are as follows:
Copy Code code 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 wording should be:
Copy Code code as follows:
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 simple, but it takes time to remember.
4. Cursor need to continue to use, not immediately shut down
Is there a situation like this? What to do?
The answer is yes, CursorAdapter is a typical example.
CursorAdapter examples are as follows:
Copy Code code as follows:
Mcursor = Getcontentresolver (). Query (Content_uri, projection,
NULL, NULL, NULL);
Madapter = new Mycursoradapter (this, R.layout.list_item, mcursor);
Setlistadapter (Madapter);
The execution Mcursor.close () cannot be closed here.
Otherwise there will be no data in the list
5. When should such cursor be closed?
This is a good answer can be said that can not answer the question, that is, when the cursor no longer use the time to shut down.
For example
The above query will requery execution every time you enter or resume.
In general, this is also the demand, very few I can not see the interface when the query results are constantly displayed, if there is, do not discuss, remember the final turn off on the OK.
At this time, we can generally turn off the cursor in the OnStop () method.
Copy Code code as follows:
@Override
protected void OnStop () {
Super.onstop ();
Mcursoradapter will release the previous cursor, equivalent to shutting down the cursor
Mcursoradapter.changecursor (NULL);
}
I have attached cursoradapter changecursor () method source code, let everyone see more clearly, lest not worry changecursor (null) method:
Copy Code code as follows:
/**
* Change of the underlying cursor to a new cursor. If There is a existing cursor it'll 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 previously set Cursor, or null if there wasa not one.
* If The given new Cursor is the same instance is the previously 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;
Notify the observers about the new cursor
Notifydatasetchanged ();
} else {
Mrowidcolumn =-1;
Mdatavalid = false;
Notify the observers about the lack of a data set
Notifydatasetinvalidated ();
}
return oldcursor;
}
6. Cursor shutdown problem in actual combat Asyncqueryhandler
Asyncqueryhandler is a classic and typical analysis of the cursor example, not only a burst of blood, can be extrapolate, and very common, for future avoidance.
Asyncqueryhandler Document Reference Address:
Http://developer.android.com/reference/android/content/AsyncQueryHandler.html
The following code is the Android2.3 system MMS Information home page conversationlist part of the source code, we see cursor correctly shut down?
Copy Code code 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 Code code as follows:
@Override
protected void OnStop () {
Super.onstop ();
Mlistadapter.changecursor (NULL);
}
Do you think there is a problem?
it's mainly two.:
(1). Is the cursor of the Thread_list_query_token branch properly closed?
(2). Is the cursor of the Have_locked_messages_token branch properly closed?
According to the previous analysis, the answer is:
(1). The cursor of the Thread_list_query_token branch is passed to the Mlistadapter, and Mlistadapter uses onstop inside Changecursor (null), When the user leaves the current activity, the cursor is released correctly and is not compromised.
(2). Have_locked_messages_token Branch of the cursor (is the parameter cursor), only as a condition of judgment, is not used after use, but also did not turn off, so cursor leakage, It's a mistake to run to this place under Strictmode surveillance:
E/strictmode (639): A resource is acquired at attached stack trace but never. 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 the leak problem in android.0 Jellybean, with the following code:
Copy Code code 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 addition of the unread_threads_query_token molecule and the Have_locked_messages_token branch was similar, and cursor was shut down in time for 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 was closed in time in Jellybean.
if (cursor!= null) {
Cursor.close ();
}
Break
Default
LOG.E (TAG, "onquerycomplete called with unknown token" + token);
}
}
}
Copy Code code as follows:
@Override
protected void OnStop () {
Super.onstop ();
Mlistadapter.changecursor (NULL);
}
is not to underestimate the Asyncqueryhandler, Google in the early version of there are some such code, not to mention that we do not pay attention to, in fact, many of the online use of Asyncqueryhandler examples have made this error, after reading this article, The future is not afraid of Asyncqueryhandler cursor leaked, but also may be able to solve many of the background strictmode you now apply cursor not close exception problem.
7. Summary
Although I think there are a lot of cursor not closed the situation did not say, but the fundamental problem is timely and correct shutdown cursor.
Memory leak Cursor Chapter is a summary of my work experience, specifically after the clear to me for everyone feel very helpful, let the complex problem essence, simplicity!