Bug 1057086: Improve type safety in UIAsyncTask r=nalexander

--HG--
rename : mobile/android/base/util/UiAsyncTask.java => mobile/android/base/util/UIAsyncTask.java
This commit is contained in:
Chris Kitching 2014-08-20 08:50:58 -07:00
parent 915d568b45
commit b72133e63f
12 changed files with 85 additions and 62 deletions

View File

@ -71,7 +71,7 @@ import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.PrefUtils;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.util.UIAsyncTask;
import org.mozilla.gecko.widget.ButtonToast;
import org.mozilla.gecko.widget.GeckoActionProvider;
@ -439,9 +439,9 @@ public class BrowserApp extends GeckoApp
}
private void handleReaderFaviconRequest(final String url) {
(new UiAsyncTask<Void, Void, String>(ThreadUtils.getBackgroundHandler()) {
(new UIAsyncTask.WithoutParams<String>(ThreadUtils.getBackgroundHandler()) {
@Override
public String doInBackground(Void... params) {
public String doInBackground() {
return Favicons.getFaviconURLForPageURL(url);
}
@ -2847,9 +2847,9 @@ public class BrowserApp extends GeckoApp
return;
}
(new UiAsyncTask<Void, Void, Boolean>(ThreadUtils.getBackgroundHandler()) {
(new UIAsyncTask.WithoutParams<Boolean>(ThreadUtils.getBackgroundHandler()) {
@Override
public synchronized Boolean doInBackground(Void... params) {
public synchronized Boolean doInBackground() {
// Check to see how many times the app has been launched.
SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
String keyName = getPackageName() + ".feedback_launch_count";
@ -2891,9 +2891,9 @@ public class BrowserApp extends GeckoApp
}
private void getLastUrl() {
(new UiAsyncTask<Void, Void, String>(ThreadUtils.getBackgroundHandler()) {
(new UIAsyncTask.WithoutParams<String>(ThreadUtils.getBackgroundHandler()) {
@Override
public synchronized String doInBackground(Void... params) {
public synchronized String doInBackground() {
// Get the most recent URL stored in browser history.
String url = "";
Cursor c = null;

View File

@ -8,7 +8,7 @@ package org.mozilla.gecko;
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.util.UIAsyncTask;
import android.content.ContentResolver;
import android.content.Context;
@ -141,9 +141,9 @@ public class EditBookmarkDialog {
*/
public void show(final String url) {
final ContentResolver cr = mContext.getContentResolver();
(new UiAsyncTask<Void, Void, Bookmark>(ThreadUtils.getBackgroundHandler()) {
(new UIAsyncTask.WithoutParams<Bookmark>(ThreadUtils.getBackgroundHandler()) {
@Override
public Bookmark doInBackground(Void... params) {
public Bookmark doInBackground() {
final Cursor cursor = BrowserDB.getBookmarkForUrl(cr, url);
if (cursor == null) {
return null;
@ -202,9 +202,9 @@ public class EditBookmarkDialog {
editPrompt.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
(new UiAsyncTask<Void, Void, Void>(ThreadUtils.getBackgroundHandler()) {
(new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
@Override
public Void doInBackground(Void... params) {
public Void doInBackground() {
String newUrl = locationText.getText().toString().trim();
String newKeyword = keywordText.getText().toString().trim();
BrowserDB.updateBookmark(context.getContentResolver(), id, newUrl, nameText.getText().toString(), newKeyword);

View File

@ -15,7 +15,7 @@ import org.json.JSONObject;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.util.UIAsyncTask;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
@ -56,9 +56,9 @@ public class GeckoAccessibility {
}));
public static void updateAccessibilitySettings (final Context context) {
new UiAsyncTask<Void, Void, Void>(ThreadUtils.getBackgroundHandler()) {
new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
@Override
public Void doInBackground(Void... args) {
public Void doInBackground() {
JSONObject ret = new JSONObject();
sEnabled = false;
AccessibilityManager accessibilityManager =

View File

@ -6,7 +6,7 @@ package org.mozilla.gecko;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.util.UIAsyncTask;
import org.json.JSONArray;
import org.json.JSONException;
@ -82,9 +82,9 @@ public final class TabsAccessor {
if (listener == null)
return;
(new UiAsyncTask<Void, Void, List<RemoteTab>>(ThreadUtils.getBackgroundHandler()) {
(new UIAsyncTask.WithoutParams<List<RemoteTab>>(ThreadUtils.getBackgroundHandler()) {
@Override
protected List<RemoteTab> doInBackground(Void... unused) {
protected List<RemoteTab> doInBackground() {
Uri uri = BrowserContract.Tabs.CONTENT_URI;
if (limit > 0) {

View File

@ -20,7 +20,7 @@ import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import static org.mozilla.gecko.favicons.Favicons.context;
import java.io.IOException;

View File

@ -14,7 +14,7 @@ import java.net.URL;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.util.UIAsyncTask;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.ThumbnailHelper;
@ -80,9 +80,9 @@ public final class BitmapUtils {
}
if (data.startsWith("jar:") || data.startsWith("file://")) {
(new UiAsyncTask<Void, Void, Drawable>(ThreadUtils.getBackgroundHandler()) {
(new UIAsyncTask.WithoutParams<Drawable>(ThreadUtils.getBackgroundHandler()) {
@Override
public Drawable doInBackground(Void... params) {
public Drawable doInBackground() {
try {
if (data.startsWith("jar:jar")) {
return GeckoJarReader.getBitmapDrawable(context.getResources(), data);

View File

@ -12,7 +12,7 @@ import org.mozilla.gecko.home.PanelLayout.ContextMenuRegistry;
import org.mozilla.gecko.home.PanelLayout.DatasetHandler;
import org.mozilla.gecko.home.PanelLayout.DatasetRequest;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.util.UIAsyncTask;
import android.content.ContentResolver;
import android.content.Context;
@ -70,7 +70,7 @@ public class DynamicPanel extends HomeFragment {
// Hold a reference to the UiAsyncTask we use to check the state of the
// PanelAuthCache, so that we can cancel it if necessary.
private UiAsyncTask<Void, Void, Boolean> mAuthStateTask;
private UIAsyncTask.WithoutParams<Boolean> mAuthStateTask;
// The configuration associated with this panel
private PanelConfig mPanelConfig;
@ -137,7 +137,7 @@ public class DynamicPanel extends HomeFragment {
mPanelAuthCache.setOnChangeListener(null);
if (mAuthStateTask != null) {
mAuthStateTask.cancel(true);
mAuthStateTask.cancel();
mAuthStateTask = null;
}
}
@ -156,9 +156,9 @@ public class DynamicPanel extends HomeFragment {
Log.d(LOGTAG, "Loading layout");
if (requiresAuth()) {
mAuthStateTask = new UiAsyncTask<Void, Void, Boolean>(ThreadUtils.getBackgroundHandler()) {
mAuthStateTask = new UIAsyncTask.WithoutParams<Boolean>(ThreadUtils.getBackgroundHandler()) {
@Override
public synchronized Boolean doInBackground(Void... params) {
public synchronized Boolean doInBackground() {
return mPanelAuthCache.isAuthenticated(mPanelConfig.getId());
}

View File

@ -26,7 +26,7 @@ import org.mozilla.gecko.home.TopSitesGridView.TopSitesGridContextMenuInfo;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.util.UIAsyncTask;
import org.mozilla.gecko.widget.ButtonToast;
import android.app.Activity;
@ -332,7 +332,7 @@ abstract class HomeFragment extends Fragment {
mIsLoaded = true;
}
private static class RemoveItemByUrlTask extends UiAsyncTask<Void, Void, Void> {
private static class RemoveItemByUrlTask extends UIAsyncTask.WithoutParams<Void> {
private final Context mContext;
private final String mUrl;
private final int mPosition;
@ -357,7 +357,7 @@ abstract class HomeFragment extends Fragment {
}
@Override
public Void doInBackground(Void... params) {
public Void doInBackground() {
ContentResolver cr = mContext.getContentResolver();
if (mPosition > -1) {

View File

@ -5,27 +5,15 @@
package org.mozilla.gecko.home;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Callback;
import org.mozilla.gecko.db.BrowserContract.TopSites;
import org.mozilla.gecko.db.URLMetadata;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import android.content.Context;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.support.v4.content.AsyncTaskLoader;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.RelativeLayout;
import android.widget.TextView;

View File

@ -78,7 +78,7 @@ gujar.sources += [
'util/RawResource.java',
'util/StringUtils.java',
'util/ThreadUtils.java',
'util/UiAsyncTask.java',
'util/UIAsyncTask.java',
'util/WebActivityMapper.java',
]
gujar.extra_jars = [

View File

@ -11,7 +11,7 @@ import org.mozilla.gecko.home.HomeConfig;
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
import org.mozilla.gecko.home.HomeConfig.State;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.util.UIAsyncTask;
import android.content.Context;
import android.text.TextUtils;
@ -23,7 +23,7 @@ public class PanelsPreferenceCategory extends CustomListCategory {
protected HomeConfig mHomeConfig;
protected HomeConfig.Editor mConfigEditor;
protected UiAsyncTask<Void, Void, HomeConfig.State> mLoadTask;
protected UIAsyncTask.WithoutParams<State> mLoadTask;
public PanelsPreferenceCategory(Context context) {
super(context);
@ -55,9 +55,9 @@ public class PanelsPreferenceCategory extends CustomListCategory {
* Load the Home Panels config and populate the preferences screen and maintain local state.
*/
private void loadHomeConfig(final String animatePanelId) {
mLoadTask = new UiAsyncTask<Void, Void, HomeConfig.State>(ThreadUtils.getBackgroundHandler()) {
mLoadTask = new UIAsyncTask.WithoutParams<State>(ThreadUtils.getBackgroundHandler()) {
@Override
public HomeConfig.State doInBackground(Void... params) {
public HomeConfig.State doInBackground() {
return mHomeConfig.load();
}
@ -171,7 +171,7 @@ public class PanelsPreferenceCategory extends CustomListCategory {
@Override
protected void onPrepareForRemoval() {
if (mLoadTask != null) {
mLoadTask.cancel(true);
mLoadTask.cancel();
}
}

View File

@ -14,10 +14,44 @@ import android.os.Looper;
* The standard {@link android.os.AsyncTask} only runs onPostExecute on the
* thread it is constructed on, so this is a convenience class for creating
* tasks off the UI thread.
*
* We use generics differently to Android's AsyncTask.
* Android uses a "Params" type parameter to represent the type of all the parameters to this task.
* It then uses arguments of type Params... to permit arbitrarily-many of these to be passed
* fluently.
*
* Unfortunately, since Java does not support generic array types (and since varargs desugars to a
* single array parameter) that behaviour exposes a hole in the type system. See:
* http://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html#vulnerabilities
*
* Instead, we equivalently have a single type parameter "Param". A UiAsyncTask may take exactly one
* parameter of type Param. Since Param can be an array type, this no more restrictive than the
* other approach, it just provides additional type safety.
*/
public abstract class UiAsyncTask<Params, Progress, Result> {
private volatile boolean mCancelled;
public abstract class UIAsyncTask<Param, Result> {
/**
* Provide a convenient API for parameter-free UiAsyncTasks by wrapping parameter-taking methods
* from UiAsyncTask in parameterless equivalents.
*/
public static abstract class WithoutParams<InnerResult> extends UIAsyncTask<Void, InnerResult> {
public WithoutParams(Handler backgroundThreadHandler) {
super(backgroundThreadHandler);
}
public void execute() {
execute(null);
}
@Override
protected InnerResult doInBackground(Void unused) {
return doInBackground();
}
protected abstract InnerResult doInBackground();
}
/* inner-access */ final Handler mBackgroundThreadHandler;
private volatile boolean mCancelled;
private static Handler sHandler;
/**
@ -25,7 +59,7 @@ public abstract class UiAsyncTask<Params, Progress, Result> {
*
* @param backgroundThreadHandler the handler to execute the background task on
*/
public UiAsyncTask(Handler backgroundThreadHandler) {
public UIAsyncTask(Handler backgroundThreadHandler) {
mBackgroundThreadHandler = backgroundThreadHandler;
}
@ -33,44 +67,45 @@ public abstract class UiAsyncTask<Params, Progress, Result> {
if (sHandler == null) {
sHandler = new Handler(Looper.getMainLooper());
}
return sHandler;
}
private final class BackgroundTaskRunnable implements Runnable {
private Params[] mParams;
private Param mParam;
public BackgroundTaskRunnable(Params... params) {
mParams = params;
public BackgroundTaskRunnable(Param param) {
mParam = param;
}
@Override
public void run() {
final Result result = doInBackground(mParams);
final Result result = doInBackground(mParam);
getUiHandler().post(new Runnable() {
@Override
public void run() {
if (mCancelled)
if (mCancelled) {
onCancelled();
else
} else {
onPostExecute(result);
}
}
});
}
}
public final void execute(final Params... params) {
protected void execute(final Param param) {
getUiHandler().post(new Runnable() {
@Override
public void run() {
onPreExecute();
mBackgroundThreadHandler.post(new BackgroundTaskRunnable(params));
mBackgroundThreadHandler.post(new BackgroundTaskRunnable(param));
}
});
}
@SuppressWarnings({"UnusedParameters"})
public final boolean cancel(boolean mayInterruptIfRunning) {
public final boolean cancel() {
mCancelled = true;
return mCancelled;
}
@ -82,5 +117,5 @@ public abstract class UiAsyncTask<Params, Progress, Result> {
protected void onPreExecute() { }
protected void onPostExecute(Result result) { }
protected void onCancelled() { }
protected abstract Result doInBackground(Params... params);
protected abstract Result doInBackground(Param param);
}