Bug 1048626 - Provide a minimal background thread pool for long-running tasks, r=rnewman

This commit is contained in:
Mark Capella 2014-08-11 22:50:47 -04:00
parent 9619f8a3b1
commit 15a17d0b15
2 changed files with 75 additions and 35 deletions

View File

@ -31,6 +31,8 @@ import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Favicons {
private static final String LOGTAG = "GeckoFavicons";
@ -60,6 +62,9 @@ public class Favicons {
// The density-adjusted maximum Favicon dimensions.
public static int largestFaviconSize;
// Executor for long-running Favicon Tasks.
public static final ExecutorService longRunningExecutor = Executors.newSingleThreadExecutor();
private static final SparseArray<LoadFaviconTask> loadTasks = new SparseArray<LoadFaviconTask>();
// Cache to hold mappings between page URLs and Favicon URLs. Used to avoid going to the DB when
@ -194,10 +199,11 @@ public class Favicons {
* @param callback Callback to fire with the result.
* @return The job ID of the spawned async task, if any.
*/
public static int getSizedFaviconForPageFromLocal(final String pageURL, final int targetSize, final OnFaviconLoadedListener callback) {
public static int getSizedFaviconForPageFromLocal(final String pageURL, final int targetSize,
final OnFaviconLoadedListener callback) {
// Firstly, try extremely hard to cheat.
// Have we cached this favicon URL? If we did, we can consult the memcache right away.
String targetURL = pageURLMappings.get(pageURL);
final String targetURL = pageURLMappings.get(pageURL);
if (targetURL != null) {
// Check if favicon has failed.
if (faviconsCache.isFailedFavicon(targetURL)) {
@ -205,7 +211,7 @@ public class Favicons {
}
// Do we have a Favicon in the cache for this favicon URL?
Bitmap result = getSizedFaviconFromCache(targetURL, targetSize);
final Bitmap result = getSizedFaviconFromCache(targetURL, targetSize);
if (result != null) {
// Victory - immediate response!
return dispatchResult(pageURL, targetURL, result, callback);
@ -213,12 +219,14 @@ public class Favicons {
}
// No joy using in-memory resources. Go to background thread and ask the database.
LoadFaviconTask task = new LoadFaviconTask(ThreadUtils.getBackgroundHandler(), pageURL, targetURL, 0, callback, targetSize, true);
int taskId = task.getId();
final LoadFaviconTask task =
new LoadFaviconTask(pageURL, targetURL, 0, callback, targetSize, true);
final int taskId = task.getId();
synchronized(loadTasks) {
loadTasks.put(taskId, task);
}
task.execute();
return taskId;
}
@ -259,8 +267,8 @@ public class Favicons {
* Contains logic to prevent the repeated loading of Favicons which have previously failed.
* There is no support for recovery from transient failures.
*
* @param pageUrl URL of the page for which to load a Favicon. If null, no job is created.
* @param faviconUrl The URL of the Favicon to load. If null, an attempt to infer the value from
* @param pageURL URL of the page for which to load a Favicon. If null, no job is created.
* @param faviconURL The URL of the Favicon to load. If null, an attempt to infer the value from
* the history database will be made, and ultimately an attempt to guess will
* be made.
* @param flags Flags to be used by the LoadFaviconTask while loading. Currently only one flag
@ -272,20 +280,20 @@ public class Favicons {
* @param listener The OnFaviconLoadedListener to invoke with the result of this Favicon load.
* @return The id of the LoadFaviconTask handling this job.
*/
private static int loadUncachedFavicon(String pageUrl, String faviconUrl, int flags, int targetSize, OnFaviconLoadedListener listener) {
private static int loadUncachedFavicon(String pageURL, String faviconURL, int flags,
int targetSize, OnFaviconLoadedListener listener) {
// Handle the case where we have no page url.
if (TextUtils.isEmpty(pageUrl)) {
if (TextUtils.isEmpty(pageURL)) {
dispatchResult(null, null, null, listener);
return NOT_LOADING;
}
LoadFaviconTask task = new LoadFaviconTask(ThreadUtils.getBackgroundHandler(), pageUrl, faviconUrl, flags, listener, targetSize, false);
int taskId = task.getId();
final LoadFaviconTask task =
new LoadFaviconTask(pageURL, faviconURL, flags, listener, targetSize, false);
final int taskId = task.getId();
synchronized(loadTasks) {
loadTasks.put(taskId, task);
}
task.execute();
return taskId;
@ -325,23 +333,23 @@ public class Favicons {
return false;
}
boolean cancelled;
synchronized (loadTasks) {
if (loadTasks.indexOfKey(taskId) < 0) {
return false;
}
Log.v(LOGTAG, "Cancelling favicon load " + taskId + ".");
LoadFaviconTask task = loadTasks.get(taskId);
cancelled = task.cancel(false);
return task.cancel();
}
return cancelled;
}
public static void close() {
Log.d(LOGTAG, "Closing Favicons database");
// Close the Executor to new tasks.
longRunningExecutor.shutdown();
// Cancel any pending tasks
synchronized (loadTasks) {
final int count = loadTasks.size();

View File

@ -8,7 +8,6 @@ package org.mozilla.gecko.favicons;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.net.http.AndroidHttpClient;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import org.apache.http.Header;
@ -31,6 +30,7 @@ import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
/**
@ -39,7 +39,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* The implementation initially tries to get the Favicon from the database. Upon failure, the icon
* is loaded from the internet.
*/
public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
public class LoadFaviconTask {
private static final String LOGTAG = "LoadFaviconTask";
// Access to this map needs to be synchronized prevent multiple jobs loading the same favicon
@ -61,6 +61,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
private int flags;
private final boolean onlyFromLocal;
/* inner-access */ volatile boolean mCancelled;
// Assuming square favicons, judging by width only is acceptable.
protected int targetWidth;
@ -69,20 +70,16 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
static AndroidHttpClient httpClient = AndroidHttpClient.newInstance(GeckoAppShell.getGeckoInterface().getDefaultUAString());
public LoadFaviconTask(Handler backgroundThreadHandler,
String pageUrl, String faviconUrl, int flags,
OnFaviconLoadedListener listener) {
this(backgroundThreadHandler, pageUrl, faviconUrl, flags, listener, -1, false);
public LoadFaviconTask(String pageURL, String faviconURL, int flags, OnFaviconLoadedListener listener) {
this(pageURL, faviconURL, flags, listener, -1, false);
}
public LoadFaviconTask(Handler backgroundThreadHandler,
String pageUrl, String faviconUrl, int flags,
OnFaviconLoadedListener listener, int targetWidth, boolean onlyFromLocal) {
super(backgroundThreadHandler);
public LoadFaviconTask(String pageURL, String faviconURL, int flags, OnFaviconLoadedListener listener,
int targetWidth, boolean onlyFromLocal) {
id = nextFaviconLoadId.incrementAndGet();
this.pageUrl = pageUrl;
this.faviconURL = faviconUrl;
this.pageUrl = pageURL;
this.faviconURL = faviconURL;
this.listener = listener;
this.flags = flags;
this.targetWidth = targetWidth;
@ -297,8 +294,45 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
return FaviconDecoder.decodeFavicon(buffer, 0, bPointer + 1);
}
@Override
protected Bitmap doInBackground(Void... unused) {
// LoadFavicon tasks are performed on a unique background executor thread
// to avoid network blocking.
public final void execute() {
ThreadUtils.assertOnUiThread();
try {
Favicons.longRunningExecutor.execute(new Runnable() {
@Override
public void run() {
final Bitmap result = doInBackground();
ThreadUtils.getUiHandler().post(new Runnable() {
@Override
public void run() {
if (mCancelled) {
onCancelled();
} else {
onPostExecute(result);
}
}
});
}
});
} catch (RejectedExecutionException e) {
// If our executor is unavailable.
onCancelled();
}
}
public final boolean cancel() {
mCancelled = true;
return true;
}
public final boolean isCancelled() {
return mCancelled;
}
/* inner-access */ Bitmap doInBackground() {
if (isCancelled()) {
return null;
}
@ -463,8 +497,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
image.getHeight() > 0;
}
@Override
protected void onPostExecute(Bitmap image) {
/* inner-access */ void onPostExecute(Bitmap image) {
if (isChaining) {
return;
}
@ -508,8 +541,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
Favicons.dispatchResult(pageUrl, faviconURL, scaled, listener);
}
@Override
protected void onCancelled() {
/* inner-access */ void onCancelled() {
Favicons.removeLoadTask(id);
synchronized(loadsInFlight) {