CursorLoader.java revision da2223f8975cab9048d554eb5229cc5ff5a1e6c1
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content;
18
19import android.database.ContentObserver;
20import android.database.Cursor;
21import android.net.Uri;
22import android.os.CancellationSignal;
23import android.os.OperationCanceledException;
24
25import java.io.FileDescriptor;
26import java.io.PrintWriter;
27import java.util.Arrays;
28
29/**
30 * A loader that queries the {@link ContentResolver} and returns a {@link Cursor}.
31 * This class implements the {@link Loader} protocol in a standard way for
32 * querying cursors, building on {@link AsyncTaskLoader} to perform the cursor
33 * query on a background thread so that it does not block the application's UI.
34 *
35 * <p>A CursorLoader must be built with the full information for the query to
36 * perform, either through the
37 * {@link #CursorLoader(Context, Uri, String[], String, String[], String)} or
38 * creating an empty instance with {@link #CursorLoader(Context)} and filling
39 * in the desired paramters with {@link #setUri(Uri)}, {@link #setSelection(String)},
40 * {@link #setSelectionArgs(String[])}, {@link #setSortOrder(String)},
41 * and {@link #setProjection(String[])}.
42 */
43public class CursorLoader extends AsyncTaskLoader<Cursor> {
44    final ForceLoadContentObserver mObserver;
45
46    Uri mUri;
47    String[] mProjection;
48    String mSelection;
49    String[] mSelectionArgs;
50    String mSortOrder;
51
52    Cursor mCursor;
53    CancellationSignal mCancellationSignal;
54
55    /* Runs on a worker thread */
56    @Override
57    public Cursor loadInBackground() {
58        synchronized (this) {
59            if (isLoadInBackgroundCanceled()) {
60                throw new OperationCanceledException();
61            }
62            mCancellationSignal = new CancellationSignal();
63        }
64        try {
65            Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
66                    mSelectionArgs, mSortOrder, mCancellationSignal);
67            if (cursor != null) {
68                try {
69                    // Ensure the cursor window is filled.
70                    cursor.getCount();
71                    cursor.registerContentObserver(mObserver);
72                } catch (RuntimeException ex) {
73                    cursor.close();
74                    throw ex;
75                }
76            }
77            return cursor;
78        } finally {
79            synchronized (this) {
80                mCancellationSignal = null;
81            }
82        }
83    }
84
85    @Override
86    public void cancelLoadInBackground() {
87        super.cancelLoadInBackground();
88
89        synchronized (this) {
90            if (mCancellationSignal != null) {
91                mCancellationSignal.cancel();
92            }
93        }
94    }
95
96    /* Runs on the UI thread */
97    @Override
98    public void deliverResult(Cursor cursor) {
99        if (isReset()) {
100            // An async query came in while the loader is stopped
101            if (cursor != null) {
102                cursor.close();
103            }
104            return;
105        }
106        Cursor oldCursor = mCursor;
107        mCursor = cursor;
108
109        if (isStarted()) {
110            super.deliverResult(cursor);
111        }
112
113        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
114            oldCursor.close();
115        }
116    }
117
118    /**
119     * Creates an empty unspecified CursorLoader.  You must follow this with
120     * calls to {@link #setUri(Uri)}, {@link #setSelection(String)}, etc
121     * to specify the query to perform.
122     */
123    public CursorLoader(Context context) {
124        super(context);
125        mObserver = new ForceLoadContentObserver();
126    }
127
128    /**
129     * Creates a fully-specified CursorLoader.  See
130     * {@link ContentResolver#query(Uri, String[], String, String[], String)
131     * ContentResolver.query()} for documentation on the meaning of the
132     * parameters.  These will be passed as-is to that call.
133     */
134    public CursorLoader(Context context, Uri uri, String[] projection, String selection,
135            String[] selectionArgs, String sortOrder) {
136        super(context);
137        mObserver = new ForceLoadContentObserver();
138        mUri = uri;
139        mProjection = projection;
140        mSelection = selection;
141        mSelectionArgs = selectionArgs;
142        mSortOrder = sortOrder;
143    }
144
145    /**
146     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
147     * will be called on the UI thread. If a previous load has been completed and is still valid
148     * the result may be passed to the callbacks immediately.
149     *
150     * Must be called from the UI thread
151     */
152    @Override
153    protected void onStartLoading() {
154        if (mCursor != null) {
155            deliverResult(mCursor);
156        }
157        if (takeContentChanged() || mCursor == null) {
158            forceLoad();
159        }
160    }
161
162    /**
163     * Must be called from the UI thread
164     */
165    @Override
166    protected void onStopLoading() {
167        // Attempt to cancel the current load task if possible.
168        cancelLoad();
169    }
170
171    @Override
172    public void onCanceled(Cursor cursor) {
173        if (cursor != null && !cursor.isClosed()) {
174            cursor.close();
175        }
176    }
177
178    @Override
179    protected void onReset() {
180        super.onReset();
181
182        // Ensure the loader is stopped
183        onStopLoading();
184
185        if (mCursor != null && !mCursor.isClosed()) {
186            mCursor.close();
187        }
188        mCursor = null;
189    }
190
191    public Uri getUri() {
192        return mUri;
193    }
194
195    public void setUri(Uri uri) {
196        mUri = uri;
197    }
198
199    public String[] getProjection() {
200        return mProjection;
201    }
202
203    public void setProjection(String[] projection) {
204        mProjection = projection;
205    }
206
207    public String getSelection() {
208        return mSelection;
209    }
210
211    public void setSelection(String selection) {
212        mSelection = selection;
213    }
214
215    public String[] getSelectionArgs() {
216        return mSelectionArgs;
217    }
218
219    public void setSelectionArgs(String[] selectionArgs) {
220        mSelectionArgs = selectionArgs;
221    }
222
223    public String getSortOrder() {
224        return mSortOrder;
225    }
226
227    public void setSortOrder(String sortOrder) {
228        mSortOrder = sortOrder;
229    }
230
231    @Override
232    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
233        super.dump(prefix, fd, writer, args);
234        writer.print(prefix); writer.print("mUri="); writer.println(mUri);
235        writer.print(prefix); writer.print("mProjection=");
236                writer.println(Arrays.toString(mProjection));
237        writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
238        writer.print(prefix); writer.print("mSelectionArgs=");
239                writer.println(Arrays.toString(mSelectionArgs));
240        writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
241        writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
242        writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged);
243    }
244}
245