CursorLoader.java revision c21b5a019c1da00b6d861cd2859e3c349a44b3a7
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                    registerContentObserver(cursor, 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    /**
97     * Registers an observer to get notifications from the content provider
98     * when the cursor needs to be refreshed.
99     */
100    void registerContentObserver(Cursor cursor, ContentObserver observer) {
101        cursor.registerContentObserver(mObserver);
102    }
103
104    /* Runs on the UI thread */
105    @Override
106    public void deliverResult(Cursor cursor) {
107        if (isReset()) {
108            // An async query came in while the loader is stopped
109            if (cursor != null) {
110                cursor.close();
111            }
112            return;
113        }
114        Cursor oldCursor = mCursor;
115        mCursor = cursor;
116
117        if (isStarted()) {
118            super.deliverResult(cursor);
119        }
120
121        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
122            oldCursor.close();
123        }
124    }
125
126    /**
127     * Creates an empty unspecified CursorLoader.  You must follow this with
128     * calls to {@link #setUri(Uri)}, {@link #setSelection(String)}, etc
129     * to specify the query to perform.
130     */
131    public CursorLoader(Context context) {
132        super(context);
133        mObserver = new ForceLoadContentObserver();
134    }
135
136    /**
137     * Creates a fully-specified CursorLoader.  See
138     * {@link ContentResolver#query(Uri, String[], String, String[], String)
139     * ContentResolver.query()} for documentation on the meaning of the
140     * parameters.  These will be passed as-is to that call.
141     */
142    public CursorLoader(Context context, Uri uri, String[] projection, String selection,
143            String[] selectionArgs, String sortOrder) {
144        super(context);
145        mObserver = new ForceLoadContentObserver();
146        mUri = uri;
147        mProjection = projection;
148        mSelection = selection;
149        mSelectionArgs = selectionArgs;
150        mSortOrder = sortOrder;
151    }
152
153    /**
154     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
155     * will be called on the UI thread. If a previous load has been completed and is still valid
156     * the result may be passed to the callbacks immediately.
157     *
158     * Must be called from the UI thread
159     */
160    @Override
161    protected void onStartLoading() {
162        if (mCursor != null) {
163            deliverResult(mCursor);
164        }
165        if (takeContentChanged() || mCursor == null) {
166            forceLoad();
167        }
168    }
169
170    /**
171     * Must be called from the UI thread
172     */
173    @Override
174    protected void onStopLoading() {
175        // Attempt to cancel the current load task if possible.
176        cancelLoad();
177    }
178
179    @Override
180    public void onCanceled(Cursor cursor) {
181        if (cursor != null && !cursor.isClosed()) {
182            cursor.close();
183        }
184    }
185
186    @Override
187    protected void onReset() {
188        super.onReset();
189
190        // Ensure the loader is stopped
191        onStopLoading();
192
193        if (mCursor != null && !mCursor.isClosed()) {
194            mCursor.close();
195        }
196        mCursor = null;
197    }
198
199    public Uri getUri() {
200        return mUri;
201    }
202
203    public void setUri(Uri uri) {
204        mUri = uri;
205    }
206
207    public String[] getProjection() {
208        return mProjection;
209    }
210
211    public void setProjection(String[] projection) {
212        mProjection = projection;
213    }
214
215    public String getSelection() {
216        return mSelection;
217    }
218
219    public void setSelection(String selection) {
220        mSelection = selection;
221    }
222
223    public String[] getSelectionArgs() {
224        return mSelectionArgs;
225    }
226
227    public void setSelectionArgs(String[] selectionArgs) {
228        mSelectionArgs = selectionArgs;
229    }
230
231    public String getSortOrder() {
232        return mSortOrder;
233    }
234
235    public void setSortOrder(String sortOrder) {
236        mSortOrder = sortOrder;
237    }
238
239    @Override
240    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
241        super.dump(prefix, fd, writer, args);
242        writer.print(prefix); writer.print("mUri="); writer.println(mUri);
243        writer.print(prefix); writer.print("mProjection=");
244                writer.println(Arrays.toString(mProjection));
245        writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
246        writer.print(prefix); writer.print("mSelectionArgs=");
247                writer.println(Arrays.toString(mSelectionArgs));
248        writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
249        writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
250        writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged);
251    }
252}
253