Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4911b783 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Fix bug where loader listener would be registered twice.

Also change LoaderManager to an interface.

Change-Id: Ife6d123e56cc6f63b6fe902afac35b2673761e60
parent 506821b4
Loading
Loading
Loading
Loading
+7 −8
Original line number Diff line number Diff line
@@ -28537,9 +28537,8 @@
</parameter>
</method>
</class>
<class name="LoaderManager"
 extends="java.lang.Object"
 abstract="false"
<interface name="LoaderManager"
 abstract="true"
 static="false"
 final="false"
 deprecated="not deprecated"
@@ -28547,7 +28546,7 @@
>
<method name="getLoader"
 return="android.content.Loader&lt;D&gt;"
 abstract="false"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
@@ -28560,7 +28559,7 @@
</method>
<method name="initLoader"
 return="android.content.Loader&lt;D&gt;"
 abstract="false"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
@@ -28577,7 +28576,7 @@
</method>
<method name="restartLoader"
 return="android.content.Loader&lt;D&gt;"
 abstract="false"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
@@ -28594,7 +28593,7 @@
</method>
<method name="stopLoader"
 return="void"
 abstract="false"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
@@ -28605,7 +28604,7 @@
<parameter name="id" type="int">
</parameter>
</method>
</class>
</interface>
<interface name="LoaderManager.LoaderCallbacks"
 abstract="true"
 static="true"
+8 −8
Original line number Diff line number Diff line
@@ -651,7 +651,7 @@ public class Activity extends ContextThemeWrapper
        Object activity;
        HashMap<String, Object> children;
        ArrayList<Fragment> fragments;
        SparseArray<LoaderManager> loaders;
        SparseArray<LoaderManagerImpl> loaders;
    }
    /* package */ NonConfigurationInstances mLastNonConfigurationInstances;
    
