summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml226
-rw-r--r--core/java/android/app/LoaderManager.java123
-rw-r--r--core/java/android/app/LoaderManagingFragment.java207
-rw-r--r--core/java/android/content/CursorLoader.java25
-rw-r--r--core/java/android/content/Loader.java24
-rw-r--r--core/java/android/view/Window.java10
-rw-r--r--core/java/android/widget/CursorAdapter.java93
-rw-r--r--core/java/android/widget/ResourceCursorAdapter.java23
-rw-r--r--core/java/android/widget/SimpleCursorAdapter.java25
-rw-r--r--test-runner/src/android/test/LoaderTestCase.java2
10 files changed, 333 insertions, 425 deletions
diff --git a/api/current.xml b/api/current.xml
index 6ff0ee3..86023fb 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -32206,6 +32206,19 @@
visibility="public"
>
</constructor>
+<method name="destroyLoader"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
<method name="dump"
return="void"
abstract="true"
@@ -32274,12 +32287,12 @@
</method>
<method name="stopLoader"
return="void"
- abstract="true"
+ abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="id" type="int">
@@ -32323,54 +32336,7 @@
<parameter name="data" type="D">
</parameter>
</method>
-</interface>
-<class name="LoaderManagingFragment"
- extends="android.app.Fragment"
- abstract="true"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-<implements name="android.content.Loader.OnLoadCompleteListener">
-</implements>
-<constructor name="LoaderManagingFragment"
- type="android.app.LoaderManagingFragment"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="getLoader"
- return="android.content.Loader&lt;D&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-</method>
-<method name="onCreateLoader"
- return="android.content.Loader&lt;D&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="id" type="int">
-</parameter>
-<parameter name="args" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="onInitializeLoaders"
+<method name="onLoaderReset"
return="void"
abstract="true"
native="false"
@@ -32378,68 +32344,12 @@
static="false"
final="false"
deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="onLoadComplete"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
visibility="public"
>
<parameter name="loader" type="android.content.Loader&lt;D&gt;">
</parameter>
-<parameter name="data" type="D">
-</parameter>
-</method>
-<method name="onLoadFinished"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="loader" type="android.content.Loader&lt;D&gt;">
-</parameter>
-<parameter name="data" type="D">
-</parameter>
</method>
-<method name="startLoading"
- return="android.content.Loader&lt;D&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-<parameter name="args" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="stopLoading"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-</method>
-</class>
+</interface>
<class name="LocalActivityManager"
extends="java.lang.Object"
abstract="false"
@@ -49433,17 +49343,6 @@
<parameter name="cursor" type="android.database.Cursor">
</parameter>
</method>
-<method name="destroy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getProjection"
return="java.lang.String[]"
abstract="false"
@@ -54750,12 +54649,12 @@
</method>
<method name="destroy"
return="void"
- abstract="true"
+ abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -54818,6 +54717,17 @@
<parameter name="listener" type="android.content.Loader.OnLoadCompleteListener&lt;D&gt;">
</parameter>
</method>
+<method name="reset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="startLoading"
return="void"
abstract="true"
@@ -234083,7 +233993,7 @@
type="android.widget.CursorAdapter"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -234256,7 +234166,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="protected"
>
<parameter name="context" type="android.content.Context">
@@ -234266,23 +234176,6 @@
<parameter name="autoRequery" type="boolean">
</parameter>
</method>
-<method name="init"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="c" type="android.database.Cursor">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
<method name="newDropDownView"
return="android.view.View"
abstract="false"
@@ -234354,6 +234247,19 @@
<parameter name="filterQueryProvider" type="android.widget.FilterQueryProvider">
</parameter>
</method>
+<method name="swapCursor"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="newCursor" type="android.database.Cursor">
+</parameter>
+</method>
<field name="FLAG_AUTO_REQUERY"
type="int"
transient="false"
@@ -234361,7 +234267,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -243872,7 +243778,7 @@
type="android.widget.ResourceCursorAdapter"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -245304,6 +245210,24 @@
type="android.widget.SimpleCursorAdapter"
static="false"
final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="layout" type="int">
+</parameter>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="from" type="java.lang.String[]">
+</parameter>
+<parameter name="to" type="int[]">
+</parameter>
+</constructor>
+<constructor name="SimpleCursorAdapter"
+ type="android.widget.SimpleCursorAdapter"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
@@ -245317,6 +245241,8 @@
</parameter>
<parameter name="to" type="int[]">
</parameter>
+<parameter name="flags" type="int">
+</parameter>
</constructor>
<method name="bindView"
return="void"
@@ -251263,7 +251189,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
@@ -335167,7 +335093,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -335235,7 +335161,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -335259,7 +335185,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -340902,7 +340828,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -340913,7 +340839,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -340924,7 +340850,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -343294,7 +343220,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -358368,7 +358294,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 0ab987a..e5db00d 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -26,7 +26,19 @@ import java.io.PrintWriter;
/**
* Interface associated with an {@link Activity} or {@link Fragment} for managing
- * one or more {@link android.content.Loader} instances associated with it.
+ * one or more {@link android.content.Loader} instances associated with it. This
+ * helps an application manage longer-running operations in conjunction with the
+ * Activity or Fragment lifecycle; the most common use of this is with a
+ * {@link android.content.CursorLoader}, however applications are free to write
+ * their own loaders for loading other types of data.
+ *
+ * <p>As an example, here is the full implementation of a {@link Fragment}
+ * that displays a {@link android.widget.ListView} containing the results of
+ * a query against the contacts content provider. It uses a
+ * {@link android.content.CursorLoader} to manage the query on the provider.
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.java
+ * fragment_cursor}
*/
public abstract class LoaderManager {
/**
@@ -49,10 +61,48 @@ public abstract class LoaderManager {
* activity's state is saved. See {@link FragmentManager#openTransaction()
* FragmentManager.openTransaction()} for further discussion on this.
*
+ * <p>This function is guaranteed to be called prior to the release of
+ * the last data that was supplied for this Loader. At this point
+ * you should remove all use of the old data (since it will be released
+ * soon), but should not do your own release of the data since its Loader
+ * owns it and will take care of that. The Loader will take care of
+ * management of its data so you don't have to. In particular:
+ *
+ * <ul>
+ * <li> <p>The Loader will monitor for changes to the data, and report
+ * them to you through new calls here. You should not monitor the
+ * data yourself. For example, if the data is a {@link android.database.Cursor}
+ * and you place it in a {@link android.widget.CursorAdapter}, use
+ * the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context,
+ * android.database.Cursor, int)} constructor <em>without</em> passing
+ * in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY}
+ * or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER}
+ * (that is, use 0 for the flags argument). This prevents the CursorAdapter
+ * from doing its own observing of the Cursor, which is not needed since
+ * when a change happens you will get a new Cursor throw another call
+ * here.
+ * <li> The Loader will release the data once it knows the application
+ * is no longer using it. For example, if the data is
+ * a {@link android.database.Cursor} from a {@link android.content.CursorLoader},
+ * you should not call close() on it yourself. If the Cursor is being placed in a
+ * {@link android.widget.CursorAdapter}, you should use the
+ * {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)}
+ * method so that the old Cursor is not closed.
+ * </ul>
+ *
* @param loader The Loader that has finished.
* @param data The data generated by the Loader.
*/
public void onLoadFinished(Loader<D> loader, D data);
+
+ /**
+ * Called when a previously created loader is being reset, and thus
+ * making its data unavailable. The application should at this point
+ * remove any references it has to the Loader's data.
+ *
+ * @param loader The Loader that is being reset.
+ */
+ public void onLoaderReset(Loader<D> loader);
}
/**
@@ -65,20 +115,33 @@ public abstract class LoaderManager {
* will be called as the loader state changes. If at the point of call
* the caller is in its started state, and the requested loader
* already exists and has generated its data, then
- * callback. {@link LoaderCallbacks#onLoadFinished} will
+ * callback {@link LoaderCallbacks#onLoadFinished} will
* be called immediately (inside of this function), so you must be prepared
* for this to happen.
+ *
+ * @param id A unique identifier for this loader. Can be whatever you want.
+ * Identifiers are scoped to a particular LoaderManager instance.
+ * @param args Optional arguments to supply to the loader at construction.
+ * @param callback Interface the LoaderManager will call to report about
+ * changes in the state of the loader. Required.
*/
public abstract <D> Loader<D> initLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<D> callback);
/**
- * Creates a new loader in this manager, registers the callbacks to it,
+ * Starts a new or restarts an existing {@link android.content.Loader} in
+ * this manager, registers the callbacks to it,
* and (if the activity/fragment is currently started) starts loading it.
* If a loader with the same id has previously been
* started it will automatically be destroyed when the new loader completes
* its work. The callback will be delivered before the old loader
* is destroyed.
+ *
+ * @param id A unique identifier for this loader. Can be whatever you want.
+ * Identifiers are scoped to a particular LoaderManager instance.
+ * @param args Optional arguments to supply to the loader at construction.
+ * @param callback Interface the LoaderManager will call to report about
+ * changes in the state of the loader. Required.
*/
public abstract <D> Loader<D> restartLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<D> callback);
@@ -86,7 +149,15 @@ public abstract class LoaderManager {
/**
* Stops and removes the loader with the given ID.
*/
- public abstract void stopLoader(int id);
+ public abstract void destroyLoader(int id);
+
+ /**
+ * @deprecated Renamed to {@link #destroyLoader}.
+ */
+ @Deprecated
+ public void stopLoader(int id) {
+ destroyLoader(id);
+ }
/**
* Return the Loader with the given id or null if no matching Loader
@@ -191,13 +262,16 @@ class LoaderManagerImpl extends LoaderManager {
stop();
}
}
- if (mStarted && mData != null) {
- // This loader was retained, and now at the point of
- // finishing the retain we find we remain started, have
- // our data, and the owner has a new callback... so
- // let's deliver the data now.
- callOnLoadFinished(mLoader, mData);
- }
+ }
+
+ if (mStarted && mData != null) {
+ // This loader has retained its data, either completely across
+ // a configuration change or just whatever the last data set
+ // was after being restarted from a stop, and now at the point of
+ // finishing the retain we find we remain started, have
+ // our data, and the owner has a new callback... so
+ // let's deliver the data now.
+ callOnLoadFinished(mLoader, mData);
}
}
@@ -211,20 +285,34 @@ class LoaderManagerImpl extends LoaderManager {
mLoader.unregisterListener(this);
mLoader.stopLoading();
}
- mData = null;
}
}
void destroy() {
if (DEBUG) Log.v(TAG, " Destroying: " + this);
mDestroyed = true;
+ if (mCallbacks != null && mLoader != null && mData != null) {
+ String lastBecause = null;
+ if (mActivity != null) {
+ lastBecause = mActivity.mFragments.mNoTransactionsBecause;
+ mActivity.mFragments.mNoTransactionsBecause = "onLoaderReset";
+ }
+ try {
+ mCallbacks.onLoaderReset(mLoader);
+ } finally {
+ if (mActivity != null) {
+ mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+ }
+ }
+ }
mCallbacks = null;
+ mData = null;
if (mLoader != null) {
if (mListenerRegistered) {
mListenerRegistered = false;
mLoader.unregisterListener(this);
}
- mLoader.destroy();
+ mLoader.reset();
}
}
@@ -238,7 +326,9 @@ class LoaderManagerImpl extends LoaderManager {
// Notify of the new data so the app can switch out the old data before
// we try to destroy it.
mData = data;
- callOnLoadFinished(loader, data);
+ if (mStarted) {
+ callOnLoadFinished(loader, data);
+ }
if (DEBUG) Log.v(TAG, "onLoadFinished returned: " + this);
@@ -388,7 +478,7 @@ class LoaderManagerImpl extends LoaderManager {
return (Loader<D>)info.mLoader;
}
- public void stopLoader(int id) {
+ public void destroyLoader(int id) {
if (DEBUG) Log.v(TAG, "stopLoader in " + this + " of " + id);
int idx = mLoaders.indexOfKey(id);
if (idx >= 0) {
@@ -416,12 +506,13 @@ class LoaderManagerImpl extends LoaderManager {
return;
}
+ mStarted = true;
+
// Call out to sub classes so they can start their loaders
// Let the existing loaders know that we want to be notified when a load is complete
for (int i = mLoaders.size()-1; i >= 0; i--) {
mLoaders.valueAt(i).start();
}
- mStarted = true;
}
void doStop() {
diff --git a/core/java/android/app/LoaderManagingFragment.java b/core/java/android/app/LoaderManagingFragment.java
deleted file mode 100644
index f0f5856..0000000
--- a/core/java/android/app/LoaderManagingFragment.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.content.Loader;
-import android.os.Bundle;
-
-import java.util.HashMap;
-
-/**
- * A Fragment that has utility methods for managing {@link Loader}s.
- *
- * @param <D> The type of data returned by the Loader. If you're using multiple Loaders with
- * different return types use Object and case the results.
- *
- * @deprecated This was an old design, it will be removed before Honeycomb ships.
- */
-@Deprecated
-public abstract class LoaderManagingFragment<D> extends Fragment
- implements Loader.OnLoadCompleteListener<D> {
- private boolean mStarted = false;
-
- static final class LoaderInfo<D> {
- public Bundle args;
- public Loader<D> loader;
- }
- private HashMap<Integer, LoaderInfo<D>> mLoaders;
- private HashMap<Integer, LoaderInfo<D>> mInactiveLoaders;
-
- /**
- * Registers a loader with this activity, registers the callbacks on it, and starts it loading.
- * If a loader with the same id has previously been started it will automatically be destroyed
- * when the new loader completes it's work. The callback will be delivered before the old loader
- * is destroyed.
- */
- public Loader<D> startLoading(int id, Bundle args) {
- LoaderInfo<D> info = mLoaders.get(id);
- if (info != null) {
- // Keep track of the previous instance of this loader so we can destroy
- // it when the new one completes.
- mInactiveLoaders.put(id, info);
- }
- info = new LoaderInfo<D>();
- info.args = args;
- mLoaders.put(id, info);
- Loader<D> loader = onCreateLoader(id, args);
- info.loader = loader;
- if (mStarted) {
- // The activity will start all existing loaders in it's onStart(), so only start them
- // here if we're past that point of the activitiy's life cycle
- loader.registerListener(id, this);
- loader.startLoading();
- }
- return loader;
- }
-
- protected abstract Loader<D> onCreateLoader(int id, Bundle args);
- protected abstract void onInitializeLoaders();
- protected abstract void onLoadFinished(Loader<D> loader, D data);
-
- public final void onLoadComplete(Loader<D> loader, D data) {
- // Notify of the new data so the app can switch out the old data before
- // we try to destroy it.
- onLoadFinished(loader, data);
-
- // Look for an inactive loader and destroy it if found
- int id = loader.getId();
- LoaderInfo<D> info = mInactiveLoaders.get(id);
- if (info != null) {
- Loader<D> oldLoader = info.loader;
- if (oldLoader != null) {
- oldLoader.destroy();
- }
- mInactiveLoaders.remove(id);
- }
- }
-
- @Override
- public void onCreate(Bundle savedState) {
- super.onCreate(savedState);
-
- if (mLoaders == null) {
- // Look for a passed along loader and create a new one if it's not there
-// TODO: uncomment once getLastNonConfigurationInstance method is available
-// mLoaders = (HashMap<Integer, LoaderInfo>) getLastNonConfigurationInstance();
- if (mLoaders == null) {
- mLoaders = new HashMap<Integer, LoaderInfo<D>>();
- onInitializeLoaders();
- }
- }
- if (mInactiveLoaders == null) {
- mInactiveLoaders = new HashMap<Integer, LoaderInfo<D>>();
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- // Call out to sub classes so they can start their loaders
- // Let the existing loaders know that we want to be notified when a load is complete
- for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
- LoaderInfo<D> info = entry.getValue();
- Loader<D> loader = info.loader;
- int id = entry.getKey();
- if (loader == null) {
- loader = onCreateLoader(id, info.args);
- info.loader = loader;
- }
- loader.registerListener(id, this);
- loader.startLoading();
- }
-
- mStarted = true;
- }
-
- @Override
- public void onStop() {
- super.onStop();
-
- for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
- LoaderInfo<D> info = entry.getValue();
- Loader<D> loader = info.loader;
- if (loader == null) {
- continue;
- }
-
- // Let the loader know we're done with it
- loader.unregisterListener(this);
-
- // The loader isn't getting passed along to the next instance so ask it to stop loading
- if (!getActivity().isChangingConfigurations()) {
- loader.stopLoading();
- }
- }
-
- mStarted = false;
- }
-
- /* TO DO: This needs to be turned into a retained fragment.
- @Override
- public Object onRetainNonConfigurationInstance() {
- // Pass the loader along to the next guy
- Object result = mLoaders;
- mLoaders = null;
- return result;
- }
- **/
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- if (mLoaders != null) {
- for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
- LoaderInfo<D> info = entry.getValue();
- Loader<D> loader = info.loader;
- if (loader == null) {
- continue;
- }
- loader.destroy();
- }
- }
- }
-
- /**
- * Stops and removes the loader with the given ID.
- */
- public void stopLoading(int id) {
- if (mLoaders != null) {
- LoaderInfo<D> info = mLoaders.remove(id);
- if (info != null) {
- Loader<D> loader = info.loader;
- if (loader != null) {
- loader.unregisterListener(this);
- loader.destroy();
- }
- }
- }
- }
-
- /**
- * @return the Loader with the given id or null if no matching Loader
- * is found.
- */
- public Loader<D> getLoader(int id) {
- LoaderInfo<D> loaderInfo = mLoaders.get(id);
- if (loaderInfo != null) {
- return mLoaders.get(id).loader;
- }
- return null;
- }
-}
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 7776874..c73f95d 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -27,6 +27,7 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> {
Cursor mCursor;
ForceLoadContentObserver mObserver;
boolean mStopped;
+ boolean mReset;
Uri mUri;
String[] mProjection;
String mSelection;
@@ -57,7 +58,7 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> {
/* Runs on the UI thread */
@Override
public void deliverResult(Cursor cursor) {
- if (mStopped) {
+ if (mReset) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
@@ -66,9 +67,12 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> {
}
Cursor oldCursor = mCursor;
mCursor = cursor;
- super.deliverResult(cursor);
- if (oldCursor != null && !oldCursor.isClosed()) {
+ if (!mStopped) {
+ super.deliverResult(cursor);
+ }
+
+ if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
@@ -94,6 +98,7 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> {
@Override
public void startLoading() {
mStopped = false;
+ mReset = false;
if (mCursor != null) {
deliverResult(mCursor);
@@ -107,11 +112,6 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> {
*/
@Override
public void stopLoading() {
- if (mCursor != null && !mCursor.isClosed()) {
- mCursor.close();
- }
- mCursor = null;
-
// Attempt to cancel the current load task if possible.
cancelLoad();
@@ -127,9 +127,16 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> {
}
@Override
- public void destroy() {
+ public void reset() {
+ mReset = true;
+
// Ensure the loader is stopped
stopLoading();
+
+ if (mCursor != null && !mCursor.isClosed()) {
+ mCursor.close();
+ }
+ mCursor = null;
}
public Uri getUri() {
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 234096a..73d7103 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -128,7 +128,7 @@ public abstract class Loader<D> {
* the data set and may deliver future callbacks if the source changes. Calling
* {@link #stopLoading} will stop the delivery of callbacks.
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
public abstract void startLoading();
@@ -141,22 +141,34 @@ public abstract class Loader<D> {
/**
* Stops delivery of updates until the next time {@link #startLoading()} is called
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
public abstract void stopLoading();
/**
- * Destroys the loader and frees its resources, making it unusable.
+ * 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
+ * {@link #startLoading()} may later be called at which point it must be
+ * able to start running again.
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
- public abstract void destroy();
+ public void reset() {
+ destroy();
+ }
+
+ /**
+ * @deprecated Old API, implement reset() now.
+ */
+ @Deprecated
+ public void destroy() {
+ }
/**
* Called when {@link ForceLoadContentObserver} detects a change. Calls {@link #forceLoad()}
* by default.
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
public void onContentChanged() {
forceLoad();
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 5385cd9..8446a8f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -576,14 +576,16 @@ public abstract class Window {
/**
* Set the width and height layout parameters of the window. The default
- * for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT to
- * make a window that is not full-screen.
+ * for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT
+ * or an absolute value to make a window that is not full-screen.
*
* @param width The desired layout width of the window.
* @param height The desired layout height of the window.
+ *
+ * @see ViewGroup.LayoutParams#height
+ * @see ViewGroup.LayoutParams#width
*/
- public void setLayout(int width, int height)
- {
+ public void setLayout(int width, int height) {
final WindowManager.LayoutParams attrs = getAttributes();
attrs.width = width;
attrs.height = height;
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index 4cf8785..516162a 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -67,7 +67,7 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable,
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
- protected DataSetObserver mDataSetObserver = new MyDataSetObserver();
+ protected DataSetObserver mDataSetObserver;
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -81,54 +81,81 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable,
/**
* If set the adapter will call requery() on the cursor whenever a content change
- * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+ * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*/
+ @Deprecated
public static final int FLAG_AUTO_REQUERY = 0x01;
/**
* If set the adapter will register a content observer on the cursor and will call
- * {@link #onContentChanged()} when a notification comes in.
+ * {@link #onContentChanged()} when a notification comes in. Be careful when
+ * using this flag: you will need to unset the current Cursor from the adapter
+ * to avoid leaks due to its registered observers. This flag is not needed
+ * when using a CursorAdapter with a
+ * {@link android.content.CursorLoader}.
*/
public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;
/**
- * Constructor. The adapter will call requery() on the cursor whenever
- * it changes so that the most recent data is always displayed.
+ * Constructor that always enables auto-requery.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*
* @param c The cursor from which to get the data.
* @param context The context
*/
+ @Deprecated
public CursorAdapter(Context context, Cursor c) {
init(context, c, FLAG_AUTO_REQUERY);
}
/**
- * Constructor
+ * Constructor that allows control over auto-requery. It is recommended
+ * you not use this, but instead {@link #CursorAdapter(Context, Cursor, int)}.
+ * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+ * will always be set.
+ *
* @param c The cursor from which to get the data.
* @param context The context
* @param autoRequery If true the adapter will call requery() on the
* cursor whenever it changes so the most recent
- * data is always displayed.
+ * data is always displayed. Using true here is discouraged.
*/
public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
}
/**
- * Constructor
+ * Recommended constructor.
+ *
* @param c The cursor from which to get the data.
* @param context The context
- * @param flags flags used to determine the behavior of the adapter
+ * @param flags Flags used to determine the behavior of the adapter; may
+ * be any combination of {@link #FLAG_AUTO_REQUERY} and
+ * {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
*/
public CursorAdapter(Context context, Cursor c, int flags) {
init(context, c, flags);
}
+ /**
+ * @deprecated Don't use this, use the normal constructor. This will
+ * be removed in the future.
+ */
+ @Deprecated
protected void init(Context context, Cursor c, boolean autoRequery) {
init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
}
- protected void init(Context context, Cursor c, int flags) {
+ void init(Context context, Cursor c, int flags) {
if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
flags |= FLAG_REGISTER_CONTENT_OBSERVER;
mAutoRequery = true;
@@ -142,13 +169,15 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable,
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
mChangeObserver = new ChangeObserver();
+ mDataSetObserver = new MyDataSetObserver();
} else {
mChangeObserver = null;
+ mDataSetObserver = null;
}
if (cursorPresent) {
if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
- c.registerDataSetObserver(mDataSetObserver);
+ if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
}
}
@@ -275,22 +304,39 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable,
* 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
+ * @param cursor The new cursor to be used
*/
public void changeCursor(Cursor cursor) {
- if (cursor == mCursor) {
- return;
+ 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;
}
- if (mCursor != null) {
- if (mChangeObserver != null) mCursor.unregisterContentObserver(mChangeObserver);
- mCursor.unregisterDataSetObserver(mDataSetObserver);
- mCursor.close();
+ Cursor oldCursor = mCursor;
+ if (oldCursor != null) {
+ if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
+ if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
- mCursor = cursor;
- if (cursor != null) {
- if (mChangeObserver != null) cursor.registerContentObserver(mChangeObserver);
- cursor.registerDataSetObserver(mDataSetObserver);
- mRowIDColumn = cursor.getColumnIndexOrThrow("_id");
+ 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();
@@ -300,6 +346,7 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable,
// notify the observers about the lack of a data set
notifyDataSetInvalidated();
}
+ return oldCursor;
}
/**
diff --git a/core/java/android/widget/ResourceCursorAdapter.java b/core/java/android/widget/ResourceCursorAdapter.java
index aee411e..7341c2c 100644
--- a/core/java/android/widget/ResourceCursorAdapter.java
+++ b/core/java/android/widget/ResourceCursorAdapter.java
@@ -35,13 +35,19 @@ public abstract class ResourceCursorAdapter extends CursorAdapter {
private LayoutInflater mInflater;
/**
- * Constructor.
+ * Constructor the enables auto-requery.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*
* @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
*/
+ @Deprecated
public ResourceCursorAdapter(Context context, int layout, Cursor c) {
super(context, c);
mLayout = mDropDownLayout = layout;
@@ -49,7 +55,11 @@ public abstract class ResourceCursorAdapter extends CursorAdapter {
}
/**
- * Constructor.
+ * Constructor with default behavior as per
+ * {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended
+ * you not use this, but instead {@link #ResourceCursorAdapter(Context, int, Cursor, int)}.
+ * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+ * will always be set.
*
* @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
@@ -58,7 +68,7 @@ public abstract class ResourceCursorAdapter extends CursorAdapter {
* @param c The cursor from which to get the data.
* @param autoRequery If true the adapter will call requery() on the
* cursor whenever it changes so the most recent
- * data is always displayed.
+ * data is always displayed. Using true here is discouraged.
*/
public ResourceCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
@@ -67,14 +77,15 @@ public abstract class ResourceCursorAdapter extends CursorAdapter {
}
/**
- * Constructor.
+ * Standard constructor.
*
* @param context The context where the ListView associated with this adapter is running
- * @param layout resource identifier of a layout file that defines the views
+ * @param layout Resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
* @param c The cursor from which to get the data.
- * @param flags flags used to determine the behavior of the adapter
+ * @param flags Flags used to determine the behavior of the adapter,
+ * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
*/
public ResourceCursorAdapter(Context context, int layout, Cursor c, int flags) {
super(context, c, flags);
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index d1c2270..497610c 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -66,7 +66,23 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
String[] mOriginalFrom;
/**
- * Constructor.
+ * Constructor the enables auto-requery.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
+ */
+ @Deprecated
+ public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
+ super(context, layout, c);
+ mTo = to;
+ mOriginalFrom = from;
+ findColumns(from);
+ }
+
+ /**
+ * Standard constructor.
*
* @param context The context where the ListView associated with this
* SimpleListItemFactory is running
@@ -80,9 +96,12 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
* These should all be TextViews. The first N views in this list
* are given the values of the first N columns in the from
* parameter. Can be null if the cursor is not available yet.
+ * @param flags Flags used to determine the behavior of the adapter,
+ * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
*/
- public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
- super(context, layout, c);
+ public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from,
+ int[] to, int flags) {
+ super(context, layout, c, flags);
mTo = to;
mOriginalFrom = from;
findColumns(from);
diff --git a/test-runner/src/android/test/LoaderTestCase.java b/test-runner/src/android/test/LoaderTestCase.java
index 8be6590..c8564c2 100644
--- a/test-runner/src/android/test/LoaderTestCase.java
+++ b/test-runner/src/android/test/LoaderTestCase.java
@@ -64,7 +64,7 @@ public class LoaderTestCase extends AndroidTestCase {
// Shut the loader down
completedLoader.unregisterListener(this);
completedLoader.stopLoading();
- completedLoader.destroy();
+ completedLoader.reset();
// Store the result, unblocking the test thread
queue.add(data);