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

Commit 9911b7f8 authored by Jeff Hamilton's avatar Jeff Hamilton
Browse files

Add the Loader and supporting classes.

Loaders are designed to make it easier to manage
asynchronously loading data.

Change-Id: I948db08c721411e94fca071dc6fb4db2b83ea4d6
parent 3c19d990
Loading
Loading
Loading
Loading
+542 −0
Original line number Diff line number Diff line
@@ -19421,6 +19421,21 @@
<parameter name="options" type="int">
</parameter>
</method>
<method name="setDisplayOptions"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="options" type="int">
</parameter>
<parameter name="mask" type="int">
</parameter>
</method>
<method name="setDividerDrawable"
 return="void"
 abstract="true"
@@ -27547,6 +27562,109 @@
</parameter>
</method>
</class>
<class name="LoaderManagingFragment"
 extends="android.app.Fragment"
 abstract="true"
 static="false"
 final="false"
 deprecated="not 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"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 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="protected"
>
<parameter name="id" type="int">
</parameter>
<parameter name="args" type="android.os.Bundle">
</parameter>
</method>
</class>
<class name="LocalActivityManager"
 extends="java.lang.Object"
 abstract="false"
@@ -35277,6 +35395,58 @@
</parameter>
</constructor>
</class>
<class name="AsyncTaskLoader"
 extends="android.content.Loader"
 abstract="true"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<constructor name="AsyncTaskLoader"
 type="android.content.AsyncTaskLoader"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="context" type="android.content.Context">
</parameter>
</constructor>
<method name="cancelLoad"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="forceLoad"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="loadInBackground"
 return="D"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
</class>
<class name="BroadcastReceiver"
 extends="java.lang.Object"
 abstract="true"
@@ -40673,6 +40843,212 @@
</parameter>
</method>
</class>
<class name="CursorLoader"
 extends="android.content.AsyncTaskLoader"
 abstract="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<constructor name="CursorLoader"
 type="android.content.CursorLoader"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="context" type="android.content.Context">
</parameter>
<parameter name="uri" type="android.net.Uri">
</parameter>
<parameter name="projection" type="java.lang.String[]">
</parameter>
<parameter name="selection" type="java.lang.String">
</parameter>
<parameter name="selectionArgs" type="java.lang.String[]">
</parameter>
<parameter name="sortOrder" type="java.lang.String">
</parameter>
</constructor>
<method name="deliverResult"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<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"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getSelection"
 return="java.lang.String"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getSelectionArgs"
 return="java.lang.String[]"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getSortOrder"
 return="java.lang.String"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getUri"
 return="android.net.Uri"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="loadInBackground"
 return="android.database.Cursor"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="setProjection"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="projection" type="java.lang.String[]">
</parameter>
</method>
<method name="setSelection"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="selection" type="java.lang.String">
</parameter>
</method>
<method name="setSelectionArgs"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="selectionArgs" type="java.lang.String[]">
</parameter>
</method>
<method name="setSortOrder"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="sortOrder" type="java.lang.String">
</parameter>
</method>
<method name="setUri"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="uri" type="android.net.Uri">
</parameter>
</method>
<method name="startLoading"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="stopLoading"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
</class>
<interface name="DialogInterface"
 abstract="true"
 static="false"
@@ -45639,6 +46015,172 @@
</parameter>
</constructor>
</class>
<class name="Loader"
 extends="java.lang.Object"
 abstract="true"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<constructor name="Loader"
 type="android.content.Loader"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="context" type="android.content.Context">
</parameter>
</constructor>
<method name="deliverResult"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="data" type="D">
</parameter>
</method>
<method name="destroy"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="forceLoad"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getContext"
 return="android.content.Context"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getId"
 return="int"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="registerListener"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="id" type="int">
</parameter>
<parameter name="listener" type="android.content.Loader.OnLoadCompleteListener&lt;D&gt;">
</parameter>
</method>
<method name="startLoading"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="stopLoading"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="unregisterListener"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="listener" type="android.content.Loader.OnLoadCompleteListener&lt;D&gt;">
</parameter>
</method>
</class>
<class name="Loader.ForceLoadContentObserver"
 extends="android.database.ContentObserver"
 abstract="false"
 static="false"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
<constructor name="Loader.ForceLoadContentObserver"
 type="android.content.Loader.ForceLoadContentObserver"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</constructor>
</class>
<interface name="Loader.OnLoadCompleteListener"
 abstract="true"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<method name="onLoadComplete"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="loader" type="android.content.Loader&lt;D&gt;">
</parameter>
<parameter name="data" type="D">
</parameter>
</method>
</interface>
<class name="MutableContextWrapper"
 extends="android.content.ContextWrapper"
 abstract="false"