@@ -669,8 +669,8 @@ public class Activity extends ContextThemeWrapper

    final FragmentManager mFragments = new FragmentManager();
    
    SparseArray<LoaderManager> mAllLoaderManagers;
    LoaderManager mLoaderManager;
    SparseArray<LoaderManagerImpl> mAllLoaderManagers;
    LoaderManagerImpl mLoaderManager;
    
    private static final class ManagedCursor {
        ManagedCursor(Cursor cursor) {
@@ -779,13 +779,13 @@ public class Activity extends ContextThemeWrapper
        return mLoaderManager;
    }
    
    LoaderManager getLoaderManager(int index, boolean started) {
    LoaderManagerImpl getLoaderManager(int index, boolean started) {
        if (mAllLoaderManagers == null) {
            mAllLoaderManagers = new SparseArray<LoaderManager>();
            mAllLoaderManagers = new SparseArray<LoaderManagerImpl>();
        }
        LoaderManager lm = mAllLoaderManagers.get(index);
        LoaderManagerImpl lm = mAllLoaderManagers.get(index);
        if (lm == null) {
            lm = new LoaderManager(started);
            lm = new LoaderManagerImpl(started);
            mAllLoaderManagers.put(index, lm);
        }
        return lm;
@@ -1554,7 +1554,7 @@ public class Activity extends ContextThemeWrapper
            // prune out any loader managers that were already stopped, so
            // have nothing useful to retain.
            for (int i=mAllLoaderManagers.size()-1; i>=0; i--) {
                LoaderManager lm = mAllLoaderManagers.valueAt(i);
                LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i);
                if (lm.mRetaining) {
                    retainLoaders = true;
                } else {
+1 −1
Original line number Diff line number Diff line
@@ -213,7 +213,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
    // The View generated for this fragment.
    View mView;
    
    LoaderManager mLoaderManager;
    LoaderManagerImpl mLoaderManager;
    boolean mStarted;
    
    /**
+80 −49
Original line number Diff line number Diff line
@@ -17,29 +17,82 @@
package android.app;

import android.content.Loader;
import android.content.Loader.OnLoadCompleteListener;
import android.os.Bundle;
import android.util.SparseArray;

/**
 * Object associated with an {@link Activity} or {@link Fragment} for managing
 * Interface associated with an {@link Activity} or {@link Fragment} for managing
 * one or more {@link android.content.Loader} instances associated with it.
 */
public class LoaderManager {
    final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>();
    final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>();
    boolean mStarted;
    boolean mRetaining;
    boolean mRetainingStarted;
    
public interface LoaderManager {
    /**
     * Callback interface for a client to interact with the manager.
     */
    public interface LoaderCallbacks<D> {
        /**
         * Instantiate and return a new Loader for the given ID.
         *
         * @param id The ID whose loader is to be created.
         * @param args Any arguments supplied by the caller.
         * @return Return a new Loader instance that is ready to start loading.
         */
        public Loader<D> onCreateLoader(int id, Bundle args);

        /**
         * Called when a previously created loader has finished its load.
         * @param loader The Loader that has finished.
         * @param data The data generated by the Loader.
         */
        public void onLoadFinished(Loader<D> loader, D data);
    }
    
    /**
     * Ensures a loader is initialized and active.  If the loader doesn't
     * already exist, one is created and (if the activity/fragment is currently
     * started) starts the loader.  Otherwise the last created
     * loader is re-used.
     *
     * <p>In either case, the given callback is associated with the loader, and
     * 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
     * be called immediately (inside of this function), so you must be prepared
     * for this to happen.
     */
    public <D> Loader<D> initLoader(int id, Bundle args,
            LoaderManager.LoaderCallbacks<D> callback);

    /**
     * Creates a new 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.
     */
    public <D> Loader<D> restartLoader(int id, Bundle args,
            LoaderManager.LoaderCallbacks<D> callback);

    /**
     * Stops and removes the loader with the given ID.
     */
    public void stopLoader(int id);

    /**
     * Return the Loader with the given id or null if no matching Loader
     * is found.
     */
    public <D> Loader<D> getLoader(int id);
}

class LoaderManagerImpl implements LoaderManager {
    final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>();
    final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>();
    boolean mStarted;
    boolean mRetaining;
    boolean mRetainingStarted;

    final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> {
        final int mId;
        final Bundle mArgs;
@@ -67,12 +120,19 @@ public class LoaderManager {
                return;
            }

            if (mStarted) {
                // If loader already started, don't restart.
                return;
            }

            if (mLoader == null && mCallbacks != null) {
               mLoader = mCallbacks.onCreateLoader(mId, mArgs);
            }
            if (mLoader != null) {
                if (!mListenerRegistered) {
                    mLoader.registerListener(mId, this);
                    mListenerRegistered = true;
                }
                mLoader.startLoading();
                mStarted = true;
            }
@@ -144,7 +204,9 @@ public class LoaderManager {
            if (info != null) {
                Loader<Object> oldLoader = info.mLoader;
                if (oldLoader != null) {
                    if (info.mListenerRegistered) {
                        oldLoader.unregisterListener(info);
                    }
                    oldLoader.destroy();
                }
                mInactiveLoaders.remove(mId);
@@ -152,7 +214,7 @@ public class LoaderManager {
        }
    }
    
    LoaderManager(boolean started) {
    LoaderManagerImpl(boolean started) {
        mStarted = started;
    }
    
@@ -163,27 +225,14 @@ public class LoaderManager {
        Loader<Object> loader = callback.onCreateLoader(id, args);
        info.mLoader = (Loader<Object>)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, info);
            loader.startLoading();
            // 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
            info.start();
        }
        return info;
    }
    
    /**
     * Ensures a loader is initialized an active.  If the loader doesn't
     * already exist, one is created and started.  Otherwise the last created
     * loader is re-used.
     * 
     * <p>In either case, the given callback is associated with the loader, and
     * 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
     * be called immediately (inside of this function), so you must be prepared
     * for this to happen.
     */
    @SuppressWarnings("unchecked")
    public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
        LoaderInfo info = mLoaders.get(id);
@@ -203,13 +252,6 @@ public class LoaderManager {
        return (Loader<D>)info.mLoader;
    }
    
    /**
     * Create a new loader in this manager, registers the callbacks to 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
     * its work. The callback will be delivered before the old loader
     * is destroyed.
     */
    @SuppressWarnings("unchecked")
    public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
        LoaderInfo info = mLoaders.get(id);
@@ -231,26 +273,15 @@ public class LoaderManager {
        return (Loader<D>)info.mLoader;
    }
    
    /**
     * Stops and removes the loader with the given ID.
     */
    public void stopLoader(int id) {
        int idx = mLoaders.indexOfKey(id);
        if (idx >= 0) {
            LoaderInfo info = mLoaders.valueAt(idx);
            mLoaders.removeAt(idx);
            Loader<Object> loader = info.mLoader;
            if (loader != null) {
                loader.unregisterListener(info);
                loader.destroy();
            }
            info.destroy();
        }
    }

    /**
     * Return the Loader with the given id or null if no matching Loader
     * is found.
     */
    @SuppressWarnings("unchecked")
    public <D> Loader<D> getLoader(int id) {
        LoaderInfo loaderInfo = mLoaders.get(id);