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