api: threads, files
This commit is contained in:
@@ -1,7 +1,18 @@
|
||||
|
||||
module.exports = function(__runtime__, scope){
|
||||
importClass(android.view.KeyEvent);
|
||||
var events = Object.create(__runtime__.events);
|
||||
|
||||
var keys = {
|
||||
"home": KeyEvent.KEYCODE_HOME,
|
||||
"menu": KeyEvent.KEYCODE_MENU,
|
||||
"back": KeyEvent.KEYCODE_BACK,
|
||||
"volume_up": KeyEvent.KEYCODE_VOLUME_UP,
|
||||
"volume_down": KeyEvent.KEYCODE_VOLUME_DOWN
|
||||
}
|
||||
|
||||
scope.keys = keys;
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
|
||||
module.exports = function(__runtime__, scope){
|
||||
var files = com.stardust.pio.PFiles;
|
||||
var files = Object.create(com.stardust.pio.PFiles);
|
||||
files.cwd = function(){
|
||||
return scope.engines.myEngine().cwd();
|
||||
}
|
||||
scope.files = files;
|
||||
scope.open = function(path, mode, encoding, bufferSize){
|
||||
if(arguments.length == 1){
|
||||
|
||||
@@ -24,6 +24,8 @@ import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/5/2.
|
||||
*
|
||||
* TODO: 优化为无锁形式
|
||||
*/
|
||||
public class ConsoleView extends FrameLayout implements StardustConsole.LogListener {
|
||||
|
||||
@@ -77,12 +79,9 @@ public class ConsoleView extends FrameLayout implements StardustConsole.LogListe
|
||||
|
||||
private void initSubmitButton() {
|
||||
final Button submit = (Button) findViewById(R.id.submit);
|
||||
submit.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
CharSequence input = mEditText.getText();
|
||||
submitInput(input);
|
||||
}
|
||||
submit.setOnClickListener(v -> {
|
||||
CharSequence input = mEditText.getText();
|
||||
submitInput(input);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -99,13 +98,10 @@ public class ConsoleView extends FrameLayout implements StardustConsole.LogListe
|
||||
mEditText = (EditText) findViewById(R.id.input);
|
||||
mEditText.setFocusableInTouchMode(true);
|
||||
mInputContainer = (LinearLayout) findViewById(R.id.input_container);
|
||||
OnClickListener listener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mWindow != null) {
|
||||
mWindow.requestWindowFocus();
|
||||
mEditText.requestFocus();
|
||||
}
|
||||
OnClickListener listener = v -> {
|
||||
if (mWindow != null) {
|
||||
mWindow.requestWindowFocus();
|
||||
mEditText.requestFocus();
|
||||
}
|
||||
};
|
||||
mEditText.setOnClickListener(listener);
|
||||
@@ -146,12 +142,9 @@ public class ConsoleView extends FrameLayout implements StardustConsole.LogListe
|
||||
|
||||
@Override
|
||||
public void onLogClear() {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mLogs.clear();
|
||||
mLogListRecyclerView.getAdapter().notifyDataSetChanged();
|
||||
}
|
||||
post(() -> {
|
||||
mLogs.clear();
|
||||
mLogListRecyclerView.getAdapter().notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -160,22 +153,24 @@ public class ConsoleView extends FrameLayout implements StardustConsole.LogListe
|
||||
return;
|
||||
int oldSize = mLogs.size();
|
||||
ArrayList<StardustConsole.Log> logs = mConsole.getAllLogs();
|
||||
final int size = logs.size();
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
if (oldSize >= size) {
|
||||
return;
|
||||
}
|
||||
if (oldSize == 0) {
|
||||
mLogs.addAll(logs);
|
||||
} else {
|
||||
for (int i = oldSize; i < size; i++) {
|
||||
mLogs.add(logs.get(i));
|
||||
synchronized (logs) {
|
||||
final int size = logs.size();
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
if (oldSize >= size) {
|
||||
return;
|
||||
}
|
||||
if (oldSize == 0) {
|
||||
mLogs.addAll(logs);
|
||||
} else {
|
||||
for (int i = oldSize; i < size; i++) {
|
||||
mLogs.add(logs.get(i));
|
||||
}
|
||||
}
|
||||
mLogListRecyclerView.getAdapter().notifyItemRangeInserted(oldSize, size - 1);
|
||||
mLogListRecyclerView.scrollToPosition(size - 1);
|
||||
}
|
||||
mLogListRecyclerView.getAdapter().notifyItemRangeInserted(oldSize, size - 1);
|
||||
mLogListRecyclerView.scrollToPosition(size - 1);
|
||||
}
|
||||
|
||||
public void setWindow(ResizableExpandableFloatyWindow window) {
|
||||
@@ -183,13 +178,10 @@ public class ConsoleView extends FrameLayout implements StardustConsole.LogListe
|
||||
}
|
||||
|
||||
public void showEditText() {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mWindow.requestWindowFocus();
|
||||
mInputContainer.setVisibility(VISIBLE);
|
||||
mEditText.requestFocus();
|
||||
}
|
||||
post(() -> {
|
||||
mWindow.requestWindowFocus();
|
||||
mInputContainer.setVisibility(VISIBLE);
|
||||
mEditText.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,9 @@ public class StardustConsole extends AbstractConsole {
|
||||
@Override
|
||||
public String println(int level, CharSequence charSequence) {
|
||||
Log log = new Log(mIdCounter.getAndIncrement(), level, charSequence, true);
|
||||
mLogs.add(log);
|
||||
synchronized (mLogs) {
|
||||
mLogs.add(log);
|
||||
}
|
||||
if (mGlobalConsole != null) {
|
||||
mGlobalConsole.println(level, charSequence);
|
||||
}
|
||||
@@ -135,20 +137,15 @@ public class StardustConsole extends AbstractConsole {
|
||||
|
||||
@Override
|
||||
public void write(int level, CharSequence charSequence) {
|
||||
Log log = new Log(mIdCounter.getAndIncrement(), level, charSequence);
|
||||
mLogs.add(log);
|
||||
if (mGlobalConsole != null) {
|
||||
mGlobalConsole.print(level, charSequence);
|
||||
}
|
||||
if (mLogListener != null && mLogListener.get() != null) {
|
||||
mLogListener.get().onNewLog(log);
|
||||
}
|
||||
println(level, charSequence);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
mLogs.clear();
|
||||
synchronized (mLogs) {
|
||||
mLogs.clear();
|
||||
}
|
||||
if (mLogListener != null && mLogListener.get() != null) {
|
||||
mLogListener.get().onLogClear();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.stardust.autojs.core.looper;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
|
||||
public class LooperHelper {
|
||||
|
||||
private static volatile ConcurrentHashMap<Thread, Looper> sLoopers = new ConcurrentHashMap<>();
|
||||
|
||||
public static void prepare() {
|
||||
if (Looper.myLooper() == Looper.getMainLooper())
|
||||
return;
|
||||
if (Looper.myLooper() == null)
|
||||
Looper.prepare();
|
||||
Looper l = Looper.myLooper();
|
||||
if (l != null)
|
||||
sLoopers.put(Thread.currentThread(), l);
|
||||
}
|
||||
|
||||
public static void quitForThread(Thread thread) {
|
||||
Looper looper = sLoopers.remove(thread);
|
||||
if (looper != null)
|
||||
looper.quit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.stardust.autojs.core.looper;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||
import com.stardust.autojs.runtime.api.Threads;
|
||||
import com.stardust.autojs.runtime.api.Timers;
|
||||
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
|
||||
import com.stardust.lang.ThreadCompat;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/29.
|
||||
*/
|
||||
|
||||
public class Loopers {
|
||||
|
||||
public interface LooperQuitHandler {
|
||||
boolean shouldQuit();
|
||||
}
|
||||
|
||||
private static final Runnable EMPTY_RUNNABLE = () -> {
|
||||
};
|
||||
|
||||
private volatile ThreadLocal<Boolean> waitWhenIdle = new ThreadLocal<>();
|
||||
private volatile Looper mServantLooper;
|
||||
private Timers mTimers;
|
||||
private ScriptRuntime mScriptRuntime;
|
||||
private LooperQuitHandler mMainLooperQuitHandler;
|
||||
private Handler mMainHandler;
|
||||
private Looper mMainLooper;
|
||||
private Threads mThreads;
|
||||
|
||||
public Loopers(ScriptRuntime runtime) {
|
||||
mTimers = runtime.timers;
|
||||
mThreads = runtime.threads;
|
||||
mScriptRuntime = runtime;
|
||||
prepare();
|
||||
mMainLooper = Looper.myLooper();
|
||||
mMainHandler = new Handler();
|
||||
}
|
||||
|
||||
|
||||
public Looper getMainLooper() {
|
||||
return mMainLooper;
|
||||
}
|
||||
|
||||
private boolean shouldQuitLooper() {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
return true;
|
||||
}
|
||||
if (mTimers.hasPendingCallbacks()) {
|
||||
return false;
|
||||
}
|
||||
return !waitWhenIdle.get();
|
||||
}
|
||||
|
||||
|
||||
private void initServantThread() {
|
||||
new ThreadCompat(() -> {
|
||||
Looper.prepare();
|
||||
final Object lock = Loopers.this;
|
||||
mServantLooper = Looper.myLooper();
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
Looper.loop();
|
||||
}).start();
|
||||
}
|
||||
|
||||
public Looper getServantLooper() {
|
||||
if (mServantLooper == null) {
|
||||
initServantThread();
|
||||
synchronized (this) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new ScriptInterruptedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mServantLooper;
|
||||
}
|
||||
|
||||
public void quitServantLooper() {
|
||||
if (mServantLooper == null)
|
||||
return;
|
||||
mServantLooper.quit();
|
||||
}
|
||||
|
||||
public void waitWhenIdle(boolean b) {
|
||||
waitWhenIdle.set(b);
|
||||
}
|
||||
|
||||
public void quitAll() {
|
||||
quitServantLooper();
|
||||
}
|
||||
|
||||
public void setMainLooperQuitHandler(LooperQuitHandler mainLooperQuitHandler) {
|
||||
mMainLooperQuitHandler = mainLooperQuitHandler;
|
||||
}
|
||||
|
||||
public void prepare() {
|
||||
if (Looper.myLooper() == null)
|
||||
Looper.prepare();
|
||||
Looper.myQueue().addIdleHandler(() -> {
|
||||
Looper l = Looper.myLooper();
|
||||
if (l == null)
|
||||
return true;
|
||||
if (l == mMainLooper) {
|
||||
if (shouldQuitLooper() && !mThreads.hasRunningThreads() &&
|
||||
mMainLooperQuitHandler != null && mMainLooperQuitHandler.shouldQuit()) {
|
||||
l.quit();
|
||||
}
|
||||
} else {
|
||||
if (shouldQuitLooper()) {
|
||||
l.quit();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
waitWhenIdle.set(Looper.myLooper() == Looper.getMainLooper());
|
||||
}
|
||||
|
||||
public void notifyThreadExit(TimerThread thread) {
|
||||
//当子线程退成时,主线程需要检查自身是否退出(主线程在所有子线程执行完成后才能退出,如果主线程已经执行完任务仍然要等待所有子线程),
|
||||
//此时通过向主线程发送一个空的Runnable,主线程执行完这个Runnable后会触发IdleHandler,从而检查自身是否退出
|
||||
mMainHandler.post(EMPTY_RUNNABLE);
|
||||
}
|
||||
}
|
||||
112
autojs/src/main/java/com/stardust/autojs/core/looper/Timer.java
Normal file
112
autojs/src/main/java/com/stardust/autojs/core/looper/Timer.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package com.stardust.autojs.core.looper;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.stardust.autojs.runtime.ScriptBridges;
|
||||
import com.stardust.concurrent.VolatileBox;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
|
||||
public class Timer {
|
||||
|
||||
private static final String LOG_TAG = "Timer";
|
||||
|
||||
private SparseArray<Runnable> mHandlerCallbacks = new SparseArray<>();
|
||||
private int mCallbackMaxId = 0;
|
||||
private ScriptBridges mBridges;
|
||||
private Handler mHandler;
|
||||
private long mMaxCallbackUptimeMillis = 0;
|
||||
private final VolatileBox<Long> mMaxCallbackMillisForAllThread;
|
||||
|
||||
public Timer(ScriptBridges bridges, VolatileBox<Long> maxCallbackMillisForAllThread) {
|
||||
mBridges = bridges;
|
||||
mMaxCallbackMillisForAllThread = maxCallbackMillisForAllThread;
|
||||
mHandler = new Handler();
|
||||
Log.d(LOG_TAG, "Timer: handler = " + mHandler + ", thread = " + Thread.currentThread());
|
||||
}
|
||||
|
||||
public int setTimeout(final Object callback, final long delay, final Object... args) {
|
||||
Log.d(LOG_TAG, "setTimeout: handler = " + mHandler);
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
Runnable r = () -> {
|
||||
Log.d(LOG_TAG, "callFunction: handler = " + mHandler);
|
||||
mBridges.callFunction(callback, null, args);
|
||||
mHandlerCallbacks.remove(id);
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, delay);
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean clearTimeout(int id) {
|
||||
return clearCallback(id);
|
||||
}
|
||||
|
||||
public int setInterval(final Object listener, final long interval, final Object... args) {
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
final Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mHandlerCallbacks.get(id) == null)
|
||||
return;
|
||||
mBridges.callFunction(listener, null, args);
|
||||
postDelayed(this, interval);
|
||||
}
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, interval);
|
||||
return id;
|
||||
}
|
||||
|
||||
private void postDelayed(Runnable r, long interval) {
|
||||
long uptime = SystemClock.uptimeMillis() + interval;
|
||||
mHandler.postAtTime(r, uptime);
|
||||
mMaxCallbackUptimeMillis = Math.max(mMaxCallbackUptimeMillis, uptime);
|
||||
synchronized (mMaxCallbackMillisForAllThread) {
|
||||
mMaxCallbackMillisForAllThread.set(Math.max(mMaxCallbackMillisForAllThread.get(), uptime));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean clearInterval(int id) {
|
||||
return clearCallback(id);
|
||||
}
|
||||
|
||||
public int setImmediate(final Object listener, final Object... args) {
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
Runnable r = () -> {
|
||||
mBridges.callFunction(listener, null, args);
|
||||
mHandlerCallbacks.remove(id);
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, 0);
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean clearImmediate(int id) {
|
||||
return clearCallback(id);
|
||||
}
|
||||
|
||||
private boolean clearCallback(int id) {
|
||||
Runnable callback = mHandlerCallbacks.get(id);
|
||||
if (callback != null) {
|
||||
mHandler.removeCallbacks(callback);
|
||||
mHandlerCallbacks.remove(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasPendingCallbacks() {
|
||||
Log.d(LOG_TAG, "[thread]hasPendingCallbacks:" + (mMaxCallbackUptimeMillis > SystemClock.uptimeMillis()));
|
||||
Log.d(LOG_TAG, "mMaxCallbackUptimeMillisForAllThreads:" + mMaxCallbackUptimeMillis);
|
||||
return mMaxCallbackUptimeMillis > SystemClock.uptimeMillis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.stardust.autojs.core.looper;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.CallSuper;
|
||||
|
||||
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
|
||||
import com.stardust.autojs.runtime.ScriptBridges;
|
||||
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
|
||||
import com.stardust.concurrent.VolatileBox;
|
||||
import com.stardust.lang.ThreadCompat;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
|
||||
public class TimerThread extends ThreadCompat {
|
||||
|
||||
private static ConcurrentHashMap<Thread, Timer> sTimerMap = new ConcurrentHashMap<>();
|
||||
|
||||
private Timer mTimer;
|
||||
private final VolatileBox<Long> mMaxCallbackUptimeMillisForAllThreads;
|
||||
private final ScriptRuntime mRuntime;
|
||||
private Runnable mTarget;
|
||||
|
||||
public TimerThread(ScriptRuntime runtime, VolatileBox<Long> maxCallbackUptimeMillisForAllThreads, Runnable target) {
|
||||
super(target);
|
||||
mRuntime = runtime;
|
||||
mTarget = target;
|
||||
mMaxCallbackUptimeMillisForAllThreads = maxCallbackUptimeMillisForAllThreads;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mRuntime.loopers.prepare();
|
||||
mTimer = new Timer(mRuntime.bridges, mMaxCallbackUptimeMillisForAllThreads);
|
||||
sTimerMap.put(Thread.currentThread(), mTimer);
|
||||
new Handler().post(mTarget);
|
||||
try {
|
||||
Looper.loop();
|
||||
} catch (Exception e) {
|
||||
if (!ScriptInterruptedException.causedByInterrupted(e)) {
|
||||
mRuntime.console.error(Thread.currentThread().toString() + ": " + e);
|
||||
}
|
||||
} finally {
|
||||
onExit();
|
||||
sTimerMap.remove(Thread.currentThread(), mTimer);
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected void onExit() {
|
||||
mRuntime.loopers.notifyThreadExit(this);
|
||||
}
|
||||
|
||||
public static Timer getTimerForThread(Thread thread) {
|
||||
return sTimerMap.get(thread);
|
||||
}
|
||||
|
||||
public static Timer getTimerForCurrentThread() {
|
||||
return getTimerForThread(Thread.currentThread());
|
||||
}
|
||||
|
||||
public int setTimeout(Object callback, long delay, Object... args) {
|
||||
return mTimer.setTimeout(callback, delay, args);
|
||||
}
|
||||
|
||||
public boolean clearTimeout(int id) {
|
||||
return mTimer.clearTimeout(id);
|
||||
}
|
||||
|
||||
public int setInterval(Object listener, long interval, Object... args) {
|
||||
return mTimer.setInterval(listener, interval, args);
|
||||
}
|
||||
|
||||
public boolean clearInterval(int id) {
|
||||
return mTimer.clearInterval(id);
|
||||
}
|
||||
|
||||
public int setImmediate(Object listener, Object... args) {
|
||||
return mTimer.setImmediate(listener, args);
|
||||
}
|
||||
|
||||
public boolean clearImmediate(int id) {
|
||||
return mTimer.clearImmediate(id);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
|
||||
import com.stardust.autojs.runtime.api.Loopers;
|
||||
import com.stardust.autojs.core.looper.LooperHelper;
|
||||
import com.stardust.autojs.script.JavaScriptSource;
|
||||
import com.stardust.autojs.script.ScriptSource;
|
||||
import com.stardust.util.Callback;
|
||||
@@ -62,7 +62,7 @@ public class LoopBasedJavaScriptEngine extends RhinoJavaScriptEngine {
|
||||
|
||||
@Override
|
||||
public void forceStop() {
|
||||
Loopers.quitForThread(getThread());
|
||||
LooperHelper.quitForThread(getThread());
|
||||
super.forceStop();
|
||||
}
|
||||
|
||||
@@ -70,13 +70,13 @@ public class LoopBasedJavaScriptEngine extends RhinoJavaScriptEngine {
|
||||
public synchronized void destroy() {
|
||||
Thread thread = getThread();
|
||||
if (thread != null)
|
||||
Loopers.quitForThread(thread);
|
||||
LooperHelper.quitForThread(thread);
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
Loopers.prepare();
|
||||
LooperHelper.prepare();
|
||||
mHandler = new Handler();
|
||||
super.init();
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ public interface ScriptEngine<S extends ScriptSource> {
|
||||
|
||||
Object getTag(String key);
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -87,6 +86,10 @@ public interface ScriptEngine<S extends ScriptSource> {
|
||||
}
|
||||
}
|
||||
|
||||
public String cwd() {
|
||||
return (String) getTag(TAG_PATH);
|
||||
}
|
||||
|
||||
public void setOnDestroyListener(OnDestroyListener onDestroyListener) {
|
||||
if (mOnDestroyListener != null)
|
||||
throw new SecurityException("setOnDestroyListener can be called only once");
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package com.stardust.autojs.execution;
|
||||
|
||||
import android.os.MessageQueue;
|
||||
import android.util.Log;
|
||||
|
||||
import com.stardust.autojs.engine.LoopBasedJavaScriptEngine;
|
||||
import com.stardust.autojs.engine.ScriptEngine;
|
||||
import com.stardust.autojs.engine.ScriptEngineManager;
|
||||
import com.stardust.autojs.runtime.api.Loopers;
|
||||
import com.stardust.autojs.core.looper.Loopers;
|
||||
import com.stardust.autojs.script.JavaScriptSource;
|
||||
import com.stardust.autojs.script.ScriptSource;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/10/27.
|
||||
@@ -28,7 +24,7 @@ public class LoopedBasedJavaScriptExecution extends RunnableScriptExecution {
|
||||
sleep(delay);
|
||||
final LoopBasedJavaScriptEngine javaScriptEngine = (LoopBasedJavaScriptEngine) engine;
|
||||
final long interval = getConfig().interval;
|
||||
javaScriptEngine.getRuntime().loopers.setLooperQuitHandler(new Loopers.LooperQuitHandler() {
|
||||
javaScriptEngine.getRuntime().loopers.setMainLooperQuitHandler(new Loopers.LooperQuitHandler() {
|
||||
long times = getConfig().loopTimes == 0 ? Integer.MAX_VALUE : getConfig().loopTimes;
|
||||
|
||||
@Override
|
||||
@@ -39,7 +35,7 @@ public class LoopedBasedJavaScriptExecution extends RunnableScriptExecution {
|
||||
javaScriptEngine.execute(getSource());
|
||||
return false;
|
||||
}
|
||||
javaScriptEngine.getRuntime().loopers.setLooperQuitHandler(null);
|
||||
javaScriptEngine.getRuntime().loopers.setMainLooperQuitHandler(null);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ import com.stardust.autojs.runtime.api.Device;
|
||||
import com.stardust.autojs.runtime.api.Engines;
|
||||
import com.stardust.autojs.runtime.api.Events;
|
||||
import com.stardust.autojs.runtime.api.Floaty;
|
||||
import com.stardust.autojs.runtime.api.Loopers;
|
||||
import com.stardust.autojs.core.looper.Loopers;
|
||||
import com.stardust.autojs.runtime.api.Threads;
|
||||
import com.stardust.autojs.runtime.api.Timers;
|
||||
import com.stardust.autojs.core.accessibility.UiSelector;
|
||||
@@ -168,6 +168,7 @@ public class ScriptRuntime {
|
||||
private AbstractShell mRootShell;
|
||||
private Supplier<AbstractShell> mShellSupplier;
|
||||
private ScreenMetrics mScreenMetrics = new ScreenMetrics();
|
||||
private Thread mThread;
|
||||
|
||||
|
||||
protected ScriptRuntime(Builder builder) {
|
||||
@@ -197,6 +198,7 @@ public class ScriptRuntime {
|
||||
timers = new Timers(bridges);
|
||||
loopers = new Loopers(this);
|
||||
events = new Events(uiHandler.getContext(), accessibilityBridge, this);
|
||||
mThread = Thread.currentThread();
|
||||
}
|
||||
|
||||
public static void setApplicationContext(Context context) {
|
||||
@@ -248,12 +250,7 @@ public class ScriptRuntime {
|
||||
return ClipboardUtil.getClipOrEmpty(uiHandler.getContext()).toString();
|
||||
}
|
||||
final VolatileDispose<String> clip = new VolatileDispose<>();
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clip.setAndNotify(ClipboardUtil.getClipOrEmpty(uiHandler.getContext()).toString());
|
||||
}
|
||||
});
|
||||
uiHandler.post(() -> clip.setAndNotify(ClipboardUtil.getClipOrEmpty(uiHandler.getContext()).toString()));
|
||||
return clip.blockedGetOrThrow(ScriptInterruptedException.class);
|
||||
}
|
||||
|
||||
@@ -297,7 +294,7 @@ public class ScriptRuntime {
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
Thread.currentThread().interrupt();
|
||||
mThread.interrupt();
|
||||
throw new ScriptInterruptedException();
|
||||
}
|
||||
|
||||
@@ -320,6 +317,8 @@ public class ScriptRuntime {
|
||||
}
|
||||
|
||||
public void onExit() {
|
||||
//清除interrupt状态
|
||||
Thread.interrupted();
|
||||
//悬浮窗需要第一时间关闭以免出现恶意脚本全屏悬浮窗屏蔽屏幕并且在exit中写死循环的问题
|
||||
ignoresException(floaty::closeAll);
|
||||
try {
|
||||
@@ -335,11 +334,9 @@ public class ScriptRuntime {
|
||||
mRootShell = null;
|
||||
mShellSupplier = null;
|
||||
});
|
||||
ignoresException(() -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
images.releaseScreenCapturer();
|
||||
}
|
||||
});
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
ignoresException(images::releaseScreenCapturer);
|
||||
}
|
||||
}
|
||||
|
||||
private void ignoresException(Runnable r) {
|
||||
|
||||
@@ -2,19 +2,21 @@ package com.stardust.autojs.runtime.api;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Point;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
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.runtime.ScriptRuntime;
|
||||
import com.stardust.notification.Notification;
|
||||
import com.stardust.notification.NotificationListenerService;
|
||||
import com.stardust.autojs.runtime.ScriptBridges;
|
||||
import com.stardust.autojs.runtime.exception.ScriptException;
|
||||
import com.stardust.autojs.core.inputevent.InputEventObserver;
|
||||
import com.stardust.autojs.core.inputevent.TouchObserver;
|
||||
@@ -41,6 +43,7 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
|
||||
private Loopers mLoopers;
|
||||
private Handler mHandler;
|
||||
private boolean mListeningNotification = false;
|
||||
private boolean mListeningToast = false;
|
||||
private ScriptRuntime mScriptRuntime;
|
||||
|
||||
public Events(Context context, AccessibilityBridge accessibilityBridge, ScriptRuntime runtime) {
|
||||
@@ -136,20 +139,30 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
|
||||
mTouchEventTimeout = touchEventTimeout;
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||
public void observeNotification() {
|
||||
mScriptRuntime.requiresApi(18);
|
||||
if (mListeningNotification)
|
||||
return;
|
||||
mAccessibilityBridge.ensureServiceEnabled();
|
||||
mListeningNotification = true;
|
||||
ensureHandler();
|
||||
mLoopers.waitWhenIdle(true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
|
||||
&& NotificationListenerService.getInstance() != null) {
|
||||
NotificationListenerService.getInstance().addListener(this);
|
||||
} else {
|
||||
mAccessibilityBridge.getNotificationObserver().addNotificationListener(this);
|
||||
if (NotificationListenerService.getInstance() == null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
mContext.startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));
|
||||
}
|
||||
throw new ScriptException(mContext.getString(R.string.exception_notification_service_disabled));
|
||||
}
|
||||
NotificationListenerService.getInstance().addListener(this);
|
||||
}
|
||||
|
||||
public void observeToast() {
|
||||
if (mListeningToast)
|
||||
return;
|
||||
mAccessibilityBridge.ensureServiceEnabled();
|
||||
mListeningToast = true;
|
||||
ensureHandler();
|
||||
mLoopers.waitWhenIdle(true);
|
||||
mAccessibilityBridge.getNotificationObserver().addToastListener(this);
|
||||
}
|
||||
|
||||
@@ -187,20 +200,17 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
|
||||
|
||||
@Override
|
||||
public void onKeyEvent(final int keyCode, final KeyEvent event) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String keyName = KeyEvent.keyCodeToString(keyCode).substring(8).toLowerCase();
|
||||
emit(keyName, event);
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
emit(PREFIX_KEY_DOWN + keyName, event);
|
||||
emit("key_down", keyCode, event);
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
emit(PREFIX_KEY_UP + keyName, event);
|
||||
emit("key_up", keyCode, event);
|
||||
}
|
||||
emit("key", keyCode, event);
|
||||
mHandler.post(() -> {
|
||||
String keyName = KeyEvent.keyCodeToString(keyCode).substring(8).toLowerCase();
|
||||
emit(keyName, event);
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
emit(PREFIX_KEY_DOWN + keyName, event);
|
||||
emit("key_down", keyCode, event);
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
emit(PREFIX_KEY_UP + keyName, event);
|
||||
emit("key_up", keyCode, event);
|
||||
}
|
||||
emit("key", keyCode, event);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -210,31 +220,16 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
|
||||
return;
|
||||
}
|
||||
mLastTouchEventMillis = System.currentTimeMillis();
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
emit("touch", new Point(x, y));
|
||||
}
|
||||
});
|
||||
mHandler.post(() -> emit("touch", new Point(x, y)));
|
||||
}
|
||||
|
||||
public void onNotification(final Notification notification) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
emit("notification", notification);
|
||||
}
|
||||
});
|
||||
mHandler.post(() -> emit("notification", notification));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToast(final AccessibilityNotificationObserver.Toast toast) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
emit("toast", toast);
|
||||
}
|
||||
});
|
||||
mHandler.post(() -> emit("toast", toast));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
package com.stardust.autojs.runtime.api;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
|
||||
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
|
||||
import com.stardust.lang.ThreadCompat;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/29.
|
||||
*/
|
||||
|
||||
public class Loopers {
|
||||
|
||||
public interface LooperQuitHandler {
|
||||
boolean shouldQuit();
|
||||
}
|
||||
|
||||
public volatile boolean waitWhenIdle = false;
|
||||
private volatile Looper mServantLooper;
|
||||
private static volatile ConcurrentHashMap<Thread, Looper> sLoopers = new ConcurrentHashMap<>();
|
||||
private Timers mTimers;
|
||||
private ScriptRuntime mScriptRuntime;
|
||||
private LooperQuitHandler mLooperQuitHandler;
|
||||
|
||||
public Loopers(ScriptRuntime runtime) {
|
||||
mTimers = runtime.timers;
|
||||
mScriptRuntime = runtime;
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
waitWhenIdle = true;
|
||||
}
|
||||
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
|
||||
@Override
|
||||
public boolean queueIdle() {
|
||||
Looper l = Looper.myLooper();
|
||||
if (l != null && shouldQuitLooper()) {
|
||||
if (mLooperQuitHandler != null && mLooperQuitHandler.shouldQuit()) {
|
||||
l.quit();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean shouldQuitLooper() {
|
||||
if (mTimers.hasPendingCallback()) {
|
||||
return false;
|
||||
}
|
||||
return !waitWhenIdle;
|
||||
}
|
||||
|
||||
|
||||
private void initServantThread() {
|
||||
new ThreadCompat(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
final Object lock = Loopers.this;
|
||||
mServantLooper = Looper.myLooper();
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
Looper.loop();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public Looper getServantLooper() {
|
||||
if (mServantLooper == null) {
|
||||
initServantThread();
|
||||
synchronized (this) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new ScriptInterruptedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mServantLooper;
|
||||
}
|
||||
|
||||
public void quitServantLooper() {
|
||||
if (mServantLooper == null)
|
||||
return;
|
||||
mServantLooper.quit();
|
||||
}
|
||||
|
||||
public void waitWhenIdle(boolean b) {
|
||||
waitWhenIdle = b;
|
||||
}
|
||||
|
||||
public void quitAll() {
|
||||
quitServantLooper();
|
||||
}
|
||||
|
||||
public static void prepare() {
|
||||
if (Looper.myLooper() == Looper.getMainLooper())
|
||||
return;
|
||||
if (Looper.myLooper() == null)
|
||||
Looper.prepare();
|
||||
Looper l = Looper.myLooper();
|
||||
if (l != null)
|
||||
sLoopers.put(Thread.currentThread(), l);
|
||||
}
|
||||
|
||||
public static void quitForThread(Thread thread) {
|
||||
Looper looper = sLoopers.remove(thread);
|
||||
if (looper != null)
|
||||
looper.quit();
|
||||
}
|
||||
|
||||
public void setLooperQuitHandler(LooperQuitHandler looperQuitHandler) {
|
||||
mLooperQuitHandler = looperQuitHandler;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
package com.stardust.autojs.runtime.api;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.stardust.autojs.core.looper.TimerThread;
|
||||
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
|
||||
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
|
||||
import com.stardust.concurrent.VolatileBox;
|
||||
import com.stardust.concurrent.VolatileDispose;
|
||||
import com.stardust.lang.ThreadCompat;
|
||||
import com.stardust.pio.PFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/3.
|
||||
@@ -23,30 +25,38 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class Threads {
|
||||
|
||||
private List<ThreadCompat> mThreads = new ArrayList<>();
|
||||
private ScriptRuntime mScriptRuntime;
|
||||
private final HashSet<Thread> mThreads = new HashSet<>();
|
||||
private ScriptRuntime mRuntime;
|
||||
|
||||
public Threads(ScriptRuntime scriptRuntime) {
|
||||
mScriptRuntime = scriptRuntime;
|
||||
public Threads(ScriptRuntime runtime) {
|
||||
mRuntime = runtime;
|
||||
}
|
||||
|
||||
public void start(Runnable runnable) {
|
||||
ThreadCompat threadCompat = new ThreadCompat(() -> {
|
||||
try {
|
||||
((RhinoJavaScriptEngine) mScriptRuntime.engines.myEngine()).createContext();
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
if (!ScriptInterruptedException.causedByInterrupted(e)) {
|
||||
mScriptRuntime.console.error(e);
|
||||
public TimerThread start(Runnable runnable) {
|
||||
TimerThread thread = startThread(runnable);
|
||||
synchronized (mThreads) {
|
||||
mThreads.add(thread);
|
||||
}
|
||||
thread.start();
|
||||
return thread;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private TimerThread startThread(Runnable runnable) {
|
||||
return new TimerThread(mRuntime, mRuntime.timers.getMaxCallbackUptimeMillisForAllThreads(),
|
||||
() -> {
|
||||
((RhinoJavaScriptEngine) mRuntime.engines.myEngine()).createContext();
|
||||
runnable.run();
|
||||
}
|
||||
) {
|
||||
@Override
|
||||
protected void onExit() {
|
||||
synchronized (mThreads) {
|
||||
mThreads.remove(Thread.currentThread());
|
||||
}
|
||||
super.onExit();
|
||||
}
|
||||
});
|
||||
mThreads.add(threadCompat);
|
||||
threadCompat.start();
|
||||
}
|
||||
|
||||
public VolatileBox variable() {
|
||||
return new VolatileBox();
|
||||
};
|
||||
}
|
||||
|
||||
public VolatileDispose disposable() {
|
||||
@@ -65,16 +75,27 @@ public class Threads {
|
||||
return new ConcurrentHashMap();
|
||||
}
|
||||
|
||||
public AtomicInteger atomicInt() {
|
||||
return new AtomicInteger();
|
||||
public AtomicLong atomic() {
|
||||
return new AtomicLong();
|
||||
}
|
||||
|
||||
public AtomicLong atomic(long value) {
|
||||
return new AtomicLong(value);
|
||||
}
|
||||
|
||||
public void shutDownAll() {
|
||||
for (ThreadCompat threadCompat : mThreads) {
|
||||
threadCompat.interrupt();
|
||||
synchronized (mThreads) {
|
||||
for (Thread thread : mThreads) {
|
||||
thread.interrupt();
|
||||
}
|
||||
mThreads.clear();
|
||||
}
|
||||
mThreads.clear();
|
||||
}
|
||||
|
||||
|
||||
public boolean hasRunningThreads() {
|
||||
synchronized (mThreads) {
|
||||
return !mThreads.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@ import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.stardust.autojs.core.looper.Timer;
|
||||
import com.stardust.autojs.core.looper.TimerThread;
|
||||
import com.stardust.autojs.runtime.ScriptBridges;
|
||||
import com.stardust.concurrent.VolatileBox;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/21.
|
||||
@@ -13,99 +16,61 @@ import com.stardust.autojs.runtime.ScriptBridges;
|
||||
|
||||
public class Timers {
|
||||
|
||||
private SparseArray<Runnable> mHandlerCallbacks = new SparseArray<>();
|
||||
private int mCallbackMaxId = 0;
|
||||
private ScriptBridges mBridges;
|
||||
private ThreadLocal<Handler> mHandler = new ThreadLocal<>();
|
||||
private long mFutureCallbackUptimeMillis = 0;
|
||||
private static final String LOG_TAG = "Timers";
|
||||
|
||||
private VolatileBox<Long> mMaxCallbackUptimeMillisForAllThreads = new VolatileBox<>(0L);
|
||||
private Thread mMainThread;
|
||||
private Timer mMainTimer;
|
||||
|
||||
public Timers(ScriptBridges bridges) {
|
||||
mBridges = bridges;
|
||||
mMainThread = Thread.currentThread();
|
||||
mMainTimer = new Timer(bridges, mMaxCallbackUptimeMillisForAllThreads);
|
||||
}
|
||||
|
||||
private void ensureHandler() {
|
||||
if (mHandler.get() == null) {
|
||||
mHandler.set(new Handler());
|
||||
public VolatileBox<Long> getMaxCallbackUptimeMillisForAllThreads() {
|
||||
return mMaxCallbackUptimeMillisForAllThreads;
|
||||
}
|
||||
|
||||
private Timer getTimerForCurrentThread() {
|
||||
if (Thread.currentThread() == mMainThread) {
|
||||
return mMainTimer;
|
||||
}
|
||||
return TimerThread.getTimerForCurrentThread();
|
||||
}
|
||||
|
||||
public int setTimeout(final Object callback, final long delay, final Object... args) {
|
||||
ensureHandler();
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
Runnable r = () -> {
|
||||
mBridges.callFunction(callback, null, args);
|
||||
mHandlerCallbacks.remove(id);
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, delay);
|
||||
return id;
|
||||
public int setTimeout(Object callback, long delay, Object... args) {
|
||||
return getTimerForCurrentThread().setTimeout(callback, delay, args);
|
||||
}
|
||||
|
||||
public boolean clearTimeout(int id) {
|
||||
return clearCallback(id);
|
||||
return getTimerForCurrentThread().clearTimeout(id);
|
||||
}
|
||||
|
||||
public int setInterval(final Object listener, final long interval, final Object... args) {
|
||||
ensureHandler();
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
final Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mHandlerCallbacks.get(id) == null)
|
||||
return;
|
||||
mBridges.callFunction(listener, null, args);
|
||||
postDelayed(this, interval);
|
||||
}
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, interval);
|
||||
return id;
|
||||
}
|
||||
|
||||
private void postDelayed(Runnable r, long interval) {
|
||||
long uptime = SystemClock.uptimeMillis() + interval;
|
||||
mHandler.get().postAtTime(r, uptime);
|
||||
mFutureCallbackUptimeMillis = Math.max(mFutureCallbackUptimeMillis, uptime);
|
||||
public int setInterval(Object listener, long interval, Object... args) {
|
||||
return getTimerForCurrentThread().setInterval(listener, interval, args);
|
||||
}
|
||||
|
||||
public boolean clearInterval(int id) {
|
||||
return clearCallback(id);
|
||||
return getTimerForCurrentThread().clearInterval(id);
|
||||
}
|
||||
|
||||
public int setImmediate(final Object listener, final Object... args) {
|
||||
ensureHandler();
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mBridges.callFunction(listener, null, args);
|
||||
mHandlerCallbacks.remove(id);
|
||||
}
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, 0);
|
||||
return id;
|
||||
public int setImmediate(Object listener, Object... args) {
|
||||
return getTimerForCurrentThread().setImmediate(listener, args);
|
||||
}
|
||||
|
||||
public boolean clearImmediate(int id) {
|
||||
return clearCallback(id);
|
||||
return getTimerForCurrentThread().clearImmediate(id);
|
||||
}
|
||||
|
||||
private boolean clearCallback(int id) {
|
||||
Runnable callback = mHandlerCallbacks.get(id);
|
||||
if (callback != null) {
|
||||
mHandler.get().removeCallbacks(callback);
|
||||
mHandlerCallbacks.remove(id);
|
||||
return true;
|
||||
public boolean hasPendingCallbacks() {
|
||||
//如果是脚本主线程,则检查所有子线程中的定时回调。mFutureCallbackUptimeMillis用来记录所有子线程中定时最久的一个。
|
||||
if (mMainThread == 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();
|
||||
}
|
||||
return false;
|
||||
//否则检查当前线程的定时回调
|
||||
return getTimerForCurrentThread().hasPendingCallbacks();
|
||||
}
|
||||
|
||||
|
||||
public boolean hasPendingCallback() {
|
||||
return mFutureCallbackUptimeMillis > SystemClock.uptimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<string name="_app_name">AutoJs</string>
|
||||
<string name="text_should_enable_key_observing">按键监听未启用,请在软件设置中开启</string>
|
||||
<string name="no_write_settings_permissin">沒有修改系統设置权限</string>
|
||||
<string name="exception_notification_service_disabled">通知服务未运行,请重新启用通知权限</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user