зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1191083 - Add mechanism to queue native calls in GeckoThread; r=snorp
We cannot call native methods until Gecko is loaded. This patch adds a mechanism in GeckoThread so that other code can queue up native method calls and have those calls automatically delivered when Gecko is ready.
This commit is contained in:
Родитель
3ef08d00ac
Коммит
5027cc3ce3
|
@ -22,6 +22,9 @@ import android.os.SystemClock;
|
|||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
@ -70,6 +73,21 @@ public class GeckoThread extends Thread implements GeckoEventListener {
|
|||
|
||||
private static final AtomicReference<State> sState = new AtomicReference<>(State.INITIAL);
|
||||
|
||||
private static class QueuedCall {
|
||||
public Method method;
|
||||
public Object target;
|
||||
public Object[] args;
|
||||
|
||||
public QueuedCall(final Method method, final Object target, final Object[] args) {
|
||||
this.method = method;
|
||||
this.target = target;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int QUEUED_CALLS_COUNT = 16;
|
||||
private static final ArrayList<QueuedCall> QUEUED_CALLS = new ArrayList<>(QUEUED_CALLS_COUNT);
|
||||
|
||||
private static final Queue<GeckoEvent> PENDING_EVENTS = new ConcurrentLinkedQueue<GeckoEvent>();
|
||||
|
||||
private static GeckoThread sGeckoThread;
|
||||
|
@ -120,6 +138,89 @@ public class GeckoThread extends Thread implements GeckoEventListener {
|
|||
return isState(State.RUNNING);
|
||||
}
|
||||
|
||||
// Invoke the given Method and handle checked Exceptions.
|
||||
private static void invokeMethod(final Method method, final Object obj, final Object[] args) {
|
||||
try {
|
||||
method.invoke(obj, args);
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new IllegalStateException("Unexpected exception", e);
|
||||
} catch (final InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException("Cannot make call", e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
// Queue a call to the given method.
|
||||
private static void queueNativeCallLocked(final Class<?> cls, final String methodName,
|
||||
final Object obj, final Object[] args) {
|
||||
final Class<?>[] argTypes = new Class<?>[args.length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Class<?> argType = args[i].getClass();
|
||||
if (argType == Boolean.class) argType = Boolean.TYPE;
|
||||
else if (argType == Byte.class) argType = Byte.TYPE;
|
||||
else if (argType == Character.class) argType = Character.TYPE;
|
||||
else if (argType == Double.class) argType = Double.TYPE;
|
||||
else if (argType == Float.class) argType = Float.TYPE;
|
||||
else if (argType == Integer.class) argType = Integer.TYPE;
|
||||
else if (argType == Long.class) argType = Long.TYPE;
|
||||
else if (argType == Short.class) argType = Short.TYPE;
|
||||
argTypes[i] = argType;
|
||||
}
|
||||
final Method method;
|
||||
try {
|
||||
method = cls.getDeclaredMethod(methodName, argTypes);
|
||||
} catch (final NoSuchMethodException e) {
|
||||
throw new UnsupportedOperationException("Cannot find method", e);
|
||||
}
|
||||
|
||||
if (QUEUED_CALLS.size() == 0 && isRunning()) {
|
||||
invokeMethod(method, obj, args);
|
||||
return;
|
||||
}
|
||||
QUEUED_CALLS.add(new QueuedCall(method, obj, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given static method until Gecko is in RUNNING state.
|
||||
*
|
||||
* @param cls Class that declares the static method.
|
||||
* @param methodName Name of the static method.
|
||||
* @param args Args to call the static method with.
|
||||
*/
|
||||
public static void queueNativeCall(final Class<?> cls, final String methodName,
|
||||
final Object... args) {
|
||||
synchronized (QUEUED_CALLS) {
|
||||
queueNativeCallLocked(cls, methodName, null, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a call to the given instance method until Gecko is in RUNNING state.
|
||||
*
|
||||
* @param obj Object that declares the instance method.
|
||||
* @param methodName Name of the instance method.
|
||||
* @param args Args to call the instance method with.
|
||||
*/
|
||||
public static void queueNativeCall(final Object obj, final String methodName,
|
||||
final Object... args) {
|
||||
synchronized (QUEUED_CALLS) {
|
||||
queueNativeCallLocked(obj.getClass(), methodName, obj, args);
|
||||
}
|
||||
}
|
||||
|
||||
// Run all queued methods
|
||||
private static void flushQueuedNativeCalls(final State state) {
|
||||
if (!state.is(State.RUNNING)) {
|
||||
return;
|
||||
}
|
||||
synchronized (QUEUED_CALLS) {
|
||||
for (QueuedCall call : QUEUED_CALLS) {
|
||||
invokeMethod(call.method, call.target, call.args);
|
||||
}
|
||||
QUEUED_CALLS.clear();
|
||||
QUEUED_CALLS.trimToSize();
|
||||
}
|
||||
}
|
||||
|
||||
private String initGeckoEnvironment() {
|
||||
final Locale locale = Locale.getDefault();
|
||||
|
||||
|
@ -312,12 +413,14 @@ public class GeckoThread extends Thread implements GeckoEventListener {
|
|||
private static void setState(final State newState) {
|
||||
ThreadUtils.assertOnGeckoThread();
|
||||
sState.set(newState);
|
||||
flushQueuedNativeCalls(newState);
|
||||
}
|
||||
|
||||
private static boolean checkAndSetState(final State currentState, final State newState) {
|
||||
if (!sState.compareAndSet(currentState, newState)) {
|
||||
return false;
|
||||
}
|
||||
flushQueuedNativeCalls(newState);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче