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