diff options
-rw-r--r-- | api/current.xml | 226 | ||||
-rw-r--r-- | core/java/android/app/LoaderManager.java | 123 | ||||
-rw-r--r-- | core/java/android/app/LoaderManagingFragment.java | 207 | ||||
-rw-r--r-- | core/java/android/content/CursorLoader.java | 25 | ||||
-rw-r--r-- | core/java/android/content/Loader.java | 24 | ||||
-rw-r--r-- | core/java/android/view/Window.java | 10 | ||||
-rw-r--r-- | core/java/android/widget/CursorAdapter.java | 93 | ||||
-rw-r--r-- | core/java/android/widget/ResourceCursorAdapter.java | 23 | ||||
-rw-r--r-- | core/java/android/widget/SimpleCursorAdapter.java | 25 | ||||
-rw-r--r-- | test-runner/src/android/test/LoaderTestCase.java | 2 |
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<D>" - 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<D>" - 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<D>"> </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<D>"> -</parameter> -<parameter name="data" type="D"> -</parameter> </method> -<method name="startLoading" - return="android.content.Loader<D>" - 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<D>"> </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); |