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 17 package android.content; 18 19 import android.database.Cursor; 20 import android.net.Uri; 21 import android.os.CancellationSignal; 22 import android.os.OperationCanceledException; 23 24 import java.io.FileDescriptor; 25 import java.io.PrintWriter; 26 import java.util.Arrays; 27 28 /** 29 * A loader that queries the {@link ContentResolver} and returns a {@link Cursor}. 30 * 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[])}. 41 */ 42 public class CursorLoader extends AsyncTaskLoader<Cursor> { 43 final ForceLoadContentObserver mObserver; 44 45 Uri mUri; 46 String[] mProjection; 47 String mSelection; 48 String[] mSelectionArgs; 49 String mSortOrder; 50 51 Cursor mCursor; 52 CancellationSignal mCancellationSignal; 53 54 /* Runs on a worker thread */ 55 @Override 56 public Cursor loadInBackground() { 57 synchronized (this) { 58 if (isLoadInBackgroundCanceled()) { 59 throw new OperationCanceledException(); 60 } 61 mCancellationSignal = new CancellationSignal(); 62 } 63 try { 64 Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection, 65 mSelectionArgs, mSortOrder, mCancellationSignal); 66 if (cursor != null) { 67 try { 68 // Ensure the cursor window is filled. 69 cursor.getCount(); 70 cursor.registerContentObserver(mObserver); 71 } catch (RuntimeException ex) { 72 cursor.close(); 73 throw ex; 74 } 75 } 76 return cursor; 77 } finally { 78 synchronized (this) { 79 mCancellationSignal = null; 80 } 81 } 82 } 83 84 @Override 85 public void cancelLoadInBackground() { 86 super.cancelLoadInBackground(); 87 88 synchronized (this) { 89 if (mCancellationSignal != null) { 90 mCancellationSignal.cancel(); 91 } 92 } 93 } 94 95 /* Runs on the UI thread */ 96 @Override 97 public void deliverResult(Cursor cursor) { 98 if (isReset()) { 99 // An async query came in while the loader is stopped 100 if (cursor != null) { 101 cursor.close(); 102 } 103 return; 104 } 105 Cursor oldCursor = mCursor; 106 mCursor = cursor; 107 108 if (isStarted()) { 109 super.deliverResult(cursor); 110 } 111 112 if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) { 113 oldCursor.close(); 114 } 115 } 116 117 /** 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 */ 133 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 147 * the result may be passed to the callbacks immediately. 148 * 149 * Must be called from the UI thread 150 */ 151 @Override 152 protected void onStartLoading() { 153 if (mCursor != null) { 154 deliverResult(mCursor); 155 } 156 if (takeContentChanged() || mCursor == null) { 157 forceLoad(); 158 } 159 } 160 161 /** 162 * Must be called from the UI thread 163 */ 164 @Override 165 protected void onStopLoading() { 166 // Attempt to cancel the current load task if possible. 167 cancelLoad(); 168 } 169 170 @Override 171 public void onCanceled(Cursor cursor) { 172 if (cursor != null && !cursor.isClosed()) { 173 cursor.close(); 174 } 175 } 176 177 @Override 178 protected void onReset() { 179 super.onReset(); 180 181 // Ensure the loader is stopped 182 onStopLoading(); 183 184 if (mCursor != null && !mCursor.isClosed()) { 185 mCursor.close(); 186 } 187 mCursor = null; 188 } 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 } 229 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 } 243 } 244