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