异步环境修复
This commit is contained in:
@@ -1,211 +0,0 @@
|
||||
package org.autojs.autojs.core.looper;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.autojs.autojs.lang.ThreadCompat;
|
||||
import org.autojs.autojs.rhino.AutoJsContext;
|
||||
import org.autojs.autojs.runtime.ScriptRuntime;
|
||||
import org.autojs.autojs.runtime.api.Threads;
|
||||
import org.autojs.autojs.runtime.api.Timers;
|
||||
import org.autojs.autojs.runtime.exception.ScriptInterruptedException;
|
||||
import org.mozilla.javascript.Context;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/29.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class Loopers implements MessageQueue.IdleHandler {
|
||||
|
||||
private static final String LOG_TAG = "Loopers";
|
||||
|
||||
public interface LooperQuitHandler {
|
||||
boolean shouldQuit();
|
||||
}
|
||||
|
||||
private static final Runnable EMPTY_RUNNABLE = () -> {
|
||||
};
|
||||
|
||||
private final ThreadLocal<Boolean> waitWhenIdle = new ThreadLocal<>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected Boolean initialValue() {
|
||||
return Looper.myLooper() == Looper.getMainLooper();
|
||||
}
|
||||
};
|
||||
private final ThreadLocal<HashSet<Integer>> waitIds = new ThreadLocal<>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected HashSet<Integer> initialValue() {
|
||||
return new HashSet<>();
|
||||
}
|
||||
};
|
||||
private final ThreadLocal<Integer> maxWaitId = new ThreadLocal<>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected Integer initialValue() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
private final ThreadLocal<CopyOnWriteArrayList<LooperQuitHandler>> looperQuitHandlers = new ThreadLocal<>();
|
||||
private volatile Looper mServantLooper;
|
||||
private final Timers mTimers;
|
||||
private LooperQuitHandler mMainLooperQuitHandler;
|
||||
private final Handler mMainHandler;
|
||||
private final Looper mMainLooper;
|
||||
private final Threads mThreads;
|
||||
private final MessageQueue mMainMessageQueue;
|
||||
|
||||
public Loopers(ScriptRuntime runtime) {
|
||||
mTimers = runtime.timers;
|
||||
mThreads = runtime.threads;
|
||||
prepare();
|
||||
mMainLooper = Looper.myLooper();
|
||||
mMainHandler = new Handler();
|
||||
mMainMessageQueue = Looper.myQueue();
|
||||
}
|
||||
|
||||
|
||||
public Looper getMainLooper() {
|
||||
return mMainLooper;
|
||||
}
|
||||
|
||||
public void addLooperQuitHandler(LooperQuitHandler handler) {
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
|
||||
if (handlers == null) {
|
||||
handlers = new CopyOnWriteArrayList<>();
|
||||
looperQuitHandlers.set(handlers);
|
||||
}
|
||||
handlers.add(handler);
|
||||
}
|
||||
|
||||
public boolean removeLooperQuitHandler(LooperQuitHandler handler) {
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
|
||||
return handlers != null && handlers.remove(handler);
|
||||
}
|
||||
|
||||
private boolean shouldQuitLooper() {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
return true;
|
||||
}
|
||||
if (mTimers.hasPendingCallbacks()) {
|
||||
return false;
|
||||
}
|
||||
if (waitWhenIdle.get() || !waitIds.get().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (((AutoJsContext) Context.getCurrentContext()).hasPendingContinuation()) {
|
||||
return false;
|
||||
}
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
|
||||
if (handlers == null) {
|
||||
return true;
|
||||
}
|
||||
for (LooperQuitHandler handler : handlers) {
|
||||
if (!handler.shouldQuit()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void initServantThread() {
|
||||
final Object lock = Loopers.this;
|
||||
new ThreadCompat(() -> {
|
||||
Looper.prepare();
|
||||
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;
|
||||
}
|
||||
|
||||
private void quitServantLooper() {
|
||||
if (mServantLooper == null)
|
||||
return;
|
||||
mServantLooper.quit();
|
||||
}
|
||||
|
||||
public int waitWhenIdle() {
|
||||
int id = maxWaitId.get();
|
||||
Log.d(LOG_TAG, "waitWhenIdle: " + id);
|
||||
maxWaitId.set(id + 1);
|
||||
waitIds.get().add(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
public void doNotWaitWhenIdle(int waitId) {
|
||||
Log.d(LOG_TAG, "doNotWaitWhenIdle: " + waitId);
|
||||
waitIds.get().remove(waitId);
|
||||
}
|
||||
|
||||
public void waitWhenIdle(boolean b) {
|
||||
waitWhenIdle.set(b);
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
quitServantLooper();
|
||||
mMainMessageQueue.removeIdleHandler(this);
|
||||
}
|
||||
|
||||
public void setMainLooperQuitHandler(LooperQuitHandler mainLooperQuitHandler) {
|
||||
mMainLooperQuitHandler = mainLooperQuitHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean queueIdle() {
|
||||
Looper l = Looper.myLooper();
|
||||
if (l == null)
|
||||
return true;
|
||||
if (l == mMainLooper) {
|
||||
Log.d(LOG_TAG, "main looper queueIdle");
|
||||
if (shouldQuitLooper() && !mThreads.hasRunningThreads() &&
|
||||
mMainLooperQuitHandler != null && mMainLooperQuitHandler.shouldQuit()) {
|
||||
Log.d(LOG_TAG, "main looper quit");
|
||||
l.quit();
|
||||
}
|
||||
} else {
|
||||
Log.d(LOG_TAG, "looper queueIdle: " + l);
|
||||
if (shouldQuitLooper()) {
|
||||
l.quit();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void prepare() {
|
||||
if (Looper.myLooper() == null)
|
||||
LooperHelper.prepare();
|
||||
Looper.myQueue().addIdleHandler(this);
|
||||
}
|
||||
|
||||
public void notifyThreadExit(TimerThread thread) {
|
||||
Log.d(LOG_TAG, "notifyThreadExit: " + thread);
|
||||
//当子线程退成时,主线程需要检查自身是否退出(主线程在所有子线程执行完成后才能退出,如果主线程已经执行完任务仍然要等待所有子线程),
|
||||
//此时通过向主线程发送一个空的Runnable,主线程执行完这个Runnable后会触发IdleHandler,从而检查自身是否退出
|
||||
mMainHandler.post(EMPTY_RUNNABLE);
|
||||
}
|
||||
}
|
||||
182
app/src/main/java/org/autojs/autojs/core/looper/Loopers.kt
Normal file
182
app/src/main/java/org/autojs/autojs/core/looper/Loopers.kt
Normal file
@@ -0,0 +1,182 @@
|
||||
package org.autojs.autojs.core.looper
|
||||
|
||||
import android.os.Looper
|
||||
import android.os.MessageQueue
|
||||
import android.util.Log
|
||||
import org.autojs.autojs.lang.ThreadCompat
|
||||
import org.autojs.autojs.rhino.AutoJsContext
|
||||
import org.autojs.autojs.runtime.ScriptRuntime
|
||||
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
|
||||
import org.mozilla.javascript.Context
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/7/29.
|
||||
*/
|
||||
/**
|
||||
* update by aiselp on 2023/6/4
|
||||
* 调整内容:
|
||||
* 使此类只负责单loop线程生命周期管理,移除繁琐的调用链
|
||||
* 调整timer由此类创建
|
||||
* 通过向此类添加AsyncTask以监听线程退出事件
|
||||
*/
|
||||
class Loopers(val runtime: ScriptRuntime) {
|
||||
@Deprecated("使用AsyncTask代替")
|
||||
interface LooperQuitHandler {
|
||||
fun shouldQuit(): Boolean
|
||||
}
|
||||
|
||||
open class AsyncTask(private val describe: String) {
|
||||
private val allBind = ConcurrentLinkedQueue<Loopers>()
|
||||
var isEnd: Boolean = false
|
||||
private set
|
||||
|
||||
//线程即将退出时调用,返回true阻止线程退出,只要有一个task返回true线程就不会退出
|
||||
open fun onFinish(loopers: Loopers): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
fun end() {
|
||||
isEnd = true
|
||||
}
|
||||
|
||||
//线程正在退出,这里应该结束任务的执行,回收资源
|
||||
open fun onStop(loopers: Loopers) {}
|
||||
override fun toString(): String {
|
||||
return "AsyncTask: $describe"
|
||||
}
|
||||
}
|
||||
|
||||
private var waitWhenIdle: Boolean
|
||||
|
||||
@Volatile
|
||||
private var mServantLooper: Looper? = null
|
||||
private var mMainLooperQuitHandler: LooperQuitHandler? = null
|
||||
private val allTasks = ConcurrentLinkedQueue<AsyncTask>()
|
||||
val mTimer: Timer
|
||||
|
||||
init {
|
||||
prepare()
|
||||
val myLooper = Looper.myLooper()!!
|
||||
mTimer = Timer(runtime, myLooper)
|
||||
waitWhenIdle = myLooper == Looper.getMainLooper()
|
||||
}
|
||||
|
||||
fun createAndAddAsyncTask(describe: String): AsyncTask {
|
||||
val task = AsyncTask(describe)
|
||||
allTasks.add(task)
|
||||
return task
|
||||
}
|
||||
|
||||
fun addAsyncTask(task: AsyncTask) {
|
||||
allTasks.add(task)
|
||||
}
|
||||
|
||||
fun removeAsyncTask(task: AsyncTask) {
|
||||
allTasks.remove(task)
|
||||
mTimer.post(EMPTY_RUNNABLE)
|
||||
}
|
||||
|
||||
private fun checkTask(): Boolean {
|
||||
allTasks.removeAll(allTasks.filter { it.isEnd }.toSet())
|
||||
for (task in allTasks) {
|
||||
if (task.onFinish(this)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun shouldQuitLooper(): Boolean {
|
||||
if (Thread.currentThread().isInterrupted) return true
|
||||
if (mTimer.hasPendingCallbacks()) return false
|
||||
//检查是否有运行中的线程
|
||||
if (checkTask()) return false
|
||||
if (waitWhenIdle) return false
|
||||
if ((Context.getCurrentContext() as AutoJsContext).hasPendingContinuation()) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun initServantThread() {
|
||||
ThreadCompat {
|
||||
Looper.prepare()
|
||||
val lock = this@Loopers as Object
|
||||
mServantLooper = Looper.myLooper()
|
||||
synchronized(lock) { lock.notifyAll() }
|
||||
Looper.loop()
|
||||
}.start()
|
||||
}
|
||||
|
||||
val servantLooper: Looper
|
||||
get() {
|
||||
if (mServantLooper == null) {
|
||||
initServantThread()
|
||||
val lock = this as Object
|
||||
synchronized(lock) {
|
||||
try {
|
||||
lock.wait()
|
||||
} catch (e: InterruptedException) {
|
||||
throw ScriptInterruptedException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return mServantLooper!!
|
||||
}
|
||||
|
||||
@Deprecated("使用AsyncTask代替")
|
||||
fun waitWhenIdle(b: Boolean) {
|
||||
waitWhenIdle = b
|
||||
}
|
||||
|
||||
fun recycle() {
|
||||
Log.d(LOG_TAG, "recycle")
|
||||
for (task in allTasks.filter { !it.isEnd }) {
|
||||
try {
|
||||
task.onStop(this)
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, e)
|
||||
}
|
||||
}
|
||||
mServantLooper?.quit()
|
||||
}
|
||||
|
||||
@Deprecated("使用AsyncTask代替")
|
||||
fun setMainLooperQuitHandler(mainLooperQuitHandler: LooperQuitHandler?) {
|
||||
mMainLooperQuitHandler = mainLooperQuitHandler
|
||||
}
|
||||
|
||||
private fun prepare() {
|
||||
if (Looper.myLooper() == null) LooperHelper.prepare()
|
||||
Looper.myQueue().addIdleHandler(MessageQueue.IdleHandler {
|
||||
if (this == runtime.loopers) {
|
||||
Log.d(LOG_TAG, "main looper queueIdle")
|
||||
if (shouldQuitLooper() &&
|
||||
mMainLooperQuitHandler != null &&
|
||||
mMainLooperQuitHandler!!.shouldQuit()
|
||||
) {
|
||||
Log.d(LOG_TAG, "main looper quit")
|
||||
Looper.myLooper()!!.quitSafely()
|
||||
}
|
||||
}else {
|
||||
Log.d(LOG_TAG, "looper queueIdle $this")
|
||||
if (shouldQuitLooper()) {
|
||||
Log.d(LOG_TAG, "looper quit $this")
|
||||
Looper.myLooper()!!.quitSafely()
|
||||
}
|
||||
}
|
||||
return@IdleHandler true
|
||||
})
|
||||
}
|
||||
|
||||
fun notifyThreadExit(thread: TimerThread) {
|
||||
Log.d(LOG_TAG, "notifyThreadExit: $thread")
|
||||
//当子线程退成时,主线程需要检查自身是否退出(主线程在所有子线程执行完成后才能退出,如果主线程已经执行完任务仍然要等待所有子线程),
|
||||
//此时通过向主线程发送一个空的Runnable,主线程执行完这个Runnable后会触发IdleHandler,从而检查自身是否退出
|
||||
//mHandler.post(EMPTY_RUNNABLE)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val LOG_TAG = "Loopers"
|
||||
private val EMPTY_RUNNABLE = Runnable {}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
package org.autojs.autojs.core.looper;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import org.autojs.autojs.runtime.ScriptRuntime;
|
||||
import org.autojs.autojs.concurrent.VolatileBox;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
public class Timer {
|
||||
|
||||
private final SparseArray<Runnable> mHandlerCallbacks = new SparseArray<>();
|
||||
private int mCallbackMaxId = 0;
|
||||
private final ScriptRuntime mRuntime;
|
||||
private final Handler mHandler;
|
||||
private long mMaxCallbackUptimeMillis = 0;
|
||||
private final VolatileBox<Long> mMaxCallbackMillisForAllThread;
|
||||
|
||||
public Timer(ScriptRuntime runtime, VolatileBox<Long> maxCallbackMillisForAllThread) {
|
||||
mRuntime = runtime;
|
||||
mMaxCallbackMillisForAllThread = maxCallbackMillisForAllThread;
|
||||
mHandler = new Handler();
|
||||
}
|
||||
|
||||
public Timer(ScriptRuntime runtime, VolatileBox<Long> maxCallbackMillisForAllThread, Looper looper) {
|
||||
mRuntime = runtime;
|
||||
mMaxCallbackMillisForAllThread = maxCallbackMillisForAllThread;
|
||||
mHandler = new Handler(looper);
|
||||
}
|
||||
|
||||
public int setTimeout(final Object callback, final long delay, final Object... args) {
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
Runnable r = () -> {
|
||||
callFunction(callback, args);
|
||||
mHandlerCallbacks.remove(id);
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, delay);
|
||||
return id;
|
||||
}
|
||||
|
||||
private void callFunction(Object callback, Object[] args) {
|
||||
if(Looper.myLooper() == Looper.getMainLooper()){
|
||||
try {
|
||||
mRuntime.bridges.callFunction(callback, null, args);
|
||||
}catch (Exception e){
|
||||
mRuntime.exit(e);
|
||||
}
|
||||
}else {
|
||||
mRuntime.bridges.callFunction(callback, null, args);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
callFunction(listener, args);
|
||||
postDelayed(this, interval);
|
||||
}
|
||||
};
|
||||
mHandlerCallbacks.put(id, r);
|
||||
postDelayed(r, interval);
|
||||
return id;
|
||||
}
|
||||
|
||||
public 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 void post(Runnable r) {
|
||||
|
||||
}
|
||||
|
||||
public boolean clearInterval(int id) {
|
||||
return clearCallback(id);
|
||||
}
|
||||
|
||||
public int setImmediate(final Object listener, final Object... args) {
|
||||
mCallbackMaxId++;
|
||||
final int id = mCallbackMaxId;
|
||||
Runnable r = () -> {
|
||||
callFunction(listener, 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() {
|
||||
return mMaxCallbackUptimeMillis > SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
public void removeAllCallbacks() {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
}
|
||||
117
app/src/main/java/org/autojs/autojs/core/looper/Timer.kt
Normal file
117
app/src/main/java/org/autojs/autojs/core/looper/Timer.kt
Normal file
@@ -0,0 +1,117 @@
|
||||
package org.autojs.autojs.core.looper
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import org.autojs.autojs.runtime.ScriptRuntime
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
class Timer(
|
||||
runtime: ScriptRuntime,
|
||||
looper: Looper
|
||||
) {
|
||||
private val mHandlerCallbacks = ConcurrentHashMap<Int, Runnable?>()
|
||||
private val mRuntime: ScriptRuntime = runtime
|
||||
private val mHandler: Handler = Handler(looper)
|
||||
private val isUiLoop: Boolean = looper == Looper.getMainLooper()
|
||||
|
||||
constructor(runtime: ScriptRuntime, ) : this(runtime, Looper.myLooper()!!)
|
||||
|
||||
fun setTimeout(callback: Any, delay: Long, vararg args: Any): Int {
|
||||
val id = createTimerId()
|
||||
val r = Runnable {
|
||||
callFunction(callback, null, args as Array<Any>)
|
||||
mHandlerCallbacks.remove(id)
|
||||
}
|
||||
mHandlerCallbacks[id] = r
|
||||
postDelayed(r, delay)
|
||||
return id
|
||||
}
|
||||
|
||||
private fun callFunction(callback: Any, thiz: Any?, args: Array<Any>) {
|
||||
try {
|
||||
mRuntime.bridges.callFunction(callback, thiz, args)
|
||||
} catch (e: Exception) {
|
||||
if (isUiLoop) {
|
||||
mRuntime.exit(e)
|
||||
} else throw e
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun createTimerId(): Int {
|
||||
var id: Int
|
||||
do {
|
||||
id = Random.nextInt()
|
||||
} while (mHandlerCallbacks.containsKey(id))
|
||||
mHandlerCallbacks[id] = EMPTY_RUNNABLE
|
||||
return id
|
||||
}
|
||||
|
||||
fun setInterval(listener: Any, interval: Long, vararg args: Any): Int {
|
||||
val id = createTimerId()
|
||||
val r: Runnable = object : Runnable {
|
||||
override fun run() {
|
||||
if (mHandlerCallbacks[id] == null) return
|
||||
callFunction(listener, null, args as Array<Any>)
|
||||
postDelayed(this, interval)
|
||||
}
|
||||
}
|
||||
mHandlerCallbacks[id] = r
|
||||
postDelayed(r, interval)
|
||||
return id
|
||||
}
|
||||
|
||||
fun postDelayed(r: Runnable, interval: Long) {
|
||||
val uptime = SystemClock.uptimeMillis() + interval
|
||||
mHandler.postAtTime(r, uptime)
|
||||
}
|
||||
|
||||
fun post(r: Runnable) {
|
||||
mHandler.post(r)
|
||||
}
|
||||
|
||||
fun clearInterval(id: Int): Boolean = clearCallback(id)
|
||||
fun clearImmediate(id: Int): Boolean = clearCallback(id)
|
||||
fun clearTimeout(id: Int): Boolean = clearCallback(id)
|
||||
|
||||
fun setImmediate(listener: Any, vararg args: Any): Int {
|
||||
val id = createTimerId()
|
||||
val r = Runnable {
|
||||
callFunction(listener, null, args as Array<Any>)
|
||||
mHandlerCallbacks.remove(id)
|
||||
}
|
||||
mHandlerCallbacks[id] = r
|
||||
post(r)
|
||||
return id
|
||||
}
|
||||
|
||||
|
||||
private fun clearCallback(id: Int): Boolean {
|
||||
val callback = mHandlerCallbacks[id]
|
||||
if (callback != null) {
|
||||
mHandler.removeCallbacks(callback)
|
||||
mHandlerCallbacks.remove(id)
|
||||
if (mHandlerCallbacks.isEmpty()) mHandler.post(EMPTY_RUNNABLE)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun hasPendingCallbacks(): Boolean {
|
||||
return mHandlerCallbacks.size > 0
|
||||
}
|
||||
|
||||
fun removeAllCallbacks() {
|
||||
mHandler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val LOG_TAG = "Timer"
|
||||
private val EMPTY_RUNNABLE = Runnable {}
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package org.autojs.autojs.core.looper;
|
||||
|
||||
import static org.autojs.autojs.util.StringUtils.str;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.autojs.autojs.concurrent.VolatileBox;
|
||||
import org.autojs.autojs.engine.RhinoJavaScriptEngine;
|
||||
import org.autojs.autojs.lang.ThreadCompat;
|
||||
import org.autojs.autojs.runtime.ScriptRuntime;
|
||||
import org.autojs.autojs.runtime.exception.ScriptInterruptedException;
|
||||
import org.autojs.autojs6.R;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
public class TimerThread extends ThreadCompat {
|
||||
|
||||
private static final ConcurrentHashMap<Thread, Timer> sTimerMap = new ConcurrentHashMap<>();
|
||||
|
||||
private Timer mTimer;
|
||||
private final VolatileBox<Long> mMaxCallbackUptimeMillisForAllThreads;
|
||||
private final ScriptRuntime mRuntime;
|
||||
private final Runnable mTarget;
|
||||
private boolean mRunning = false;
|
||||
private final Object mRunningLock = new Object();
|
||||
|
||||
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, mMaxCallbackUptimeMillisForAllThreads);
|
||||
sTimerMap.put(Thread.currentThread(), mTimer);
|
||||
((RhinoJavaScriptEngine) mRuntime.engines.myEngine()).enterContext();
|
||||
notifyRunning();
|
||||
new Handler().post(mTarget);
|
||||
try {
|
||||
Looper.loop();
|
||||
} catch (Throwable e) {
|
||||
if (!ScriptInterruptedException.causedByInterrupted(e)) {
|
||||
mRuntime.console.error(Thread.currentThread() + ": ", e);
|
||||
}
|
||||
} finally {
|
||||
onExit();
|
||||
mTimer = null;
|
||||
org.mozilla.javascript.Context.exit();
|
||||
sTimerMap.remove(Thread.currentThread(), mTimer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt() {
|
||||
LooperHelper.quitForThread(this);
|
||||
super.interrupt();
|
||||
}
|
||||
|
||||
private void notifyRunning() {
|
||||
synchronized (mRunningLock) {
|
||||
mRunning = true;
|
||||
mRunningLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@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 getTimer().setTimeout(callback, delay, args);
|
||||
}
|
||||
|
||||
public Timer getTimer() {
|
||||
if (mTimer == null) {
|
||||
throw new IllegalStateException(str(R.string.error_thread_is_not_alive));
|
||||
}
|
||||
return mTimer;
|
||||
}
|
||||
|
||||
public boolean clearTimeout(int id) {
|
||||
return getTimer().clearTimeout(id);
|
||||
}
|
||||
|
||||
public int setInterval(Object listener, long interval, Object... args) {
|
||||
return getTimer().setInterval(listener, interval, args);
|
||||
}
|
||||
|
||||
public boolean clearInterval(int id) {
|
||||
return getTimer().clearInterval(id);
|
||||
}
|
||||
|
||||
public int setImmediate(Object listener, Object... args) {
|
||||
return getTimer().setImmediate(listener, args);
|
||||
}
|
||||
|
||||
public boolean clearImmediate(int id) {
|
||||
return getTimer().clearImmediate(id);
|
||||
}
|
||||
|
||||
public void waitFor() throws InterruptedException {
|
||||
synchronized (mRunningLock) {
|
||||
if (mRunning)
|
||||
return;
|
||||
mRunningLock.wait();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Thread[" + getName() + "," + getPriority() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
126
app/src/main/java/org/autojs/autojs/core/looper/TimerThread.kt
Normal file
126
app/src/main/java/org/autojs/autojs/core/looper/TimerThread.kt
Normal file
@@ -0,0 +1,126 @@
|
||||
package org.autojs.autojs.core.looper
|
||||
|
||||
import android.os.Looper
|
||||
import androidx.annotation.CallSuper
|
||||
import org.autojs.autojs.engine.RhinoJavaScriptEngine
|
||||
import org.autojs.autojs.lang.ThreadCompat
|
||||
import org.autojs.autojs.runtime.ScriptRuntime
|
||||
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
|
||||
import org.mozilla.javascript.Context
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/27.
|
||||
*/
|
||||
open class TimerThread(private val mRuntime: ScriptRuntime, private val mTarget: Runnable) :
|
||||
ThreadCompat(mTarget) {
|
||||
private var mTimer: Timer? = null
|
||||
private var mRunning = false
|
||||
private val mRunningLock = Object()
|
||||
private val mAsyncTask = Loopers.AsyncTask("TimerThread")
|
||||
var loopers: Loopers? = null
|
||||
init {
|
||||
mRuntime.loopers.addAsyncTask(mAsyncTask)
|
||||
}
|
||||
override fun run() {
|
||||
loopers = Loopers(mRuntime)
|
||||
mTimer = loopers!!.mTimer
|
||||
sTimerMap[currentThread()] = mTimer!!
|
||||
(mRuntime.engines.myEngine() as RhinoJavaScriptEngine).enterContext()
|
||||
notifyRunning()
|
||||
mTimer!!.post(mTarget)
|
||||
try {
|
||||
Looper.loop()
|
||||
} catch (e: Throwable) {
|
||||
if (!ScriptInterruptedException.causedByInterrupted(e)) {
|
||||
mRuntime.console.error(currentThread().toString() + ": ", e)
|
||||
}
|
||||
} finally {
|
||||
//mRuntime.console.log("TimerThread exit");
|
||||
onExit()
|
||||
mTimer = null
|
||||
Context.exit()
|
||||
sTimerMap.remove(currentThread(), mTimer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun interrupt() {
|
||||
LooperHelper.quitForThread(this)
|
||||
super.interrupt()
|
||||
}
|
||||
|
||||
private fun notifyRunning() {
|
||||
synchronized(mRunningLock) {
|
||||
mRunning = true
|
||||
mRunningLock.notifyAll()
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun onExit() {
|
||||
mRuntime.loopers.removeAsyncTask(mAsyncTask)
|
||||
mRuntime.loopers.notifyThreadExit(this)
|
||||
}
|
||||
|
||||
fun setTimeout(callback: Any, delay: Long, vararg args: Any?): Int {
|
||||
return timer.setTimeout(callback, delay, *args as Array<out Any>)
|
||||
}
|
||||
|
||||
fun setTimeout(callback: Any): Int {
|
||||
return setTimeout(callback, 1)
|
||||
}
|
||||
|
||||
val timer: Timer
|
||||
get() {
|
||||
checkNotNull(mTimer) { "thread is not alive" }
|
||||
return mTimer as Timer
|
||||
}
|
||||
|
||||
fun clearTimeout(id: Int): Boolean {
|
||||
return timer.clearTimeout(id)
|
||||
}
|
||||
|
||||
fun setInterval(listener: Any?, interval: Long, vararg args: Any?): Int {
|
||||
return timer.setInterval(listener!!, interval, *args as Array<out Any>)
|
||||
}
|
||||
|
||||
fun setInterval(listener: Any?): Int {
|
||||
return setInterval(listener, 1)
|
||||
}
|
||||
|
||||
fun clearInterval(id: Int): Boolean {
|
||||
return timer.clearInterval(id)
|
||||
}
|
||||
|
||||
fun setImmediate(listener: Any, vararg args: Any?): Int {
|
||||
return timer.setImmediate(listener, *args as Array<out Any>)
|
||||
}
|
||||
|
||||
fun clearImmediate(id: Int): Boolean {
|
||||
return timer.clearImmediate(id)
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class)
|
||||
fun waitFor() {
|
||||
synchronized(mRunningLock) {
|
||||
if (mRunning) return
|
||||
mRunningLock.wait()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Thread[$name,$priority]"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val sTimerMap = ConcurrentHashMap<Thread, Timer?>()
|
||||
|
||||
@JvmStatic
|
||||
fun getTimerForThread(thread: Thread): Timer? {
|
||||
return sTimerMap[thread]
|
||||
}
|
||||
|
||||
val timerForCurrentThread: Timer?
|
||||
get() = getTimerForThread(currentThread())
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public class JsDialogBuilder extends MaterialDialog.Builder {
|
||||
private final Timer mTimer;
|
||||
private final Loopers mLoopers;
|
||||
private JsDialog mDialog;
|
||||
private volatile int mWaitId = -1;
|
||||
private volatile Loopers.AsyncTask task;
|
||||
|
||||
|
||||
public JsDialogBuilder(Context context, ScriptRuntime runtime) {
|
||||
@@ -55,14 +55,14 @@ public class JsDialogBuilder extends MaterialDialog.Builder {
|
||||
}
|
||||
});
|
||||
dismissListener(dialog -> {
|
||||
mTimer.postDelayed(() -> mLoopers.doNotWaitWhenIdle(mWaitId), 0);
|
||||
mTimer.postDelayed(() -> mLoopers.removeAsyncTask(task), 0);
|
||||
emit("dismiss", dialog);
|
||||
});
|
||||
cancelListener(dialog -> emit("cancel", dialog));
|
||||
}
|
||||
|
||||
public void onShowCalled() {
|
||||
mTimer.postDelayed(() -> mWaitId = mLoopers.waitWhenIdle(), 0);
|
||||
mTimer.postDelayed(() -> task = mLoopers.createAndAddAsyncTask("js-dialog"), 0);
|
||||
}
|
||||
|
||||
public JsDialog getDialog() {
|
||||
|
||||
@@ -22,7 +22,9 @@ import java.util.Set;
|
||||
/**
|
||||
* Created by Stardust on 2018/2/5.
|
||||
*/
|
||||
public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler {
|
||||
|
||||
public class Sensors extends EventEmitter {
|
||||
|
||||
|
||||
public class SensorEventEmitter extends EventEmitter implements SensorEventListener {
|
||||
|
||||
@@ -82,6 +84,13 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler {
|
||||
private final ScriptBridges mScriptBridges;
|
||||
private final SensorEventEmitter mNoOpSensorEventEmitter;
|
||||
private final ScriptRuntime mScriptRuntime;
|
||||
private final Loopers.AsyncTask mAsyncTask = new Loopers.AsyncTask("Sensors"){
|
||||
@Override
|
||||
public boolean onFinish(@NonNull Loopers loopers) {
|
||||
return !mSensorEventEmitters.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Sensors(Context context, ScriptRuntime runtime) {
|
||||
super(runtime.bridges);
|
||||
@@ -89,7 +98,7 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler {
|
||||
mScriptBridges = runtime.bridges;
|
||||
mNoOpSensorEventEmitter = new SensorEventEmitter(runtime.bridges);
|
||||
mScriptRuntime = runtime;
|
||||
runtime.loopers.addLooperQuitHandler(this);
|
||||
runtime.loopers.addAsyncTask(mAsyncTask);
|
||||
}
|
||||
|
||||
public SensorEventEmitter register(String sensorName) {
|
||||
@@ -121,11 +130,6 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler {
|
||||
return emitter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldQuit() {
|
||||
return mSensorEventEmitters.isEmpty();
|
||||
}
|
||||
|
||||
public Sensor getSensor(String sensorName) {
|
||||
sensorName = sensorName.toUpperCase();
|
||||
Integer type = SENSORS.get(sensorName);
|
||||
@@ -158,6 +162,6 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler {
|
||||
}
|
||||
mSensorEventEmitters.clear();
|
||||
}
|
||||
mScriptRuntime.loopers.removeLooperQuitHandler(this);
|
||||
mScriptRuntime.loopers.removeAsyncTask(mAsyncTask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.autojs.autojs.runtime.api
|
||||
|
||||
import org.autojs.autojs.annotation.ScriptInterface
|
||||
import org.autojs.autojs.concurrent.VolatileDispose
|
||||
import org.autojs.autojs.core.looper.Loopers
|
||||
import org.autojs.autojs.core.looper.MainThreadProxy
|
||||
import org.autojs.autojs.core.looper.TimerThread
|
||||
import org.autojs.autojs.runtime.ScriptRuntime
|
||||
import org.autojs.autojs.util.StringUtils.str
|
||||
import org.autojs.autojs6.R
|
||||
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ThreadFactory
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
@@ -14,58 +15,81 @@ import java.util.concurrent.locks.ReentrantLock
|
||||
* Created by Stardust on 2017/12/3.
|
||||
*/
|
||||
class Threads(private val mRuntime: ScriptRuntime) {
|
||||
|
||||
private val mThreads = HashSet<Thread>()
|
||||
val mainThread: Thread = Thread.currentThread()
|
||||
private val mMainThreadProxy = MainThreadProxy(Thread.currentThread(), mRuntime)
|
||||
private var mSpawnCount = 0
|
||||
private var mTaskCount = AtomicLong(0)
|
||||
private var mExit = false
|
||||
private val looperTask = Loopers.AsyncTask("AsyncTaskThreadPool")
|
||||
private val threadPool = Executors.newFixedThreadPool(20, ThreadFactory {
|
||||
val thread = Thread(it)
|
||||
thread.name = mainThread.name + " (AsyncThread)"
|
||||
thread
|
||||
})
|
||||
|
||||
val mainThread: Thread = Thread.currentThread()
|
||||
fun currentThread(): Any {
|
||||
val thread = Thread.currentThread()
|
||||
return if (thread === mainThread) mMainThreadProxy else thread
|
||||
}
|
||||
|
||||
fun currentThread(): Any = Thread.currentThread().let { thread ->
|
||||
if (thread === mainThread) mMainThreadProxy else thread
|
||||
fun runTaskForThreadPool(runnable: Runnable) {
|
||||
if (mTaskCount.addAndGet(1) == 1L) mRuntime.loopers.addAsyncTask(looperTask)
|
||||
threadPool.execute {
|
||||
try {
|
||||
runnable.run()
|
||||
} catch (e: Throwable) {
|
||||
if (!ScriptInterruptedException.causedByInterrupted(e)) {
|
||||
mRuntime.console.error("$this: ", e)
|
||||
}
|
||||
} finally {
|
||||
if (mTaskCount.addAndGet(-1) == 0L){
|
||||
mRuntime.loopers.removeAsyncTask(looperTask)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun start(runnable: Runnable): TimerThread {
|
||||
val thread = createThread(runnable)
|
||||
synchronized(mThreads) {
|
||||
check(!mExit) { str(R.string.error_script_is_on_exiting) }
|
||||
thread.let {
|
||||
mThreads.add(it)
|
||||
it.name = "${mainThread.name} (Spawn-$mSpawnCount)"
|
||||
mSpawnCount++
|
||||
it.start()
|
||||
}
|
||||
check(!mExit) { "script exiting" }
|
||||
mThreads.add(thread)
|
||||
thread.name = mainThread.name + " (Spawn-" + mSpawnCount + ")"
|
||||
mSpawnCount++
|
||||
thread.start()
|
||||
}
|
||||
return thread
|
||||
}
|
||||
|
||||
private fun createThread(runnable: Runnable): TimerThread {
|
||||
val millis = mRuntime.timers.maxCallbackUptimeMillisForAllThreads
|
||||
return object : TimerThread(mRuntime, millis, runnable) {
|
||||
|
||||
return object : TimerThread(mRuntime, runnable) {
|
||||
override fun onExit() {
|
||||
synchronized(mThreads) { mThreads.remove(currentThread()) }
|
||||
super.onExit()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun disposable() = VolatileDispose<Any?>()
|
||||
fun disposable(): VolatileDispose<*> {
|
||||
return VolatileDispose<Any?>()
|
||||
}
|
||||
|
||||
fun atomic(value: Long) = AtomicLong(value)
|
||||
fun atomic(value: Long): AtomicLong {
|
||||
return AtomicLong(value)
|
||||
}
|
||||
|
||||
fun atomic() = AtomicLong()
|
||||
|
||||
fun lock() = ReentrantLock()
|
||||
|
||||
fun shutDownAll() {
|
||||
threadPool.shutdownNow()
|
||||
synchronized(mThreads) {
|
||||
mThreads.apply {
|
||||
forEach { it.interrupt() }
|
||||
clear()
|
||||
for (thread in mThreads) {
|
||||
thread.interrupt()
|
||||
}
|
||||
mThreads.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +100,7 @@ class Threads(private val mRuntime: ScriptRuntime) {
|
||||
}
|
||||
}
|
||||
|
||||
fun hasRunningThreads(): Boolean = synchronized(mThreads) { return mThreads.isNotEmpty() }
|
||||
|
||||
fun hasRunningThreads(): Boolean {
|
||||
synchronized(mThreads) { return mThreads.isNotEmpty() }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package org.autojs.autojs.runtime.api;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import org.autojs.autojs.concurrent.VolatileBox;
|
||||
import org.autojs.autojs.core.looper.Timer;
|
||||
import org.autojs.autojs.core.looper.TimerThread;
|
||||
import org.autojs.autojs.runtime.ScriptRuntime;
|
||||
@@ -11,28 +8,24 @@ import org.autojs.autojs.runtime.ScriptRuntime;
|
||||
/**
|
||||
* Created by Stardust on 2017/7/21.
|
||||
*/
|
||||
|
||||
public class Timers {
|
||||
|
||||
private static final String LOG_TAG = "Timers";
|
||||
|
||||
private final VolatileBox<Long> mMaxCallbackUptimeMillisForAllThreads = new VolatileBox<>(0L);
|
||||
private final Threads mThreads;
|
||||
private final Timer mMainTimer;
|
||||
private final Timer mUiTimer;
|
||||
|
||||
//private VolatileBox<Long> mMaxCallbackUptimeMillisForAllThreads = new VolatileBox<>(0L);
|
||||
private Threads mThreads;
|
||||
private Timer mUiTimer;
|
||||
private ScriptRuntime mRuntime;
|
||||
|
||||
public Timers(ScriptRuntime runtime) {
|
||||
mMainTimer = new Timer(runtime, mMaxCallbackUptimeMillisForAllThreads);
|
||||
mUiTimer = new Timer(runtime, mMaxCallbackUptimeMillisForAllThreads, Looper.getMainLooper());
|
||||
mUiTimer = new Timer(runtime, Looper.getMainLooper());
|
||||
mThreads = runtime.threads;
|
||||
mRuntime = runtime;
|
||||
}
|
||||
|
||||
public Timer getMainTimer() {
|
||||
return mMainTimer;
|
||||
}
|
||||
|
||||
VolatileBox<Long> getMaxCallbackUptimeMillisForAllThreads() {
|
||||
return mMaxCallbackUptimeMillisForAllThreads;
|
||||
return mRuntime.loopers.getMTimer();
|
||||
}
|
||||
|
||||
public Timer getTimerForCurrentThread() {
|
||||
@@ -41,19 +34,27 @@ public class Timers {
|
||||
|
||||
public Timer getTimerForThread(Thread thread) {
|
||||
if (thread == mThreads.getMainThread()) {
|
||||
return mMainTimer;
|
||||
return mRuntime.loopers.getMTimer();
|
||||
}
|
||||
Timer timer = TimerThread.getTimerForThread(thread);
|
||||
if (timer == null && Looper.myLooper() == Looper.getMainLooper()) {
|
||||
return mUiTimer;
|
||||
}
|
||||
return timer;
|
||||
if (timer == null) {
|
||||
return mRuntime.loopers.getMTimer();
|
||||
} else {
|
||||
return timer;
|
||||
}
|
||||
}
|
||||
|
||||
public int setTimeout(Object callback, long delay, Object... args) {
|
||||
return getTimerForCurrentThread().setTimeout(callback, delay, args);
|
||||
}
|
||||
|
||||
public int setTimeout(Object callback) {
|
||||
return setTimeout(callback, 1);
|
||||
}
|
||||
|
||||
public boolean clearTimeout(int id) {
|
||||
return getTimerForCurrentThread().clearTimeout(id);
|
||||
}
|
||||
@@ -62,6 +63,10 @@ public class Timers {
|
||||
return getTimerForCurrentThread().setInterval(listener, interval, args);
|
||||
}
|
||||
|
||||
public int setInterval(Object listener) {
|
||||
return setInterval(listener, 1);
|
||||
}
|
||||
|
||||
public boolean clearInterval(int id) {
|
||||
return getTimerForCurrentThread().clearInterval(id);
|
||||
}
|
||||
@@ -74,17 +79,8 @@ public class Timers {
|
||||
return getTimerForCurrentThread().clearImmediate(id);
|
||||
}
|
||||
|
||||
public boolean hasPendingCallbacks() {
|
||||
//如果是脚本主线程,则检查所有子线程中的定时回调。mFutureCallbackUptimeMillis用来记录所有子线程中定时最久的一个。
|
||||
if (mThreads.getMainThread() == Thread.currentThread()) {
|
||||
return mMaxCallbackUptimeMillisForAllThreads.get() > SystemClock.uptimeMillis();
|
||||
}
|
||||
// 否则检查当前线程的定时回调
|
||||
return getTimerForCurrentThread().hasPendingCallbacks();
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
mMainTimer.removeAllCallbacks();
|
||||
mRuntime.loopers.getMTimer().removeAllCallbacks();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user