Fix issue #3381489: IllegalStateException: attempt to re-open...
...an already-closed object: android.database.sqlite.SQLiteQuery It turns out there is a state we are missing -- the loader is still needed, but in the inactive list. In this case the loader needs to continue holding on to its current data, and not deliver any new data (which would result in it releasing its old data). This introduces the new state to Loader, and uses it in AsyncTaskLoader so all subclasses of that should get the new correct behavior. A further improvement would be to unregister CursorLoader's content listener when going in to this state, but that can wait for later. Change-Id: I6d30173b94f8e30b5be31d018accd328cc3388ec
This commit is contained in:
46
api/11.xml
46
api/11.xml
@@ -43763,19 +43763,6 @@
|
||||
<parameter name="data" type="D">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onCancelled"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="data" type="D">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onLoadInBackground"
|
||||
return="D"
|
||||
abstract="false"
|
||||
@@ -55609,6 +55596,17 @@
|
||||
<parameter name="context" type="android.content.Context">
|
||||
</parameter>
|
||||
</constructor>
|
||||
<method name="abandon"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="dataToString"
|
||||
return="java.lang.String"
|
||||
abstract="false"
|
||||
@@ -55687,6 +55685,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="isAbandoned"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="isReset"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
@@ -55709,6 +55718,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="onAbandon"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="protected"
|
||||
>
|
||||
</method>
|
||||
<method name="onContentChanged"
|
||||
return="void"
|
||||
abstract="false"
|
||||
|
||||
@@ -43763,19 +43763,6 @@
|
||||
<parameter name="data" type="D">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onCancelled"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="data" type="D">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onLoadInBackground"
|
||||
return="D"
|
||||
abstract="false"
|
||||
@@ -55609,6 +55596,17 @@
|
||||
<parameter name="context" type="android.content.Context">
|
||||
</parameter>
|
||||
</constructor>
|
||||
<method name="abandon"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="dataToString"
|
||||
return="java.lang.String"
|
||||
abstract="false"
|
||||
@@ -55687,6 +55685,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="isAbandoned"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="isReset"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
@@ -55709,6 +55718,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="onAbandon"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="protected"
|
||||
>
|
||||
</method>
|
||||
<method name="onContentChanged"
|
||||
return="void"
|
||||
abstract="false"
|
||||
|
||||
@@ -587,6 +587,7 @@ class LoaderManagerImpl extends LoaderManager {
|
||||
if (DEBUG) Log.v(TAG, " Removing last inactive loader: " + info);
|
||||
inactive.mDeliveredData = false;
|
||||
inactive.destroy();
|
||||
info.mLoader.abandon();
|
||||
mInactiveLoaders.put(id, info);
|
||||
} else {
|
||||
// We already have an inactive loader for this ID that we are
|
||||
@@ -617,6 +618,7 @@ class LoaderManagerImpl extends LoaderManager {
|
||||
// Keep track of the previous instance of this loader so we can destroy
|
||||
// it when the new one completes.
|
||||
if (DEBUG) Log.v(TAG, " Making last loader inactive: " + info);
|
||||
info.mLoader.abandon();
|
||||
mInactiveLoaders.put(id, info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,11 +169,6 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> {
|
||||
* to properly dispose of the result.
|
||||
*/
|
||||
public void onCanceled(D data) {
|
||||
onCancelled(data);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void onCancelled(D data) {
|
||||
}
|
||||
|
||||
void executePendingTask() {
|
||||
@@ -214,10 +209,15 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> {
|
||||
if (DEBUG) Slog.v(TAG, "Load complete of old task, trying to cancel");
|
||||
dispatchOnCancelled(task, data);
|
||||
} else {
|
||||
mLastLoadCompleteTime = SystemClock.uptimeMillis();
|
||||
mTask = null;
|
||||
if (DEBUG) Slog.v(TAG, "Delivering result");
|
||||
deliverResult(data);
|
||||
if (isAbandoned()) {
|
||||
// This cursor has been abandoned; just cancel the new data.
|
||||
onCanceled(data);
|
||||
} else {
|
||||
mLastLoadCompleteTime = SystemClock.uptimeMillis();
|
||||
mTask = null;
|
||||
if (DEBUG) Slog.v(TAG, "Delivering result");
|
||||
deliverResult(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ public class Loader<D> {
|
||||
OnLoadCompleteListener<D> mListener;
|
||||
Context mContext;
|
||||
boolean mStarted = false;
|
||||
boolean mAbandoned = false;
|
||||
boolean mReset = true;
|
||||
boolean mContentChanged = false;
|
||||
|
||||
@@ -150,6 +151,15 @@ public class Loader<D> {
|
||||
return mStarted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this loader has been abandoned. In this state, the
|
||||
* loader <em>must not</em> report any new data, and <em>must</em> keep
|
||||
* its last reported data valid until it is finally reset.
|
||||
*/
|
||||
public boolean isAbandoned() {
|
||||
return mAbandoned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this load has been reset. That is, either the loader
|
||||
* has not yet been started for the first time, or its {@link #reset()}
|
||||
@@ -177,6 +187,7 @@ public class Loader<D> {
|
||||
public final void startLoading() {
|
||||
mStarted = true;
|
||||
mReset = false;
|
||||
mAbandoned = false;
|
||||
onStartLoading();
|
||||
}
|
||||
|
||||
@@ -235,6 +246,28 @@ public class Loader<D> {
|
||||
protected void onStopLoading() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the Loader that it is being abandoned. This is called prior
|
||||
* to {@link #reset} to have it retain its current data but not report
|
||||
* any new data.
|
||||
*/
|
||||
public void abandon() {
|
||||
mAbandoned = true;
|
||||
onAbandon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses implement this to take care of being abandoned. This is
|
||||
* an optional intermediate state prior to {@link #onReset()} -- it means that
|
||||
* the client is no longer interested in any new data from the loader,
|
||||
* so the loader must not report any further updates. However, the
|
||||
* loader <em>must</em> keep its last reported data valid until the final
|
||||
* {@link #onReset()} happens. You can retrieve the current abandoned
|
||||
* state with {@link #isAbandoned}.
|
||||
*/
|
||||
protected void onAbandon() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the state of the Loader. The Loader should at this point free
|
||||
* all of its resources, since it may never be called again; however, its
|
||||
@@ -251,6 +284,7 @@ public class Loader<D> {
|
||||
onReset();
|
||||
mReset = true;
|
||||
mStarted = false;
|
||||
mAbandoned = false;
|
||||
mContentChanged = false;
|
||||
}
|
||||
|
||||
@@ -327,6 +361,7 @@ public class Loader<D> {
|
||||
writer.print(" mListener="); writer.println(mListener);
|
||||
writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
|
||||
writer.print(" mContentChanged="); writer.print(mContentChanged);
|
||||
writer.print(" mAbandoned="); writer.print(mAbandoned);
|
||||
writer.print(" mReset="); writer.println(mReset);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user