api: threads, events

This commit is contained in:
hyb1996 2017-12-28 21:21:41 +08:00
parent 729958d419
commit 3647cef81b
13 changed files with 394 additions and 34 deletions

View File

@ -0,0 +1,23 @@
auto();
threads.start(function(){
//在子线程中调用observeKey()从而使按键事件处理在子线程执行
events.observeKey();
events.on("key_down", function(keyCode, events){
//音量键关闭脚本
if(keyCode == keys.volume_up){
exit();
}
});
});
toast("音量上键关闭脚本");
events.on("exit", function(){
toast("脚本已结束");
});
while(true){
log("脚本运行中...");
sleep(2000);
}

View File

@ -88,6 +88,13 @@ public class EditActivity extends BaseActivity implements OnActivityResultDelega
super.onActionModeStarted(mode);
}
@Override
public void onBackPressed() {
if (!mEditor.onBackPressed()) {
super.onBackPressed();
}
}
@Override
public void finish() {
if (mEditor.isTextChanged()) {

View File

@ -91,7 +91,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
FunctionsKeyboardView mFunctionsKeyboard;
@ViewById(R.id.docs)
EWebView mEWebView;
EWebView mDocsWebView;
@ViewById(R.id.drawer_layout)
DrawerLayout mDrawerLayout;
@ -201,8 +201,8 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
setUpInputMethodEnhancedBar();
setUpFunctionsKeyboard();
setMenuItemStatus(R.id.save, false);
mEWebView.getWebView().getSettings().setDisplayZoomControls(true);
mEWebView.getWebView().loadUrl(Pref.getDocumentationUrl() + "index.html");
mDocsWebView.getWebView().getSettings().setDisplayZoomControls(true);
mDocsWebView.getWebView().loadUrl(Pref.getDocumentationUrl() + "index.html");
}
@ -257,6 +257,18 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
mShowFunctionsButton.setColorFilter(textColor);
}
public boolean onBackPressed() {
if (mDrawerLayout.isDrawerOpen(Gravity.START)) {
if (mDocsWebView.getWebView().canGoBack()) {
mDocsWebView.getWebView().goBack();
} else {
mDrawerLayout.closeDrawer(Gravity.START);
}
return true;
}
return false;
}
@Click(R.id.run)
public void runAndSaveFileIfNeeded() {
@ -414,7 +426,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
.title(title)
.url(absUrl)
.pinToLeft(v -> {
mEWebView.getWebView().loadUrl(absUrl);
mDocsWebView.getWebView().loadUrl(absUrl);
mDrawerLayout.openDrawer(Gravity.START);
})
.show();

View File

@ -49,7 +49,6 @@ var __that__ = this;
var Promise = require('promise.js');
var JSON = require('__json2__.js');
var util = require('__util__.js');
var threads = __runtime__.threads;
var device = __runtime__.device;
var __asGlobal__ = function(obj, functions){
@ -65,7 +64,7 @@ require("__general__")(__runtime__, this);
(function(scope){
var modules = ['app', 'automator', 'console', 'dialogs', 'io', 'selector', 'shell', 'web', 'ui',
"images", "timers", "events", "engines", "RootAutomator", "http", "storages", "floaty"];
"images", "timers", "threads", "events", "engines", "RootAutomator", "http", "storages", "floaty"];
var len = modules.length;
for(var i = 0; i < len; i++) {
var m = modules[i];

View File

@ -0,0 +1,12 @@
module.exports = function(__runtime__, scope){
var threads = Object.create(__runtime__.threads);
scope.sync = function(func, lock){
lock = lock || null;
return new org.mozilla.javascript.Synchronizer(func, lock);
}
return threads;
}

View File

@ -3,6 +3,7 @@ package com.stardust.autojs.core.eventloop;
import android.support.annotation.NonNull;
import com.stardust.autojs.core.looper.Timer;
import com.stardust.autojs.runtime.ScriptBridges;
import com.stardust.autojs.runtime.exception.ScriptException;
@ -19,6 +20,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class EventEmitter {
private static class ListenerWrapper {
Object listener;
boolean isOnce;
@ -52,7 +54,11 @@ public class EventEmitter {
Iterator<ListenerWrapper> listenerIterator = mListenerWrappers.iterator();
while (listenerIterator.hasNext()) {
ListenerWrapper listenerWrapper = listenerIterator.next();
mBridges.callFunction(listenerWrapper.listener, EventEmitter.this, args);
if (mTimer != null) {
mTimer.setImmediate(listenerWrapper.listener, args);
} else {
mBridges.callFunction(listenerWrapper.listener, EventEmitter.this, args);
}
if (listenerWrapper.isOnce) {
listenerIterator.remove();
}
@ -93,11 +99,17 @@ public class EventEmitter {
public static int defaultMaxListeners = 10;
private int mMaxListeners = defaultMaxListeners;
protected ScriptBridges mBridges;
private Timer mTimer;
public EventEmitter(ScriptBridges bridges) {
mBridges = bridges;
}
public EventEmitter(ScriptBridges bridges, Timer timer) {
mTimer = timer;
mBridges = bridges;
}
public EventEmitter once(String eventName, Object listener) {
getListeners(eventName).add(listener, true);
return this;

View File

@ -0,0 +1,225 @@
package com.stardust.autojs.core.looper;
import android.content.pm.PackageManager;
import com.stardust.autojs.runtime.ScriptRuntime;
import java.util.Map;
/**
* Created by Stardust on 2017/12/28.
*/
public class MainThreadProxy {
private final Thread mThread;
private ScriptRuntime mRuntime;
public MainThreadProxy(Thread thread, ScriptRuntime runtime) {
mThread = thread;
mRuntime = runtime;
}
public int setTimeout(Object callback, long delay, Object... args) {
return getMainTimer().setTimeout(callback, delay, args);
}
private Timer getMainTimer() {
return mRuntime.timers.getMainTimer();
}
public boolean clearTimeout(int id) {
return getMainTimer().clearTimeout(id);
}
public int setInterval(Object listener, long interval, Object... args) {
return getMainTimer().setInterval(listener, interval, args);
}
public boolean clearInterval(int id) {
return getMainTimer().clearInterval(id);
}
public int setImmediate(Object listener, Object... args) {
return getMainTimer().setImmediate(listener, args);
}
public boolean clearImmediate(int id) {
return getMainTimer().clearImmediate(id);
}
public static Thread currentThread() {
return Thread.currentThread();
}
public static void yield() {
Thread.yield();
}
public static void sleep(long millis) throws InterruptedException {
Thread.sleep(millis);
}
public static void sleep(long millis, int nanos) throws InterruptedException {
Thread.sleep(millis, nanos);
}
public void start() {
mThread.start();
}
public void run() {
mThread.run();
}
@Deprecated
public void stop() {
mThread.stop();
}
@Deprecated
public void stop(Throwable obj) {
mThread.stop(obj);
}
public void interrupt() {
mThread.interrupt();
}
public static boolean interrupted() {
return Thread.interrupted();
}
public boolean isInterrupted() {
return mThread.isInterrupted();
}
@Deprecated
public void destroy() {
mThread.destroy();
}
public boolean isAlive() {
return mThread.isAlive();
}
@Deprecated
public void suspend() {
mThread.suspend();
}
@Deprecated
public void resume() {
mThread.resume();
}
public void setPriority(int newPriority) {
mThread.setPriority(newPriority);
}
public int getPriority() {
return mThread.getPriority();
}
public void setName(String name) {
mThread.setName(name);
}
public String getName() {
return mThread.getName();
}
public ThreadGroup getThreadGroup() {
return mThread.getThreadGroup();
}
public static int activeCount() {
return Thread.activeCount();
}
public static int enumerate(Thread[] tarray) {
return Thread.enumerate(tarray);
}
@Deprecated
public int countStackFrames() {
return mThread.countStackFrames();
}
public void join(long millis) throws InterruptedException {
mThread.join(millis);
}
public void join(long millis, int nanos) throws InterruptedException {
mThread.join(millis, nanos);
}
public void join() throws InterruptedException {
mThread.join();
}
public static void dumpStack() {
Thread.dumpStack();
}
public void setDaemon(boolean on) {
mThread.setDaemon(on);
}
public boolean isDaemon() {
return mThread.isDaemon();
}
public void checkAccess() {
mThread.checkAccess();
}
public ClassLoader getContextClassLoader() {
return mThread.getContextClassLoader();
}
public void setContextClassLoader(ClassLoader cl) {
mThread.setContextClassLoader(cl);
}
public static boolean holdsLock(Object obj) {
return Thread.holdsLock(obj);
}
public StackTraceElement[] getStackTrace() {
return mThread.getStackTrace();
}
public static Map<Thread, StackTraceElement[]> getAllStackTraces() {
return Thread.getAllStackTraces();
}
public long getId() {
return mThread.getId();
}
public Thread.State getState() {
return mThread.getState();
}
@Override
public String toString() {
return mThread.toString();
}
public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) {
Thread.setDefaultUncaughtExceptionHandler(eh);
}
public static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() {
return Thread.getDefaultUncaughtExceptionHandler();
}
public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
return mThread.getUncaughtExceptionHandler();
}
public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) {
mThread.setUncaughtExceptionHandler(eh);
}
}

View File

@ -109,4 +109,5 @@ public class Timer {
Log.d(LOG_TAG, "mMaxCallbackUptimeMillisForAllThreads:" + mMaxCallbackUptimeMillis);
return mMaxCallbackUptimeMillis > SystemClock.uptimeMillis();
}
}

View File

@ -11,6 +11,8 @@ import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.concurrent.VolatileBox;
import com.stardust.lang.ThreadCompat;
import org.mozilla.javascript.NativeArray;
import java.util.concurrent.ConcurrentHashMap;
/**
@ -25,6 +27,8 @@ public class TimerThread extends ThreadCompat {
private final VolatileBox<Long> mMaxCallbackUptimeMillisForAllThreads;
private final ScriptRuntime mRuntime;
private Runnable mTarget;
private boolean mRunning = false;
private final Object mRunningLock = new Object();
public TimerThread(ScriptRuntime runtime, VolatileBox<Long> maxCallbackUptimeMillisForAllThreads, Runnable target) {
super(target);
@ -38,6 +42,7 @@ public class TimerThread extends ThreadCompat {
mRuntime.loopers.prepare();
mTimer = new Timer(mRuntime.bridges, mMaxCallbackUptimeMillisForAllThreads);
sTimerMap.put(Thread.currentThread(), mTimer);
notifyRunning();
new Handler().post(mTarget);
try {
Looper.loop();
@ -47,10 +52,18 @@ public class TimerThread extends ThreadCompat {
}
} finally {
onExit();
mTimer = null;
sTimerMap.remove(Thread.currentThread(), mTimer);
}
}
private void notifyRunning() {
synchronized (mRunningLock) {
mRunning = true;
mRunningLock.notifyAll();
}
}
@CallSuper
protected void onExit() {
mRuntime.loopers.notifyThreadExit(this);
@ -65,26 +78,46 @@ public class TimerThread extends ThreadCompat {
}
public int setTimeout(Object callback, long delay, Object... args) {
return mTimer.setTimeout(callback, delay, args);
return getTimer().setTimeout(callback, delay, args);
}
private Timer getTimer() {
if (mTimer == null) {
throw new IllegalStateException("thread is not alive");
}
return mTimer;
}
public boolean clearTimeout(int id) {
return mTimer.clearTimeout(id);
return getTimer().clearTimeout(id);
}
public int setInterval(Object listener, long interval, Object... args) {
return mTimer.setInterval(listener, interval, args);
return getTimer().setInterval(listener, interval, args);
}
public boolean clearInterval(int id) {
return mTimer.clearInterval(id);
return getTimer().clearInterval(id);
}
public int setImmediate(Object listener, Object... args) {
return mTimer.setImmediate(listener, args);
return getTimer().setImmediate(listener, args);
}
public boolean clearImmediate(int id) {
return mTimer.clearImmediate(id);
return getTimer().clearImmediate(id);
}
public void waitFor() throws InterruptedException {
synchronized (mRunningLock) {
if (mRunning)
return;
mRunningLock.wait();
}
}
@Override
public String toString() {
return "Thread[" + getName() + "," + getPriority() + "]";
}
}

View File

@ -153,7 +153,7 @@ public class ScriptRuntime {
public final Engines engines;
@ScriptVariable
public final Threads threads;
public Threads threads;
@ScriptVariable
public final Floaty floaty;
@ -187,7 +187,6 @@ public class ScriptRuntime {
}
engines = new Engines(builder.mEngineService);
dialogs = new Dialogs(app, uiHandler, bridges);
threads = new Threads(this);
device = new Device(uiHandler.getContext());
floaty = new Floaty(uiHandler, ui);
}
@ -195,7 +194,8 @@ public class ScriptRuntime {
public void init() {
if (loopers != null)
throw new IllegalStateException("already initialized");
timers = new Timers(bridges);
threads = new Threads(this);
timers = new Timers(bridges, threads);
loopers = new Loopers(this);
events = new Events(uiHandler.getContext(), accessibilityBridge, this);
mThread = Thread.currentThread();

View File

@ -14,6 +14,8 @@ import com.stardust.autojs.R;
import com.stardust.autojs.core.accessibility.AccessibilityBridge;
import com.stardust.autojs.core.eventloop.EventEmitter;
import com.stardust.autojs.core.looper.Loopers;
import com.stardust.autojs.core.looper.MainThreadProxy;
import com.stardust.autojs.core.looper.Timer;
import com.stardust.autojs.runtime.ScriptRuntime;
import com.stardust.notification.Notification;
import com.stardust.notification.NotificationListenerService;
@ -58,6 +60,15 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
return new EventEmitter(mBridges);
}
public EventEmitter emitter(Thread thread) {
Timer timer = mScriptRuntime.timers.getTimerForThread(thread);
return new EventEmitter(mBridges, timer);
}
public EventEmitter emitter(MainThreadProxy mainThreadProxy) {
return new EventEmitter(mBridges, mScriptRuntime.timers.getMainTimer());
}
public void observeKey() {
if (mListeningKey)
return;

View File

@ -2,6 +2,7 @@ package com.stardust.autojs.runtime.api;
import android.support.annotation.NonNull;
import com.stardust.autojs.core.looper.MainThreadProxy;
import com.stardust.autojs.core.looper.TimerThread;
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
import com.stardust.autojs.runtime.ScriptRuntime;
@ -14,10 +15,15 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by Stardust on 2017/12/3.
@ -27,15 +33,33 @@ public class Threads {
private final HashSet<Thread> mThreads = new HashSet<>();
private ScriptRuntime mRuntime;
private final Thread mMainThread;
private MainThreadProxy mMainThreadProxy;
private int mSpawnCount = 0;
public Threads(ScriptRuntime runtime) {
mRuntime = runtime;
mMainThread = Thread.currentThread();
mMainThreadProxy = new MainThreadProxy(Thread.currentThread(), mRuntime);
}
public Thread getMainThread() {
return mMainThread;
}
public Object currentThread() {
Thread thread = Thread.currentThread();
if (thread == mMainThread)
return mMainThreadProxy;
return thread;
}
public TimerThread start(Runnable runnable) {
TimerThread thread = startThread(runnable);
synchronized (mThreads) {
mThreads.add(thread);
thread.setName(thread.getName() + " (Spawn-" + mSpawnCount + ")");
mSpawnCount++;
}
thread.start();
return thread;
@ -63,24 +87,16 @@ public class Threads {
return new VolatileDispose();
}
public List list() {
return Collections.synchronizedList(new ArrayList<>());
}
public Set set() {
return new ConcurrentSkipListSet();
}
public Map map() {
return new ConcurrentHashMap();
public AtomicLong atomic(long value) {
return new AtomicLong(value);
}
public AtomicLong atomic() {
return new AtomicLong();
}
public AtomicLong atomic(long value) {
return new AtomicLong(value);
public Lock lock() {
return new ReentrantLock();
}
public void shutDownAll() {

View File

@ -19,12 +19,17 @@ public class Timers {
private static final String LOG_TAG = "Timers";
private VolatileBox<Long> mMaxCallbackUptimeMillisForAllThreads = new VolatileBox<>(0L);
private Thread mMainThread;
private Threads mThreads;
private Timer mMainTimer;
public Timers(ScriptBridges bridges) {
mMainThread = Thread.currentThread();
public Timers(ScriptBridges bridges, Threads threads) {
mMainTimer = new Timer(bridges, mMaxCallbackUptimeMillisForAllThreads);
mThreads = threads;
}
public Timer getMainTimer() {
return mMainTimer;
}
public VolatileBox<Long> getMaxCallbackUptimeMillisForAllThreads() {
@ -32,10 +37,14 @@ public class Timers {
}
private Timer getTimerForCurrentThread() {
if (Thread.currentThread() == mMainThread) {
return getTimerForThread(Thread.currentThread());
}
public Timer getTimerForThread(Thread thread) {
if (thread == mThreads.getMainThread()) {
return mMainTimer;
}
return TimerThread.getTimerForCurrentThread();
return TimerThread.getTimerForThread(thread);
}
public int setTimeout(Object callback, long delay, Object... args) {
@ -64,7 +73,7 @@ public class Timers {
public boolean hasPendingCallbacks() {
//如果是脚本主线程则检查所有子线程中的定时回调mFutureCallbackUptimeMillis用来记录所有子线程中定时最久的一个
if (mMainThread == Thread.currentThread()) {
if (mThreads.getMainThread() == Thread.currentThread()) {
Log.d(LOG_TAG, "[main thread]hasPendingCallbacks:" + (mMaxCallbackUptimeMillisForAllThreads.get() > SystemClock.uptimeMillis()));
Log.d(LOG_TAG, "mMaxCallbackUptimeMillisForAllThreads:" + mMaxCallbackUptimeMillisForAllThreads.get());
return mMaxCallbackUptimeMillisForAllThreads.get() > SystemClock.uptimeMillis();