AsyncDbTask.java revision 0645b17e2818b69b996da48fd93731bc8a01f114
1/* 2 * Copyright (C) 2015 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.tv.util; 18 19import android.content.ContentResolver; 20import android.database.Cursor; 21import android.media.tv.TvContract; 22import android.media.tv.TvContract.Programs; 23import android.net.Uri; 24import android.os.AsyncTask; 25import android.support.annotation.MainThread; 26import android.support.annotation.Nullable; 27import android.support.annotation.WorkerThread; 28import android.util.Log; 29import android.util.Range; 30import com.android.tv.TvSingletons; 31import com.android.tv.common.BuildConfig; 32import com.android.tv.common.SoftPreconditions; 33import com.android.tv.data.Channel; 34import com.android.tv.data.Program; 35import com.android.tv.dvr.data.RecordedProgram; 36import java.util.ArrayList; 37import java.util.List; 38import java.util.concurrent.Executor; 39 40/** 41 * {@link AsyncTask} that defaults to executing on its own single threaded Executor Service. 42 * 43 * @paramthe type of the parameters sent to the task upon execution. 44 * @param the type of the progress units published during the background computation. 45 * @param the type of the result of the background computation. 46 */ 47public abstract class AsyncDbTask<Params, Progress, Result> 48 extends AsyncTask<Params, Progress, Result> { 49 private static final String TAG = "AsyncDbTask"; 50 private static final boolean DEBUG = false; 51 52 private final Executor mExecutor; 53 boolean mCalledExecuteOnDbThread; 54 55 protected AsyncDbTask(Executor mExecutor) { 56 this.mExecutor = mExecutor; 57 } 58 59 /** 60 * Returns the result of a {@link ContentResolver#query(Uri, String[], String, String[], 61 * String)}. 62 * 63 * <p>{@link #doInBackground(Void...)} executes the query on call {@link #onQuery(Cursor)} which 64 * is implemented by subclasses. 65 * 66 * @param the type of result returned by {@link #onQuery(Cursor)} 67 */ 68 public abstract static class AsyncQueryTask<Result> extends AsyncDbTask<Void, Void, Result> { 69 private final ContentResolver mContentResolver; 70 private final Uri mUri; 71 private final String[] mProjection; 72 private final String mSelection; 73 private final String[] mSelectionArgs; 74 private final String mOrderBy; 75 76 public AsyncQueryTask( 77 Executor executor, 78 ContentResolver contentResolver, 79 Uri uri, 80 String[] projection, 81 String selection, 82 String[] selectionArgs, 83 String orderBy) { 84 super(executor); 85 mContentResolver = contentResolver; 86 mUri = uri; 87 mProjection = projection; 88 mSelection = selection; 89 mSelectionArgs = selectionArgs; 90 mOrderBy = orderBy; 91 } 92 93 @Override 94 protected final Result doInBackground(Void... params) { 95 if (!mCalledExecuteOnDbThread) { 96 IllegalStateException e = 97 new IllegalStateException( 98 this 99 + " should only be executed using executeOnDbThread, " 100 + "but it was called on thread " 101 + Thread.currentThread()); 102 Log.w(TAG, e); 103 if (BuildConfig.ENG) { 104 throw e; 105 } 106 } 107 108 if (isCancelled()) { 109 // This is guaranteed to never call onPostExecute because the task is canceled. 110 return null; 111 } 112 if (DEBUG) { 113 Log.v(TAG, "Starting query for " + this); 114 } 115 try (Cursor c = 116 mContentResolver.query( 117 mUri, mProjection, mSelection, mSelectionArgs, mOrderBy)) { 118 if (c != null && !isCancelled()) { 119 Result result = onQuery(c); 120 if (DEBUG) { 121 Log.v(TAG, "Finished query for " + this); 122 } 123 return result; 124 } else { 125 if (c == null) { 126 Log.e(TAG, "Unknown query error for " + this); 127 } else { 128 if (DEBUG) { 129 Log.d(TAG, "Canceled query for " + this); 130 } 131 } 132 return null; 133 } 134 } catch (Exception e) { 135 SoftPreconditions.warn(TAG, null, e, "Error querying " + this); 136 return null; 137 } 138 } 139 140 /** 141 * Return the result from the cursor. 142 * 143 * <p><b>Note</b> This is executed on the DB thread by {@link #doInBackground(Void...)} 144 */ 145 @WorkerThread 146 protected abstract Result onQuery(Cursor c); 147 148 @Override 149 public String toString() { 150 return this.getClass().getName() + "(" + mUri + ")"; 151 } 152 } 153 154 /** 155 * Returns the result of a query as an {@link List} of {@code T}. 156 * 157 * <p>Subclasses must implement {@link #fromCursor(Cursor)}. 158 * 159 * @param <T> the type of result returned in a list by {@link #onQuery(Cursor)} 160 */ 161 public abstract static class AsyncQueryListTask<T> extends AsyncQueryTask<List<T>> { 162 private final CursorFilter mFilter; 163 164 public AsyncQueryListTask( 165 Executor executor, 166 ContentResolver contentResolver, 167 Uri uri, 168 String[] projection, 169 String selection, 170 String[] selectionArgs, 171 String orderBy) { 172 this( 173 executor, 174 contentResolver, 175 uri, 176 projection, 177 selection, 178 selectionArgs, 179 orderBy, 180 null); 181 } 182 183 public AsyncQueryListTask( 184 Executor executor, 185 ContentResolver contentResolver, 186 Uri uri, 187 String[] projection, 188 String selection, 189 String[] selectionArgs, 190 String orderBy, 191 CursorFilter filter) { 192 super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy); 193 mFilter = filter; 194 } 195 196 @Override 197 protected final List<T> onQuery(Cursor c) { 198 List<T> result = new ArrayList<>(); 199 while (c.moveToNext()) { 200 if (isCancelled()) { 201 // This is guaranteed to never call onPostExecute because the task is canceled. 202 return null; 203 } 204 if (mFilter != null && !mFilter.filter(c)) { 205 continue; 206 } 207 T t = fromCursor(c); 208 result.add(t); 209 } 210 if (DEBUG) { 211 Log.v(TAG, "Found " + result.size() + " for " + this); 212 } 213 return result; 214 } 215 216 /** 217 * Return a single instance of {@code T} from the cursor. 218 * 219 * <p><b>NOTE</b> Do not move the cursor or close it, that is handled by {@link 220 * #onQuery(Cursor)}. 221 * 222 * <p><b>Note</b> This is executed on the DB thread by {@link #onQuery(Cursor)} 223 * 224 * @param c The cursor with the values to create T from. 225 */ 226 @WorkerThread 227 protected abstract T fromCursor(Cursor c); 228 } 229 230 /** 231 * Returns the result of a query as a single instance of {@code T}. 232 * 233 * <p>Subclasses must implement {@link #fromCursor(Cursor)}. 234 */ 235 public abstract static class AsyncQueryItemTask<T> extends AsyncQueryTask<T> { 236 237 public AsyncQueryItemTask( 238 Executor executor, 239 ContentResolver contentResolver, 240 Uri uri, 241 String[] projection, 242 String selection, 243 String[] selectionArgs, 244 String orderBy) { 245 super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy); 246 } 247 248 @Override 249 protected final T onQuery(Cursor c) { 250 if (c.moveToNext()) { 251 if (isCancelled()) { 252 // This is guaranteed to never call onPostExecute because the task is canceled. 253 return null; 254 } 255 T result = fromCursor(c); 256 if (c.moveToNext()) { 257 Log.w(TAG, "More than one result for found for " + this); 258 } 259 return result; 260 } else { 261 if (DEBUG) { 262 Log.v(TAG, "No result for found for " + this); 263 } 264 return null; 265 } 266 } 267 268 /** 269 * Return a single instance of {@code T} from the cursor. 270 * 271 * <p><b>NOTE</b> Do not move the cursor or close it, that is handled by {@link 272 * #onQuery(Cursor)}. 273 * 274 * <p><b>Note</b> This is executed on the DB thread by {@link #onQuery(Cursor)} 275 * 276 * @param c The cursor with the values to create T from. 277 */ 278 @WorkerThread 279 protected abstract T fromCursor(Cursor c); 280 } 281 282 /** Gets an {@link List} of {@link Channel}s from {@link TvContract.Channels#CONTENT_URI}. */ 283 public abstract static class AsyncChannelQueryTask extends AsyncQueryListTask<Channel> { 284 285 public AsyncChannelQueryTask(Executor executor, ContentResolver contentResolver) { 286 super( 287 executor, 288 contentResolver, 289 TvContract.Channels.CONTENT_URI, 290 Channel.PROJECTION, 291 null, 292 null, 293 null); 294 } 295 296 @Override 297 protected final Channel fromCursor(Cursor c) { 298 return Channel.fromCursor(c); 299 } 300 } 301 302 /** Gets an {@link List} of {@link Program}s from {@link TvContract.Programs#CONTENT_URI}. */ 303 public abstract static class AsyncProgramQueryTask extends AsyncQueryListTask<Program> { 304 public AsyncProgramQueryTask(Executor executor, ContentResolver contentResolver) { 305 super( 306 executor, 307 contentResolver, 308 Programs.CONTENT_URI, 309 Program.PROJECTION, 310 null, 311 null, 312 null); 313 } 314 315 public AsyncProgramQueryTask( 316 Executor executor, 317 ContentResolver contentResolver, 318 Uri uri, 319 String selection, 320 String[] selectionArgs, 321 String sortOrder, 322 CursorFilter filter) { 323 super( 324 executor, 325 contentResolver, 326 uri, 327 Program.PROJECTION, 328 selection, 329 selectionArgs, 330 sortOrder, 331 filter); 332 } 333 334 @Override 335 protected final Program fromCursor(Cursor c) { 336 return Program.fromCursor(c); 337 } 338 } 339 340 /** Gets an {@link List} of {@link TvContract.RecordedPrograms}s. */ 341 public abstract static class AsyncRecordedProgramQueryTask 342 extends AsyncQueryListTask<RecordedProgram> { 343 public AsyncRecordedProgramQueryTask( 344 Executor executor, ContentResolver contentResolver, Uri uri) { 345 super(executor, contentResolver, uri, RecordedProgram.PROJECTION, null, null, null); 346 } 347 348 @Override 349 protected final RecordedProgram fromCursor(Cursor c) { 350 return RecordedProgram.fromCursor(c); 351 } 352 } 353 354 /** Execute the task on {@link TvSingletons#getDbExecutor()}. */ 355 @SafeVarargs 356 @MainThread 357 public final void executeOnDbThread(Params... params) { 358 mCalledExecuteOnDbThread = true; 359 executeOnExecutor(mExecutor, params); 360 } 361 362 /** 363 * Gets an {@link List} of {@link Program}s for a given channel and period {@link 364 * TvContract#buildProgramsUriForChannel(long, long, long)}. If the {@code period} is {@code 365 * null}, then all the programs is queried. 366 */ 367 public static class LoadProgramsForChannelTask extends AsyncProgramQueryTask { 368 protected final Range<Long> mPeriod; 369 protected final long mChannelId; 370 371 public LoadProgramsForChannelTask( 372 Executor executor, 373 ContentResolver contentResolver, 374 long channelId, 375 @Nullable Range<Long> period) { 376 super( 377 executor, 378 contentResolver, 379 period == null 380 ? TvContract.buildProgramsUriForChannel(channelId) 381 : TvContract.buildProgramsUriForChannel( 382 channelId, period.getLower(), period.getUpper()), 383 null, 384 null, 385 null, 386 null); 387 mPeriod = period; 388 mChannelId = channelId; 389 } 390 391 public long getChannelId() { 392 return mChannelId; 393 } 394 395 public final Range<Long> getPeriod() { 396 return mPeriod; 397 } 398 } 399 400 /** Gets a single {@link Program} from {@link TvContract.Programs#CONTENT_URI}. */ 401 public static class AsyncQueryProgramTask extends AsyncQueryItemTask<Program> { 402 403 public AsyncQueryProgramTask( 404 Executor executor, ContentResolver contentResolver, long programId) { 405 super( 406 executor, 407 contentResolver, 408 TvContract.buildProgramUri(programId), 409 Program.PROJECTION, 410 null, 411 null, 412 null); 413 } 414 415 @Override 416 protected Program fromCursor(Cursor c) { 417 return Program.fromCursor(c); 418 } 419 } 420 421 /** An interface which filters the row. */ 422 public interface CursorFilter extends Filter<Cursor> {} 423} 424