Introduction :
This article describes how to detect Cursor leaks on Android and how to use them, and also points out several common examples of errors. There are some leaks that are hard to detect in the code, but the program will inevitably appear abnormally after a long run. at the same time, this method is also suitable for other situations that need to detect resource leaks .
Recently found a vegetable mobile phone connector A serious Cursor leak occurs when querying the media store (Mediaprovider) database, which causes all programs in the system that are using the database to become unusable after a period of time has elapsed. In addition, in the work also often found that some applications have a Cursor leakage phenomenon, because it takes a long time to run before the exception, so some of these bugs have not been discovered for a long time.
But once the Cursor leaks accumulate to a certain number (usually hundreds of), there is a case that the database cannot be queried, only if the process of the database service is dead and restarted to return to normal. The usual error message is as follows, indicating that a PID program opened 866 Cursor without closing, resulting in exception:
3634 3644 E Javabinder: * * * uncaught remote exception! (Exceptions is not yet supported across processes.) 3634 3644 E JavaBinder:android.database.CursorWindowAllocationException:Cursor window allocation of 2048 KB failed. # Open cursors=866 (# Cursors opened by PID 1565=866) 3634 3644 E javabinder:at Android.database.CursorWindow. (cursorwindow.java:104) 3634 3644 E javabinder:at Android.database.AbstractWindowedCursor.clearOrCreateWindow ( abstractwindowedcursor.java:198) 3634 3644 E javabinder:at Android.database.sqlite.SQLiteCursor.fillWindow ( sqlitecursor.java:147) 3634 3644 E javabinder:at android.database.sqlite.SQLiteCursor.getCount (Sqlitecursor.java : 141) 3634 3644 E javabinder:at android.database.CursorToBulkCursorAdaptor.getBulkCursorDescriptor ( cursortobulkcursoradaptor.java:143) 3634 3644 E javabinder:at android.content.ContentProviderNative.onTransact ( contentprovidernative.java:118) 3634 3644 e javabinder:at android.os.Binder.execTransact (binder.java:367) 3634 3644 E JavabindeR:at Dalvik.system.NativeStart.run (Native Method) |
1. Cursor Detection principle
When the Cursor object is run to the Finalize () method by the JVM recycle, the detection of the close () method has not been invoked, and this approach is also applied in Contentresolver. The simplified example code is as follows:
1 Import android.database.Cursor; 2 Import Android.database.CursorWrapper; 3 Import Android.util.Log; 4 5 public class Testcursor extends Cursorwrapper {6 private static final String TAG = "Testcursor"; 7 Privat E Boolean misclosed = false; 8 private Throwable mtrace; 9 public testcursor (Cursor c) {One super (c); mtrace = new Throwable ("Explicit termination method ' close () ' not called"), }14 @Override16 public void Close () {17< c12/>misclosed = true;18 }19 @Override21 public Void Finalize () throws Throwable { try { if (misclosed! = True) { log.e (TAG, "Cursor leaks", mtrace); }26 } finally { Super.finalize (); }29 }30}
Then, when querying, return the testcursor as the query result to the APP:
1 return new testcursor (cursor); The cursor is the result of a normal query, such as from Contentprovider.query ()
This method is also suitable for all cases where an explicit release resource method needs to be detected without being called, which is a common method. But the test in the Finalize () method requires attention
Advantages: Accurate. Because the resource was not released when the Cursor object was reclaimed, there must have been a resource leak.
Disadvantage: Depending on the Finalize () method, it is also dependent on the JVM's garbage collection policy. For example, an APP now has 10 Cursor object leaks, and these 10 objects are no longer pointed to in a recyclable state by any reference, but the JVM may not be recycled immediately (time is unpredictable), and if you check now you are not able to find the problem. Also, in some cases, even if the object is reclaimed, Finalize () may not be executed, that is, there is no guarantee that all problems will be detected. For more information on finalize () refer to the Item 7:avoid of effective Java 2nd Edition finalizers
2. How to use it for APP developers
Starting with Gingerbread, Android provides the Strictmode tool to help developers check if they have accidentally done something they shouldn't. The use of the method is to set the Strictmode in the Activity, the following example is open to check the leaking SQLite object and Closeable object (ordinary cursor/fileinputstream, etc.) function, found that there is a violation of the record log and make The program forcibly exits.
1 Import Android.os.StrictMode; 2 3 public class Testactivity extends Activity {4 private static final Boolean Developer_mode = true; 5 Publi c void OnCreate () {6 if (developer_mode) {7 strictmode.setvmpolicy (New StrictMode.VMPolicy.Builder () 8 . Detectleakedsqlliteobjects () 9 . Detectleakedclosableobjects () Penaltylog (). Penaltydeath () 12 . Build ()); }14 super.oncreate (); }16}
For framework Developers
If you are providing database data through ContentProvider, there are closeguard classes in contentresolver that have similar detection but need to open themselves (the above example is also open Closeguard):
1 closeguard.setenabled (TRUE);
The more recommended approach is to follow the detection principle in the first section of this article, in the Contentresolver internal class Cursorwrapperinner inside the added. It is also possible to use this detection principle when other needs to detect a resource-like leak.
3. Error-Prone areas
Forgetting to call Close () has nothing to say about this low-level error, which should also account for a small proportion. Here is a less obvious example.
Early return
Sometimes careless makes this mistake, and return it before close (), especially if the function is more complex than the larger logic is more likely to err. This can be resolved by placing close () in the finally block of code
1 private void Method () {2 cursor cursor = query ();//Suppose query () is a function that queries the database to return a Cursor result 3 if (flag = = False) {
//!! Early return 4 return;5 }6 cursor.close (); 7}
member variables of the class
Suppose the class has a member variable that is globally valid in the class, gets the result of the query in method A, and then gets the result of the query in another place, then the second query should close the previous Cursor object first.
1 public class Testcursor {2 private Cursor mcursor; 3 4 private void MethodA () {5 mcursor = query (); 6 } 7 8 private void MethodB () {9 ///!! The previous cursor object must be closed first mcursor = query (); }12}
Note: There have been people who have doubts about Mcursor, it is clearly the same variable why need to close first? First, Mcursor is a reference to a cursor object, and mcursor points to a cursor object returned by query () when MethodA is 1; at MethodB () it points to another cursor object 2 that is returned. You must close Cursor Object 1 before pointing to Cursor Object 2, or the Cursor object 1 does not call Close () before Finalize ().
Exception handling
The code between the open and close Cursor appears exception, causing no running to the closed place:
1 try {2 cursor cursor = query (), 3 //middle omits some code that appears exception 4 cursor.close (); 5} catch (Exception e) {6 //!!! An exception did not run to Cursor.close () 7}
In this case, close () should be placed inside the finally code block:
Press CTRL + C to copy the code<textarea></textarea>Press CTRL + C to copy code 4. Summary thinking
In Finalize () the detection is feasible, and basically can meet the needs. For finalize () execution time uncertainties and possible non-execution issues, can be partially resolved by recording the number of Cursor currently open not closed, more than a certain number of warnings, the combination of two means.
Are there any other testing options? There, in the Cursor construction method and the close () method add log, run for a period of time to check the log to see where it is not closed. Simplify the code as follows:
1 Import android.database.Cursor; 2 Import Android.database.CursorWrapper; 3 Import Android.util.Log; 4 5 public class Testcursor extends Cursorwrapper {6 private static final String TAG = "Testcursor"; 7 Privat e throwable mtrace; 8 9 Public testcursor (Cursor c) {Ten super (c); mtrace = new Throwable ("Cusor opened here") LOG.D (TAG, "Cursor" + this.hashcode () + "opened, StackTrace is:", mtrace); }14 @Override16 Pub LIC void Close () { misclosed = true;18 log.d (TAG, "Cursor" + this.hashcode () + "closed."); }20}
Check to see if a hashcode () Cursor has called the Close () method, there is no indication that the resource is compromised. The advantages of this method are equally accurate and more reliable. The disadvantage is that you need to check a large number of logs, and the open/close places may be far apart, if you do not write a small script analysis of artificial look will be more painful;
Reprint Please specify source: http://www.cnblogs.com/imouto/archive/2013/01/14/how-to-detect-leaked-cursor.html
External image of this article: Http://oteku.blogspot.com/2013/01/how-to-detect-android-cursor-leak-c
Summary: Frequent use of the database, be sure to pay attention to the cursor problem, with the use of off, do not appear this cursor is still not closed by the system, and opened a cursor of the possibility, in addition to pay attention to the cursor of the empty.
How to detect Android Cursor leaks