Brief Introduction:
This article describes how to detect Cursor leaks in Android and how to use them, and also points out several common error examples. Some leaks are difficult to detect in the code, but the program is bound to appear abnormally after a long run of time. At the same time, this method is also suitable for other situations that need to detect resource leakage.
Recently found that a vegetable mobile phone connector in the query media store (mediaprovider) database, a serious Cursor leakage phenomenon, after running for a period of time will cause all the system to use the database program can not use. In addition, in the work also often found that some applications have Cursor leakage phenomenon, because it takes a long time to run the exception, so some such bugs have been found for a long time.
However, once the Cursor leakage accumulated to a certain number (usually hundreds of) will inevitably appear unable to query the database, only the database service where the process of death restart to return to normal. The usual error message is as follows, pointing out that a PID program opened 866 Cursor without shutting down, leading to the exception:
Copy Code code as follows:
3634 3644 E javabinder: uncaught remote exception! (Exceptions are 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
Detection of the Close () method is not invoked when the Cursor object is run by the JVM to the Finalize () method, and is also applied in Contentresolver. The simplified sample code is as follows:
Copy Code code as follows:
Import Android.database.Cursor;
Import Android.database.CursorWrapper;
Import Android.util.Log;
public class Testcursor extends Cursorwrapper {
private static final String TAG = "Testcursor";
Private Boolean misclosed = false;
Private Throwable mtrace;
Public Testcursor (Cursor c) {
Super (c);
Mtrace = new Throwable ("Explicit termination method ' close () ' not called");
}
@Override
public void Close () {
Misclosed = true;
}
@Override
public void Finalize () throws Throwable {
try {
if (misclosed!= true) {
LOG.E (TAG, "Cursor leaks", mtrace);
}
finally {
Super.finalize ();
}
}
}
Then, when querying, return the testcursor as a query result to the APP:
1 return new testcursor (cursor); Cursor is the result of a normal query, for example, from Contentprovider.query ()
This method is also suitable for all cases where the explicit release resource method needs to be detected without being invoked, and is a general method. But testing in the Finalize () method requires attention
AdvantagesAccurate Because the resource was not released when the Cursor object was reclaimed, a resource leak must have occurred.
Disadvantage: Relying on the Finalize () method also relies on the JVM's garbage collection policy. For example, an APP now has 10 Cursor objects leaking, and these 10 objects are no longer referenced by any references to be recyclable, but the JVM may not be recycled immediately (unpredictable time), if you are not able to detect the problem now. In addition, in some cases even if the object is recycled Finalize () may not be executed, that is, it is not guaranteed to detect all problems. For more information on Finalize (), refer to the Item 7:avoid of the effective Java 2nd Edition finalizers
2. How to use
For APP Developers
Starting with Gingerbread, Android provides strictmode tools to help developers check if they accidentally did something they shouldn't. The use of the method is to set strictmode in the activity, the following example is open to check the leak of the SQLite object and Closeable objects (ordinary cursor/fileinputstream, etc.) functions, found that there is a violation of the log and the The program forcibly exits.
Copy Code code as follows:
Import Android.os.StrictMode;
public class Testactivity extends activity {
Private static Final Boolean Developer_mode = true;
public void OnCreate () {
if (Developer_mode) {
Strictmode.setvmpolicy (New StrictMode.VMPolicy.Builder ()
. Detectleakedsqlliteobjects ()
. Detectleakedclosableobjects ()
. Penaltylog ()
. Penaltydeath ()
. build ());
}
Super.oncreate ();
}
}
For framework Developers
If it is through ContentProvider to provide database data, in the Contentresolver has Closeguard class to implement similar detection, but need to open themselves (the above example is also open Closeguard):
1 closeguard.setenabled (TRUE); the more recommended approach is to join the Contentresolver inner class Cursorwrapperinner in accordance with the detection principle in the first section of this article. Other needs to detect similar to resource leaks, the same can be used to detect the principle.
3. Error-Prone areas
Forget to call Close () there is nothing to say about this low-level error, which should also account for a small proportion. Here are some examples that are not so obvious.
return early
Sometimes carelessness makes such a mistake, and return before close (), especially when the function is more complex and the logic is more difficult to make mistakes. This situation can be solved by putting close () in a finally code block
Copy Code code as follows:
private void Method () {
Cursor Cursor = query (); Suppose query () is a function that queries the database to return Cursor results
if (flag = false) {//!!! return early
Return
}
Cursor.close ();
}
class's member variable
Suppose the class has a member variable that is globally valid in the class, the query results are obtained in method A, and then the query results are obtained elsewhere, so the second query should first close the previous Cursor object.
Copy Code code as follows:
public class Testcursor {
Private Cursor mcursor;
private void MethodA () {
Mcursor = query ();
}
private void MethodB () {
!! You must first close the previous cursor object
Mcursor = query ();
}
}
Note: Once met someone to mcursor feel puzzled, clearly is the same variable why still need to close first? First Mcursor is a reference to a Cursor object, which, when MethodA, points to a Cursor object returned by query (), and at MethodB () it points to another Cursor object 2 that is returned. Cursor Object 1 must be closed before pointing to Cursor Object 2, or the Cursor object 1 does not call Close () before Finalize ().
Exception Handling
The code between opening and closing Cursor appears exception, causing no running to the closed place:
Copy Code code as follows:
try {
Cursor Cursor = query ();
Omit some code that appears to be abnormal in the middle
Cursor.close ();
catch (Exception e) {
!! An exception did not run to Cursor.close ()
}
In this case, close () should be placed in the finally code block:
Copy Code code as follows:
Cursor Cursor = null;
try {
cursor = query ();
Omit some code that appears to be abnormal in the middle
catch (Exception e) {
An exception occurred
finally {
if (cursor!= null)
Cursor.close ();
}
4. Concluding thoughts
Testing in Finalize () is feasible and basic enough to meet the needs. For the Finalize () execution time uncertainty and may not execute the question, may by the record currently opens does not turn off the number of Cursor to partially solve, exceeds a certain number to issue the warning, two kinds of means unifies.
Is there any other way to test it? There, in the Cursor construction method and the close () method add log, run for a period of time after checking the log to see where is not closed. Simplify the code as follows:
Copy Code code as follows:
Import Android.database.Cursor;
Import Android.database.CursorWrapper;
Import Android.util.Log;
public class Testcursor extends Cursorwrapper {
private static final String TAG = "Testcursor";
Private Throwable mtrace;
Public Testcursor (Cursor c) {
Super (c);
Mtrace = new Throwable ("Cusor opened here");
LOG.D (TAG, "Cursor" + this.hashcode () + "opened, StackTrace is:", mtrace);
}
@Override
public void Close () {
Misclosed = true;
LOG.D (TAG, "Cursor" + this.hashcode () + "closed.");
}
}
Check to see if a hashcode () Cursor has called the Close () method, and there is no indication that the resource has been compromised. The advantages of this method are equally accurate and more reliable. The disadvantage is that you need to check a large number of log, and open/close the place may be far away, if not write a small script analysis of the human look will be more painful, and the APP must be completely exited to check, because some Cursor in the background running is still in normal use.