aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Jin <kjin@google.com>2015-03-06 19:42:50 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-03-06 19:42:50 +0000
commitb9cf8750ebf4394555f9ef801b2114d4901682a5 (patch)
tree9ad9578a09ee627b39f68c5c0128131ab32f4c03
parentb284da9483474581abc25a8cc822b4dadf2dfa34 (diff)
parent1dbff05bf375557ea73e07632d732bd90a71af9e (diff)
downloaddroiddriver-b9cf8750ebf4394555f9ef801b2114d4901682a5.tar.gz
Merge "runOnMainSync on a single thread"
-rw-r--r--src/io/appium/droiddriver/base/BaseUiElement.java11
-rw-r--r--src/io/appium/droiddriver/exceptions/DroidDriverException.java33
-rw-r--r--src/io/appium/droiddriver/helpers/DroidDrivers.java16
-rw-r--r--src/io/appium/droiddriver/util/InstrumentationUtils.java23
4 files changed, 49 insertions, 34 deletions
diff --git a/src/io/appium/droiddriver/base/BaseUiElement.java b/src/io/appium/droiddriver/base/BaseUiElement.java
index 293ee9c..e98ee44 100644
--- a/src/io/appium/droiddriver/base/BaseUiElement.java
+++ b/src/io/appium/droiddriver/base/BaseUiElement.java
@@ -26,7 +26,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import io.appium.droiddriver.UiElement;
@@ -201,14 +200,8 @@ public abstract class BaseUiElement<R, E extends BaseUiElement<R, E>> implements
try {
return futureTask.get();
- } catch (ExecutionException e) {
- Throwable cause = e.getCause();
- if (cause instanceof RuntimeException) {
- throw (RuntimeException) cause;
- }
- throw new DroidDriverException(cause);
- } catch (InterruptedException e) {
- throw new DroidDriverException(e);
+ } catch (Throwable t) {
+ throw DroidDriverException.propagate(t);
}
}
diff --git a/src/io/appium/droiddriver/exceptions/DroidDriverException.java b/src/io/appium/droiddriver/exceptions/DroidDriverException.java
index e7ba2b7..482130f 100644
--- a/src/io/appium/droiddriver/exceptions/DroidDriverException.java
+++ b/src/io/appium/droiddriver/exceptions/DroidDriverException.java
@@ -34,4 +34,37 @@ public class DroidDriverException extends RuntimeException {
public DroidDriverException(String message, Throwable cause) {
super(message, cause);
}
+
+ /**
+ * Adapted from <a href="http://guava-libraries.googlecode.com">Guava libraries</a>. <p>
+ * Propagates {@code throwable} as-is if it is an instance of {@link RuntimeException} or {@link
+ * Error}, or else as a last resort, wraps it in a {@code DroidDriverException} and then
+ * propagates. <p> This method always throws an exception. The {@code DroidDriverException} return
+ * type is only for client code to make Java type system happy in case a return value is required
+ * by the enclosing method. Example usage:
+ * <pre>
+ * T doSomething() {
+ * try {
+ * return someMethodThatCouldThrowAnything();
+ * } catch (IKnowWhatToDoWithThisException e) {
+ * return handle(e);
+ * } catch (Throwable t) {
+ * throw DroidDriverException.propagate(t);
+ * }
+ * }
+ * </pre>
+ *
+ * @param throwable the Throwable to propagate
+ * @return nothing will ever be returned; this return type is only for your convenience, as
+ * illustrated in the example above
+ */
+ public static DroidDriverException propagate(Throwable throwable) {
+ if (throwable instanceof RuntimeException) {
+ throw (RuntimeException) throwable;
+ }
+ if (throwable instanceof Error) {
+ throw (Error) throwable;
+ }
+ throw new DroidDriverException(throwable);
+ }
}
diff --git a/src/io/appium/droiddriver/helpers/DroidDrivers.java b/src/io/appium/droiddriver/helpers/DroidDrivers.java
index 5cddb4f..7725bf5 100644
--- a/src/io/appium/droiddriver/helpers/DroidDrivers.java
+++ b/src/io/appium/droiddriver/helpers/DroidDrivers.java
@@ -20,8 +20,6 @@ import android.annotation.TargetApi;
import android.app.Instrumentation;
import android.os.Build;
-import java.lang.reflect.InvocationTargetException;
-
import io.appium.droiddriver.DroidDriver;
import io.appium.droiddriver.exceptions.DroidDriverException;
import io.appium.droiddriver.instrumentation.InstrumentationDriver;
@@ -77,18 +75,8 @@ public class DroidDrivers {
try {
return (DroidDriver) Class.forName(driverClass).getConstructor(Instrumentation.class)
.newInstance(instrumentation);
- } catch (ClassNotFoundException e) {
- throw new DroidDriverException(e);
- } catch (NoSuchMethodException e) {
- throw new DroidDriverException(e);
- } catch (InstantiationException e) {
- throw new DroidDriverException(e);
- } catch (IllegalAccessException e) {
- throw new DroidDriverException(e);
- } catch (IllegalArgumentException e) {
- throw new DroidDriverException(e);
- } catch (InvocationTargetException e) {
- throw new DroidDriverException(e);
+ } catch (Throwable t) {
+ throw DroidDriverException.propagate(t);
}
}
diff --git a/src/io/appium/droiddriver/util/InstrumentationUtils.java b/src/io/appium/droiddriver/util/InstrumentationUtils.java
index a019008..95eb48c 100644
--- a/src/io/appium/droiddriver/util/InstrumentationUtils.java
+++ b/src/io/appium/droiddriver/util/InstrumentationUtils.java
@@ -22,6 +22,8 @@ import android.os.Looper;
import android.util.Log;
import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
@@ -41,6 +43,7 @@ public class InstrumentationUtils {
public void run() {
}
};
+ private static final Executor RUN_ON_MAIN_SYNC_EXECUTOR = Executors.newSingleThreadExecutor();
/**
* Initializes this class. If you use a runner that is not DroidDriver-aware, you need to call
@@ -112,8 +115,8 @@ public class InstrumentationUtils {
Logs.log(Log.INFO,
"Timed out after " + timeoutMillis + " milliseconds waiting for idle on main looper");
return false;
- } catch (Throwable e) {
- throw new DroidDriverException(e);
+ } catch (Throwable t) {
+ throw DroidDriverException.propagate(t);
}
return true;
}
@@ -132,12 +135,10 @@ public class InstrumentationUtils {
* Runs {@code callable} on the main thread on best-effort basis up to a time limit, which
* defaults to {@code 10000L} and can be set as an am instrument option under the key {@code
* dd.runOnMainSyncTimeout}. <p>This is a safer variation of {@link Instrumentation#runOnMainSync}
- * because the latter may hang. But it is heavy because a new thread is created for each call. You
- * may turn off this behavior by setting {@code "-e dd.runOnMainSyncTimeout 0"} on the am command
- * line.</p>The {@code callable} may never run, for example, in case that the main Looper has
- * exited due to uncaught exception.
+ * because the latter may hang. You may turn off this behavior by setting {@code "-e
+ * dd.runOnMainSyncTimeout 0"} on the am command line.</p>The {@code callable} may never run, for
+ * example, if the main Looper has exited due to uncaught exception.
*/
- // TODO: call runOnMainSync on a single worker thread?
public static <V> V runOnMainSyncWithTimeout(Callable<V> callable) {
validateNotAppThread();
final RunOnMainSyncFutureTask<V> futureTask = new RunOnMainSyncFutureTask<>(callable);
@@ -146,12 +147,12 @@ public class InstrumentationUtils {
// Call runOnMainSync on current thread without time limit.
futureTask.runOnMainSyncNoThrow();
} else {
- new Thread() {
+ RUN_ON_MAIN_SYNC_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
futureTask.runOnMainSyncNoThrow();
}
- }.start();
+ });
}
try {
@@ -159,8 +160,8 @@ public class InstrumentationUtils {
} catch (java.util.concurrent.TimeoutException e) {
throw new TimeoutException("Timed out after " + runOnMainSyncTimeoutMillis
+ " milliseconds waiting for Instrumentation.runOnMainSync", e);
- } catch (Throwable e) {
- throw new DroidDriverException(e);
+ } catch (Throwable t) {
+ throw DroidDriverException.propagate(t);
} finally {
futureTask.cancel(false);
}