blob: c78871c30a80e0f48340bebe927a0b4fbca7e05c [file] [log] [blame]
Jeff Hamilton9911b7f2010-05-15 02:20:31 -05001/*
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.Cursor;
20import android.net.Uri;
Jeff Browna7771df2012-05-07 20:06:46 -070021import android.os.CancellationSignal;
22import android.os.OperationCanceledException;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050023
Dianne Hackborna2ea7472010-12-20 12:10:01 -080024import java.io.FileDescriptor;
25import java.io.PrintWriter;
26import java.util.Arrays;
27
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050028/**
29 * A loader that queries the {@link ContentResolver} and returns a {@link Cursor}.
Dianne Hackborn327fbd22011-01-17 14:38:50 -080030 * This class implements the {@link Loader} protocol in a standard way for
31 * querying cursors, building on {@link AsyncTaskLoader} to perform the cursor
32 * query on a background thread so that it does not block the application's UI.
33 *
34 * <p>A CursorLoader must be built with the full information for the query to
35 * perform, either through the
36 * {@link #CursorLoader(Context, Uri, String[], String, String[], String)} or
37 * creating an empty instance with {@link #CursorLoader(Context)} and filling
38 * in the desired paramters with {@link #setUri(Uri)}, {@link #setSelection(String)},
39 * {@link #setSelectionArgs(String[])}, {@link #setSortOrder(String)},
40 * and {@link #setProjection(String[])}.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050041 */
42public class CursorLoader extends AsyncTaskLoader<Cursor> {
Dianne Hackborna2ea7472010-12-20 12:10:01 -080043 final ForceLoadContentObserver mObserver;
44
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050045 Uri mUri;
46 String[] mProjection;
47 String mSelection;
48 String[] mSelectionArgs;
49 String mSortOrder;
50
Dianne Hackborna2ea7472010-12-20 12:10:01 -080051 Cursor mCursor;
Jeff Brown4c1241d2012-02-02 17:05:00 -080052 CancellationSignal mCancellationSignal;
Dianne Hackborna2ea7472010-12-20 12:10:01 -080053
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050054 /* Runs on a worker thread */
55 @Override
56 public Cursor loadInBackground() {
Jeff Brown75ea64f2012-01-25 19:37:13 -080057 synchronized (this) {
58 if (isLoadInBackgroundCanceled()) {
59 throw new OperationCanceledException();
60 }
Jeff Brown4c1241d2012-02-02 17:05:00 -080061 mCancellationSignal = new CancellationSignal();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050062 }
Jeff Brown75ea64f2012-01-25 19:37:13 -080063 try {
64 Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
Jeff Brown4c1241d2012-02-02 17:05:00 -080065 mSelectionArgs, mSortOrder, mCancellationSignal);
Jeff Brown75ea64f2012-01-25 19:37:13 -080066 if (cursor != null) {
Jeff Brownc21b5a02013-01-07 17:15:12 -080067 try {
68 // Ensure the cursor window is filled.
69 cursor.getCount();
Scott Kennedyda2223f2013-03-13 14:40:11 -070070 cursor.registerContentObserver(mObserver);
Jeff Brownc21b5a02013-01-07 17:15:12 -080071 } catch (RuntimeException ex) {
72 cursor.close();
73 throw ex;
74 }
Jeff Brown75ea64f2012-01-25 19:37:13 -080075 }
76 return cursor;
77 } finally {
78 synchronized (this) {
Jeff Brown4c1241d2012-02-02 17:05:00 -080079 mCancellationSignal = null;
Jeff Brown75ea64f2012-01-25 19:37:13 -080080 }
81 }
82 }
83
84 @Override
Jeff Brownb19a71a2012-01-31 11:48:39 -080085 public void cancelLoadInBackground() {
86 super.cancelLoadInBackground();
Jeff Brown75ea64f2012-01-25 19:37:13 -080087
88 synchronized (this) {
Jeff Brown4c1241d2012-02-02 17:05:00 -080089 if (mCancellationSignal != null) {
90 mCancellationSignal.cancel();
Jeff Brown75ea64f2012-01-25 19:37:13 -080091 }
92 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050093 }
94
95 /* Runs on the UI thread */
96 @Override
97 public void deliverResult(Cursor cursor) {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -080098 if (isReset()) {
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050099 // An async query came in while the loader is stopped
Dmitri Plotnikov4565d522010-07-15 18:24:07 -0700100 if (cursor != null) {
Dianne Hackborn229edbc2011-10-09 16:01:40 -0700101 cursor.close();
Dmitri Plotnikov4565d522010-07-15 18:24:07 -0700102 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500103 return;
104 }
Ben Komalo8e6f69b2010-07-22 16:21:22 -0700105 Cursor oldCursor = mCursor;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500106 mCursor = cursor;
Ben Komalo8e6f69b2010-07-22 16:21:22 -0700107
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800108 if (isStarted()) {
Dianne Hackbornc9189352010-12-15 14:57:25 -0800109 super.deliverResult(cursor);
110 }
111
112 if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
Dianne Hackborn229edbc2011-10-09 16:01:40 -0700113 oldCursor.close();
Ben Komalo8e6f69b2010-07-22 16:21:22 -0700114 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500115 }
116
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800117 /**
118 * Creates an empty unspecified CursorLoader. You must follow this with
119 * calls to {@link #setUri(Uri)}, {@link #setSelection(String)}, etc
120 * to specify the query to perform.
121 */
122 public CursorLoader(Context context) {
123 super(context);
124 mObserver = new ForceLoadContentObserver();
125 }
126
127 /**
128 * Creates a fully-specified CursorLoader. See
129 * {@link ContentResolver#query(Uri, String[], String, String[], String)
130 * ContentResolver.query()} for documentation on the meaning of the
131 * parameters. These will be passed as-is to that call.
132 */
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500133 public CursorLoader(Context context, Uri uri, String[] projection, String selection,
134 String[] selectionArgs, String sortOrder) {
135 super(context);
136 mObserver = new ForceLoadContentObserver();
137 mUri = uri;
138 mProjection = projection;
139 mSelection = selection;
140 mSelectionArgs = selectionArgs;
141 mSortOrder = sortOrder;
142 }
143
144 /**
145 * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
146 * will be called on the UI thread. If a previous load has been completed and is still valid
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700147 * the result may be passed to the callbacks immediately.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500148 *
149 * Must be called from the UI thread
150 */
151 @Override
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800152 protected void onStartLoading() {
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500153 if (mCursor != null) {
154 deliverResult(mCursor);
Dianne Hackbornf73c75c2010-12-17 16:54:05 -0800155 }
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800156 if (takeContentChanged() || mCursor == null) {
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500157 forceLoad();
158 }
159 }
160
161 /**
162 * Must be called from the UI thread
163 */
164 @Override
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800165 protected void onStopLoading() {
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500166 // Attempt to cancel the current load task if possible.
167 cancelLoad();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500168 }
169
170 @Override
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800171 public void onCanceled(Cursor cursor) {
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700172 if (cursor != null && !cursor.isClosed()) {
Dianne Hackborn229edbc2011-10-09 16:01:40 -0700173 cursor.close();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700174 }
175 }
176
177 @Override
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800178 protected void onReset() {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800179 super.onReset();
180
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500181 // Ensure the loader is stopped
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800182 onStopLoading();
Dianne Hackbornc9189352010-12-15 14:57:25 -0800183
184 if (mCursor != null && !mCursor.isClosed()) {
Dianne Hackborn229edbc2011-10-09 16:01:40 -0700185 mCursor.close();
Dianne Hackbornc9189352010-12-15 14:57:25 -0800186 }
187 mCursor = null;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500188 }
189
190 public Uri getUri() {
191 return mUri;
192 }
193
194 public void setUri(Uri uri) {
195 mUri = uri;
196 }
197
198 public String[] getProjection() {
199 return mProjection;
200 }
201
202 public void setProjection(String[] projection) {
203 mProjection = projection;
204 }
205
206 public String getSelection() {
207 return mSelection;
208 }
209
210 public void setSelection(String selection) {
211 mSelection = selection;
212 }
213
214 public String[] getSelectionArgs() {
215 return mSelectionArgs;
216 }
217
218 public void setSelectionArgs(String[] selectionArgs) {
219 mSelectionArgs = selectionArgs;
220 }
221
222 public String getSortOrder() {
223 return mSortOrder;
224 }
225
226 public void setSortOrder(String sortOrder) {
227 mSortOrder = sortOrder;
228 }
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800229
230 @Override
231 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
232 super.dump(prefix, fd, writer, args);
233 writer.print(prefix); writer.print("mUri="); writer.println(mUri);
234 writer.print(prefix); writer.print("mProjection=");
235 writer.println(Arrays.toString(mProjection));
236 writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
237 writer.print(prefix); writer.print("mSelectionArgs=");
238 writer.println(Arrays.toString(mSelectionArgs));
239 writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
240 writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
241 writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged);
242 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500243}