ObjectCursorLoader.java revision 60593354f1126b9687d09c3b11a852a3459f7118
1/* 2 * Copyright (C) 2013 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 com.android.mail.content; 18 19import com.android.mail.utils.LogTag; 20 21import android.content.AsyncTaskLoader; 22import android.content.Context; 23import android.database.ContentObserver; 24import android.database.Cursor; 25import android.net.Uri; 26 27import java.io.FileDescriptor; 28import java.io.PrintWriter; 29import java.util.Arrays; 30 31/** 32 * A copy of the framework's {@link android.content.CursorLoader} class. Copied because 33 * CursorLoader is not parameterized, and we want to parameterize over the underlying cursor type. 34 * @param <T> 35 */ 36public class ObjectCursorLoader<T> extends AsyncTaskLoader<ObjectCursor<T>> { 37 final ForceLoadContentObserver mObserver; 38 protected static final String LOG_TAG = LogTag.getLogTag(); 39 40 final Uri mUri; 41 final String[] mProjection; 42 // Copied over from CursorLoader, but none of our uses specify this. So these are hardcoded to 43 // null right here. 44 final String mSelection = null; 45 final String[] mSelectionArgs = null; 46 final String mSortOrder = null; 47 48 /** The underlying cursor that contains the data. */ 49 ObjectCursor<T> mCursor; 50 51 /** The factory that knows how to create T objects from cursors: one object per row. */ 52 private final CursorCreator<T> mFactory; 53 54 public ObjectCursorLoader(Context context, Uri uri, String[] projection, 55 CursorCreator<T> factory) { 56 super(context); 57 58 /* 59 * If these are null, it's going to crash anyway in loadInBackground(), but this stack trace 60 * is much more useful. 61 */ 62 if (uri == null) { 63 throw new NullPointerException("The uri cannot be null"); 64 } 65 if (factory == null) { 66 throw new NullPointerException("The factory cannot be null"); 67 } 68 69 mObserver = new ForceLoadContentObserver(); 70 mUri = uri; 71 mProjection = projection; 72 mFactory = factory; 73 } 74 75 /* Runs on a worker thread */ 76 @Override 77 public ObjectCursor<T> loadInBackground() { 78 final Cursor inner = getContext().getContentResolver().query(mUri, mProjection, 79 mSelection, mSelectionArgs, mSortOrder); 80 if (inner != null) { 81 // Ensure the cursor window is filled 82 inner.getCount(); 83 registerContentObserver(inner, mObserver); 84 } 85 // Modifications to the ObjectCursor, create an Object Cursor and fill the cache. 86 final ObjectCursor<T> cursor = new ObjectCursor<T>(inner, mFactory); 87 cursor.fillCache(); 88 return cursor; 89 } 90 91 /** 92 * Registers an observer to get notifications from the content provider 93 * when the cursor needs to be refreshed. 94 */ 95 void registerContentObserver(Cursor cursor, ContentObserver observer) { 96 cursor.registerContentObserver(mObserver); 97 } 98 99 /* Runs on the UI thread */ 100 @Override 101 public void deliverResult(ObjectCursor<T> cursor) { 102 if (isReset()) { 103 // An async query came in while the loader is stopped 104 if (cursor != null) { 105 cursor.close(); 106 } 107 return; 108 } 109 final Cursor oldCursor = mCursor; 110 mCursor = cursor; 111 112 if (isStarted()) { 113 super.deliverResult(cursor); 114 } 115 116 if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) { 117 oldCursor.close(); 118 } 119 } 120 121 /** 122 * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks 123 * will be called on the UI thread. If a previous load has been completed and is still valid 124 * the result may be passed to the callbacks immediately. 125 * 126 * Must be called from the UI thread 127 */ 128 @Override 129 protected void onStartLoading() { 130 if (mCursor != null) { 131 deliverResult(mCursor); 132 } 133 if (takeContentChanged() || mCursor == null) { 134 forceLoad(); 135 } 136 } 137 138 /** 139 * Must be called from the UI thread 140 */ 141 @Override 142 protected void onStopLoading() { 143 // Attempt to cancel the current load task if possible. 144 cancelLoad(); 145 } 146 147 @Override 148 public boolean cancelLoad() { 149 if (mCursor != null && !mCursor.isClosed()) { 150 mCursor.close(); 151 } 152 return super.cancelLoad(); 153 } 154 155 @Override 156 protected void onReset() { 157 super.onReset(); 158 159 // Ensure the loader is stopped 160 onStopLoading(); 161 162 if (mCursor != null && !mCursor.isClosed()) { 163 mCursor.close(); 164 } 165 mCursor = null; 166 } 167 168 @Override 169 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 170 super.dump(prefix, fd, writer, args); 171 writer.print(prefix); writer.print("mUri="); writer.println(mUri); 172 writer.print(prefix); writer.print("mProjection="); 173 writer.println(Arrays.toString(mProjection)); 174 writer.print(prefix); writer.print("mSelection="); writer.println(mSelection); 175 writer.print(prefix); writer.print("mSelectionArgs="); 176 writer.println(Arrays.toString(mSelectionArgs)); 177 writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder); 178 writer.print(prefix); writer.print("mCursor="); writer.println(mCursor); 179 } 180} 181