diff --git a/app/src/common/shared/org/mozilla/vrbrowser/AppExecutors.java b/app/src/common/shared/org/mozilla/vrbrowser/AppExecutors.java index b34e24790..a23cf6ab8 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/AppExecutors.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/AppExecutors.java @@ -1,13 +1,14 @@ package org.mozilla.vrbrowser; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; +import androidx.annotation.NonNull; + import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import androidx.annotation.NonNull; - public class AppExecutors { private final Executor mDiskIO; @@ -16,14 +17,19 @@ public class AppExecutors { private final Executor mMainThread; + private final HandlerThread mBackgroundThread; + private Handler mBackgroundHandler; + private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) { this.mDiskIO = diskIO; this.mNetworkIO = networkIO; this.mMainThread = mainThread; + mBackgroundThread = new HandlerThread("BackgroundThread"); } public AppExecutors() { - this(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(3), + this(Executors.newSingleThreadExecutor(), + Executors.newFixedThreadPool(3), new MainThreadExecutor()); } @@ -39,6 +45,14 @@ public Executor mainThread() { return mMainThread; } + public Handler backgroundThread() { + if (!mBackgroundThread.isAlive()) { + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + } + return mBackgroundHandler; + } + private static class MainThreadExecutor implements Executor { private Handler mainThreadHandler = new Handler(Looper.getMainLooper()); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStore.java index 3f0bfec52..ab2f2fd2d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStore.java @@ -18,12 +18,12 @@ import org.mozilla.vrbrowser.browser.content.TrackingProtectionStore; import org.mozilla.vrbrowser.db.SitePermission; import org.mozilla.vrbrowser.utils.SystemUtils; -import org.mozilla.vrbrowser.utils.ThreadUtils; import org.mozilla.vrbrowser.utils.UrlUtils; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.Executor; public class SessionStore implements GeckoSession.PermissionDelegate{ private static final String LOGTAG = SystemUtils.createLogtag(SessionStore.class); @@ -38,6 +38,7 @@ public static SessionStore get() { return mInstance; } + private Executor mMainExecutor; private Context mContext; private GeckoRuntime mRuntime; private ArrayList mSessions; @@ -55,6 +56,7 @@ private SessionStore() { public void setContext(Context context, Bundle aExtras) { mContext = context; + mMainExecutor = ((VRBrowserApplication)context.getApplicationContext()).getExecutors().mainThread(); // FIXME: Once GeckoView has a prefs API SessionUtils.vrPrefsWorkAround(context, aExtras); @@ -199,7 +201,7 @@ void sessionActiveStateChanged() { if (count > MAX_GECKO_SESSIONS) { Log.d(LOGTAG, "Too many GeckoSessions. Active: " + activeCount + " Inactive: " + inactiveCount + " Suspended: " + suspendedCount); mSuspendPending = true; - ThreadUtils.postToUiThread(this::limitInactiveSessions); + mMainExecutor.execute(this::limitInactiveSessions); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java index 2625ef528..f27080d23 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java @@ -16,18 +16,20 @@ import androidx.recyclerview.widget.RecyclerView; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.VRBrowserApplication; import org.mozilla.vrbrowser.databinding.LanguageItemBinding; import org.mozilla.vrbrowser.ui.callbacks.LanguageItemCallback; -import org.mozilla.vrbrowser.utils.ThreadUtils; import org.mozilla.vrbrowser.utils.ViewUtils; import java.util.Collections; import java.util.List; +import java.util.concurrent.Executor; public class LanguagesAdapter extends RecyclerView.Adapter { private static final int ICON_ANIMATION_DURATION = 200; + private Executor mMainExecutor; private List mLanguagesList; private boolean mIsPreferred; @@ -40,6 +42,7 @@ public class LanguagesAdapter extends RecyclerView.Adapter notifyDataSetChanged()); + mMainExecutor.execute(this::notifyDataSetChanged); } public void addItemAlphabetical(Language language) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java index 2983a2b57..26cfe3709 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java @@ -29,7 +29,6 @@ import org.mozilla.vrbrowser.ui.widgets.TooltipWidget; import org.mozilla.vrbrowser.ui.widgets.UIWidget; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; -import org.mozilla.vrbrowser.utils.ThreadUtils; import org.mozilla.vrbrowser.utils.ViewUtils; public class UIButton extends AppCompatImageButton implements CustomUIButton { @@ -150,11 +149,11 @@ public void setCurvedTooltip(boolean aEnabled) { public boolean onHoverEvent(MotionEvent event) { if (getTooltipText() != null) { if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) { - ThreadUtils.postDelayedToUiThread(mShowTooltipRunnable, mTooltipDelay); + postDelayed(mShowTooltipRunnable, mTooltipDelay); } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { - ThreadUtils.removeCallbacksFromUiThread(mShowTooltipRunnable); - ThreadUtils.postToUiThread(mHideTooltipRunnable); + removeCallbacks(mShowTooltipRunnable); + post(mHideTooltipRunnable); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NotificationManager.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NotificationManager.java index 802b10c79..ef8a47dd1 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NotificationManager.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NotificationManager.java @@ -10,9 +10,9 @@ import androidx.annotation.StringRes; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.VRBrowserApplication; import org.mozilla.vrbrowser.ui.views.UIButton; import org.mozilla.vrbrowser.ui.widgets.NotificationManager.Notification.NotificationPosition; -import org.mozilla.vrbrowser.utils.ThreadUtils; import java.util.HashMap; import java.util.Iterator; @@ -166,7 +166,7 @@ public static void show(int notificationId, @NonNull Notification notification) } Runnable hideTask = () -> hide(notificationId); - ThreadUtils.postDelayedToUiThread(hideTask, notification.mDuration); + notification.mView.postDelayed(hideTask, notification.mDuration); mData.put(notificationId, new NotificationData(notificationView, notification, hideTask)); } @@ -192,7 +192,7 @@ public static void hideAll() { } private static void hideNotification(@NonNull NotificationData data) { - ThreadUtils.removeCallbacksFromUiThread(data.mHideTask); + data.mNotificationView.removeCallbacks(data.mHideTask); data.mNotificationView.hide(UIWidget.REMOVE_WIDGET); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/VoiceSearchWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/VoiceSearchWidget.java index a89f1a369..6733fe210 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/VoiceSearchWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/VoiceSearchWidget.java @@ -33,7 +33,6 @@ import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.LocaleUtils; -import org.mozilla.vrbrowser.utils.ThreadUtils; public class VoiceSearchWidget extends UIDialog implements WidgetManagerDelegate.PermissionListener, Application.ActivityLifecycleCallbacks { @@ -298,7 +297,7 @@ public void show(@ShowFlags int aShowFlags) { if (index == PromptDialogWidget.POSITIVE) { SettingsStore.getInstance(getContext()).setSpeechDataCollectionEnabled(true); } - ThreadUtils.postToUiThread(() -> show(aShowFlags)); + post(() -> show(aShowFlags)); }, () -> { mWidgetManager.openNewTabForeground(getResources().getString(R.string.private_policy_url)); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java index acb67aae0..d1772e238 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java @@ -21,10 +21,6 @@ import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.LocaleUtils; -import org.mozilla.vrbrowser.utils.ThreadUtils; - -import java.util.Collections; -import java.util.List; public class ContentLanguageOptionsView extends SettingsView { @@ -126,7 +122,7 @@ private void saveCurrentLanguages() { } private void refreshLanguages() { - ThreadUtils.postToUiThread(() -> { + post(() -> { mPreferredAdapter.setLanguageList(LocaleUtils.getPreferredLanguages(getContext())); mAvailableAdapter.setLanguageList(LocaleUtils.getAvailableLanguages(getContext())); }); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java index 51762e8f7..3ee01fb65 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java @@ -121,7 +121,7 @@ public static void scaleIn(@NonNull View aView, long duration, long delay, final public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (aCallback != null) - ThreadUtils.postToUiThread(aCallback); + aView.post(aCallback); } }).setUpdateListener(animation -> aView.invalidate()); } @@ -134,7 +134,7 @@ public static void scaleOut(@NonNull View aView, long duration, long delay, fina public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (aCallback != null) - ThreadUtils.postToUiThread(aCallback); + aView.post(aCallback); } }).setUpdateListener(animation -> aView.invalidate()); } @@ -142,7 +142,7 @@ public void onAnimationEnd(Animator animation) { public static void scaleTo(@NonNull View aView, float scaleX, float scaleY, long duration, long delay, final Runnable aCallback) { if (aView.getScaleX() == scaleX && aView.getScaleY() == scaleY) { if (aCallback != null) { - ThreadUtils.postToUiThread(aCallback); + aView.post(aCallback); } return; } @@ -151,7 +151,7 @@ public static void scaleTo(@NonNull View aView, float scaleX, float scaleY, long public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (aCallback != null) { - ThreadUtils.postToUiThread(aCallback); + aView.post(aCallback); } } }).setUpdateListener(animation -> aView.invalidate()); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java index 6e4fdac14..9ef84fec1 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java @@ -13,6 +13,7 @@ import org.mozilla.vrbrowser.BuildConfig; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.VRBrowserActivity; +import org.mozilla.vrbrowser.VRBrowserApplication; import java.io.BufferedReader; import java.io.File; @@ -59,7 +60,7 @@ public static String createLogtag(@NonNull Class aClass) { private static final String CRASH_STATS_URL = "https://crash-stats.mozilla.com/report/index/"; private static void sendCrashFiles(@NonNull Context context, @NonNull final String aDumpFile, @NonNull final String aExtraFile) { - ThreadUtils.postToBackgroundThread(() -> { + ((VRBrowserApplication)context.getApplicationContext()).getExecutors().backgroundThread().post(() -> { try { GeckoResult result = CrashReporter.sendCrashReport(context, new File(aDumpFile), new File(aExtraFile), context.getString(R.string.crash_app_name)); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/ThreadUtils.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/ThreadUtils.java deleted file mode 100644 index 33e0433a3..000000000 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/ThreadUtils.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.mozilla.vrbrowser.utils; - -import android.os.Handler; -import android.os.Looper; - -public class ThreadUtils extends Thread { - private static final Handler sUiHandler = new Handler(Looper.getMainLooper()); - - private static final String LOOPER_NAME = "VRBBackgroundThread"; - - // Guarded by 'ThreadUtils.class'. - private static Handler mBackgroundHandler; - private static Thread mBackgroundThread; - - // The initial Runnable to run on the new mBackgroundThread. Its purpose - // is to avoid us having to wait for the new mBackgroundThread to start. - private Runnable mInitialRunnable; - - // Singleton, so private constructor. - private ThreadUtils(final Runnable initialRunnable) { - mInitialRunnable = initialRunnable; - } - - @Override - public void run() { - setName(LOOPER_NAME); - Looper.prepare(); - - synchronized (ThreadUtils.class) { - mBackgroundHandler = new Handler(); - ThreadUtils.class.notifyAll(); - } - - if (mInitialRunnable != null) { - mInitialRunnable.run(); - mInitialRunnable = null; - } - - Looper.loop(); - } - - private static void startThread(final Runnable initialRunnable) { - mBackgroundThread = new ThreadUtils(initialRunnable); - mBackgroundThread.setDaemon(true); - mBackgroundThread.start(); - } - - // Get a Handler for a looper mBackgroundThread, or create one if it doesn't yet exist. - /*package*/ static synchronized Handler getHandler() { - if (mBackgroundThread == null) { - startThread(null); - } - - while (mBackgroundHandler == null) { - try { - ThreadUtils.class.wait(); - } catch (final InterruptedException e) { - } - } - return mBackgroundHandler; - } - - public static synchronized void postToBackgroundThread(final Runnable runnable) { - if (mBackgroundThread == null) { - startThread(runnable); - return; - } - getHandler().post(runnable); - } - - public static void postToUiThread(final Runnable runnable) { - sUiHandler.post(runnable); - } - - public static void postDelayedToUiThread(final Runnable runnable, final long timeout) { - sUiHandler.postDelayed(runnable, timeout); - } - - public static void removeCallbacksFromUiThread(final Runnable runnable) { - sUiHandler.removeCallbacks(runnable); - } -}