From 45fe86d46f10d4f0df0c6e39fbcc316d3d90aa4e Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Wed, 22 Jun 2016 22:33:30 -0500 Subject: [PATCH] Execute async IAB methods on single named thread --- .../java/org/tasks/analytics/Tracker.java | 8 +++- .../java/org/tasks/analytics/Tracker.java | 6 ++- .../android/vending/billing/IabHelper.java | 15 +++++--- .../java/org/tasks/analytics/Tracker.java | 8 +++- .../org/tasks/billing/InventoryHelper.java | 10 ++++- .../org/tasks/billing/PurchaseHelper.java | 10 +++-- .../ErrorReportingSingleThreadExecutor.java | 38 +++++++++++++++++++ .../tasks/injection/ApplicationModule.java | 15 ++++++++ 8 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/tasks/ErrorReportingSingleThreadExecutor.java diff --git a/src/amazon/java/org/tasks/analytics/Tracker.java b/src/amazon/java/org/tasks/analytics/Tracker.java index cc0759014..faa2e9684 100644 --- a/src/amazon/java/org/tasks/analytics/Tracker.java +++ b/src/amazon/java/org/tasks/analytics/Tracker.java @@ -43,9 +43,13 @@ public class Tracker { analytics.setAppOptOut(!enabled); } - public void reportException(Exception e) { + public void reportException(Throwable t) { + reportException(Thread.currentThread(), t); + } + + public void reportException(Thread thread, Throwable t) { tracker.send(new HitBuilders.ExceptionBuilder() - .setDescription(exceptionParser.getDescription(Thread.currentThread().getName(), e)) + .setDescription(exceptionParser.getDescription(thread.getName(), t)) .setFatal(false) .build()); } diff --git a/src/generic/java/org/tasks/analytics/Tracker.java b/src/generic/java/org/tasks/analytics/Tracker.java index 5fe0467e2..1d8626d01 100644 --- a/src/generic/java/org/tasks/analytics/Tracker.java +++ b/src/generic/java/org/tasks/analytics/Tracker.java @@ -17,7 +17,11 @@ public class Tracker { } - public void reportException(Exception e) { + public void reportException(Throwable t) { + + } + + public void reportException(Thread thread, Throwable t) { } diff --git a/src/googleplay/java/com/android/vending/billing/IabHelper.java b/src/googleplay/java/com/android/vending/billing/IabHelper.java index 3c5eea685..d00daf4d5 100644 --- a/src/googleplay/java/com/android/vending/billing/IabHelper.java +++ b/src/googleplay/java/com/android/vending/billing/IabHelper.java @@ -36,6 +36,7 @@ import org.json.JSONException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; /** @@ -69,6 +70,9 @@ import java.util.List; * */ public class IabHelper { + + private final Executor executor; + // Is debug logging enabled? boolean mDebugLog = false; String mDebugTag = "IabHelper"; @@ -164,9 +168,10 @@ public class IabHelper { * public key in your application's page on Google Play Developer Console. Note that this * is NOT your "developer public key". */ - public IabHelper(Context ctx, String base64PublicKey) { + public IabHelper(Context ctx, String base64PublicKey, Executor executor) { mContext = ctx.getApplicationContext(); mSignatureBase64 = base64PublicKey; + this.executor = executor; logDebug("IAB helper created."); } @@ -649,7 +654,7 @@ public class IabHelper { checkNotDisposed(); checkSetupDone("queryInventory"); flagStartAsync("refresh inventory"); - (new Thread(new Runnable() { + executor.execute(new Runnable() { public void run() { IabResult result = new IabResult(BILLING_RESPONSE_RESULT_OK, "Inventory refresh successful."); Inventory inv = null; @@ -672,7 +677,7 @@ public class IabHelper { }); } } - })).start(); + }); } public void queryInventoryAsync(QueryInventoryFinishedListener listener) { @@ -1006,7 +1011,7 @@ public class IabHelper { final OnConsumeMultiFinishedListener multiListener) { final Handler handler = new Handler(); flagStartAsync("consume"); - (new Thread(new Runnable() { + executor.execute(new Runnable() { public void run() { final List results = new ArrayList(); for (Purchase purchase : purchases) { @@ -1035,7 +1040,7 @@ public class IabHelper { }); } } - })).start(); + }); } void logDebug(String msg) { diff --git a/src/googleplay/java/org/tasks/analytics/Tracker.java b/src/googleplay/java/org/tasks/analytics/Tracker.java index 01dc22a0f..c199451f9 100644 --- a/src/googleplay/java/org/tasks/analytics/Tracker.java +++ b/src/googleplay/java/org/tasks/analytics/Tracker.java @@ -44,9 +44,13 @@ public class Tracker { analytics.setAppOptOut(!enabled); } - public void reportException(Exception e) { + public void reportException(Throwable t) { + reportException(Thread.currentThread(), t); + } + + public void reportException(Thread thread, Throwable t) { tracker.send(new HitBuilders.ExceptionBuilder() - .setDescription(exceptionParser.getDescription(Thread.currentThread().getName(), e)) + .setDescription(exceptionParser.getDescription(thread.getName(), t)) .setFatal(false) .build()); } diff --git a/src/googleplay/java/org/tasks/billing/InventoryHelper.java b/src/googleplay/java/org/tasks/billing/InventoryHelper.java index 9c3e674eb..7b1792ebc 100644 --- a/src/googleplay/java/org/tasks/billing/InventoryHelper.java +++ b/src/googleplay/java/org/tasks/billing/InventoryHelper.java @@ -14,7 +14,10 @@ import org.tasks.R; import org.tasks.injection.ForApplication; import org.tasks.preferences.Preferences; +import java.util.concurrent.Executor; + import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import timber.log.Timber; @@ -25,14 +28,17 @@ public class InventoryHelper implements IabBroadcastReceiver.IabBroadcastListene private final Context context; private final Preferences preferences; private final Broadcaster broadcaster; + private final Executor executor; private Inventory inventory; @Inject - public InventoryHelper(@ForApplication Context context, Preferences preferences, Broadcaster broadcaster) { + public InventoryHelper(@ForApplication Context context, Preferences preferences, + Broadcaster broadcaster, @Named("iab-executor") Executor executor) { this.context = context; this.preferences = preferences; this.broadcaster = broadcaster; + this.executor = executor; } public void initialize() { @@ -41,7 +47,7 @@ public class InventoryHelper implements IabBroadcastReceiver.IabBroadcastListene } public void refreshInventory() { - final IabHelper helper = new IabHelper(context, context.getString(R.string.gp_key)); + final IabHelper helper = new IabHelper(context, context.getString(R.string.gp_key), executor); helper.startSetup(getSetupListener(helper)); } diff --git a/src/googleplay/java/org/tasks/billing/PurchaseHelper.java b/src/googleplay/java/org/tasks/billing/PurchaseHelper.java index 5bf7040aa..98f8d877b 100644 --- a/src/googleplay/java/org/tasks/billing/PurchaseHelper.java +++ b/src/googleplay/java/org/tasks/billing/PurchaseHelper.java @@ -21,8 +21,10 @@ import org.tasks.preferences.Preferences; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import timber.log.Timber; @@ -37,18 +39,20 @@ public class PurchaseHelper implements IabHelper.OnIabSetupFinishedListener { private final Tracker tracker; private final Broadcaster broadcaster; private final InventoryHelper inventory; + private Executor executor; private PurchaseHelperCallback activityResultCallback; private IabHelper iabHelper; @Inject public PurchaseHelper(@ForApplication Context context, Preferences preferences, Tracker tracker, - Broadcaster broadcaster, InventoryHelper inventory) { + Broadcaster broadcaster, InventoryHelper inventory, @Named("iab-executor") Executor executor) { this.context = context; this.preferences = preferences; this.tracker = tracker; this.broadcaster = broadcaster; this.inventory = inventory; + this.executor = executor; } @Override @@ -101,7 +105,7 @@ public class PurchaseHelper implements IabHelper.OnIabSetupFinishedListener { if (themes != null) { purchases.add(themes); } - final IabHelper iabHelper = new IabHelper(context, context.getString(R.string.gp_key)); + final IabHelper iabHelper = new IabHelper(context, context.getString(R.string.gp_key), executor); iabHelper.enableDebugLogging(true); iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { @Override @@ -150,7 +154,7 @@ public class PurchaseHelper implements IabHelper.OnIabSetupFinishedListener { callback.purchaseCompleted(false, sku); return; } - iabHelper = new IabHelper(context, context.getString(R.string.gp_key)); + iabHelper = new IabHelper(context, context.getString(R.string.gp_key), executor); iabHelper.enableDebugLogging(BuildConfig.DEBUG); Timber.d("%s: startSetup", iabHelper); iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { diff --git a/src/main/java/org/tasks/ErrorReportingSingleThreadExecutor.java b/src/main/java/org/tasks/ErrorReportingSingleThreadExecutor.java new file mode 100644 index 000000000..7373e9de5 --- /dev/null +++ b/src/main/java/org/tasks/ErrorReportingSingleThreadExecutor.java @@ -0,0 +1,38 @@ +package org.tasks; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import org.tasks.analytics.Tracker; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; + +import static java.util.concurrent.Executors.newSingleThreadExecutor; + +public class ErrorReportingSingleThreadExecutor implements Executor, Thread.UncaughtExceptionHandler { + private final ExecutorService executorService; + private final Tracker tracker; + + public ErrorReportingSingleThreadExecutor(String nameFormat, Tracker tracker) { + executorService = newSingleThreadExecutor( + new ThreadFactoryBuilder() + .setNameFormat(String.format("%s-%%d", nameFormat)) + .setUncaughtExceptionHandler(this) + .build()); + this.tracker = tracker; + } + + @Override + public void execute(Runnable runnable) { + try { + executorService.execute(runnable); + } catch (Exception e) { + tracker.reportException(e); + } + } + + @Override + public void uncaughtException(Thread thread, Throwable throwable) { + tracker.reportException(thread, throwable); + } +} diff --git a/src/main/java/org/tasks/injection/ApplicationModule.java b/src/main/java/org/tasks/injection/ApplicationModule.java index fc423e341..89ade44a2 100644 --- a/src/main/java/org/tasks/injection/ApplicationModule.java +++ b/src/main/java/org/tasks/injection/ApplicationModule.java @@ -2,6 +2,14 @@ package org.tasks.injection; import android.content.Context; +import org.tasks.ErrorReportingSingleThreadExecutor; +import org.tasks.analytics.Tracker; + +import java.util.concurrent.Executor; + +import javax.inject.Named; +import javax.inject.Singleton; + import dagger.Module; import dagger.Provides; @@ -18,4 +26,11 @@ public class ApplicationModule { public Context getApplicationContext() { return context; } + + @Provides + @Singleton + @Named("iab-executor") + public Executor getIabExecutor(Tracker tracker) { + return new ErrorReportingSingleThreadExecutor("iab-executor", tracker); + } }