+188 −0
Original line number Diff line number Diff line
/*
 * 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.
 */
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.
     */
    protected 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();
            }
        }
    }

    /** 
     * @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;
    }
}
+86 −0
Original line number Diff line number Diff line
/*
 * 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.content;

import android.os.AsyncTask;

/**
 * Abstract Loader that provides an {@link AsyncTask} to do the work.
 * 
 * @param <D> the data type to be loaded.
 */
public abstract class AsyncTaskLoader<D> extends Loader<D> {
    final class LoadTask extends AsyncTask<Void, Void, D> {
        /* Runs on a worker thread */
        @Override
        protected D doInBackground(Void... params) {
            return AsyncTaskLoader.this.loadInBackground();
        }

        /* Runs on the UI thread */
        @Override
        protected void onPostExecute(D data) {
            AsyncTaskLoader.this.dispatchOnLoadComplete(data);
        }
    }

    LoadTask mTask;

    public AsyncTaskLoader(Context context) {
        super(context);
    }

    /**
     * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
     * loaded data set and load a new one.
     */
    @Override
    public void forceLoad() {
        mTask = new LoadTask();
        mTask.execute((Void[]) null);
    }

    /**
     * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
     * for more info.
     *
     * @return <tt>false</tt> if the task could not be canceled,
     *         typically because it has already completed normally, or
     *         because {@link #startLoading()} hasn't been called, and
     *         <tt>true</tt> otherwise
     */
    public boolean cancelLoad() {
        if (mTask != null) {
            return mTask.cancel(false);
        }
        return false;
    }
    
    void dispatchOnLoadComplete(D data) {
        mTask = null;
        deliverResult(data);
    }

    /**
     * Called on a worker thread to perform the actual load. Implementations should not deliver the
     * results directly, but should return them from this method and deliver them from
     * {@link #onPostExecute()}
     *
     * @return the result of the load
     */
    public abstract D loadInBackground();
}
+151 −0
Original line number Diff line number Diff line
/*
 * 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.content;

import android.database.Cursor;
import android.net.Uri;

/**
 * A loader that queries the {@link ContentResolver} and returns a {@link Cursor}.
 */
public class CursorLoader extends AsyncTaskLoader<Cursor> {
    Cursor mCursor;
    ForceLoadContentObserver mObserver;
    boolean mStopped;
    Uri mUri;
    String[] mProjection;
    String mSelection;
    String[] mSelectionArgs;
    String mSortOrder;

    /* Runs on a worker thread */
    @Override
    public Cursor loadInBackground() {
        Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
                mSelectionArgs, mSortOrder);
        // Ensure the cursor window is filled
        if (cursor != null) {
            cursor.getCount();
            cursor.registerContentObserver(mObserver);
        }
        return cursor;
    }

    /* Runs on the UI thread */
    @Override
    public void deliverResult(Cursor cursor) {
        if (mStopped) {
            // An async query came in while the loader is stopped
            cursor.close();
            return;
        }
        mCursor = cursor;
        super.deliverResult(cursor);
    }

    public CursorLoader(Context context, Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        super(context);
        mObserver = new ForceLoadContentObserver();
        mUri = uri;
        mProjection = projection;
        mSelection = selection;
        mSelectionArgs = selectionArgs;
        mSortOrder = sortOrder;
    }

    /**
     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
     * will be called on the UI thread. If a previous load has been completed and is still valid
     * the result may be passed to the callbacks immediately. 
     *
     * Must be called from the UI thread
     */
    @Override
    public void startLoading() {
        mStopped = false;

        if (mCursor != null) {
            deliverResult(mCursor);
        } else {
            forceLoad();
        }
    }

    /**
     * Must be called from the UI thread
     */
    @Override
    public void stopLoading() {
        if (mCursor != null && !mCursor.isClosed()) {
            mCursor.close();
            mCursor = null;
        }

        // Attempt to cancel the current load task if possible.
        cancelLoad();

        // Make sure that any outstanding loads clean themselves up properly
        mStopped = true;
    }

    @Override
    public void destroy() {
        // Ensure the loader is stopped
        stopLoading();
    }

    public Uri getUri() {
        return mUri;
    }

    public void setUri(Uri uri) {
        mUri = uri;
    }

    public String[] getProjection() {
        return mProjection;
    }

    public void setProjection(String[] projection) {
        mProjection = projection;
    }

    public String getSelection() {
        return mSelection;
    }

    public void setSelection(String selection) {
        mSelection = selection;
    }

    public String[] getSelectionArgs() {
        return mSelectionArgs;
    }

    public void setSelectionArgs(String[] selectionArgs) {
        mSelectionArgs = selectionArgs;
    }

    public String getSortOrder() {
        return mSortOrder;
    }

    public void setSortOrder(String sortOrder) {
        mSortOrder = sortOrder;
    }
}
+154 −0

File added.

Preview size limit exceeded, changes collapsed.