mirror of
https://github.com/TonyJiangWJ/Auto.js.git
synced 2026-06-21 21:01:43 +08:00
fix: sensors's callback persisted in loopers' ThreadLocal, causes ScriptExecuteActivity memory leak
This commit is contained in:
parent
16709529e3
commit
bb130f56ea
@ -91,7 +91,7 @@ public class App extends MultiDexApplication {
|
||||
// You should not init your app in this process.
|
||||
return;
|
||||
}
|
||||
LeakCanary.install(this);
|
||||
//LeakCanary.install(this);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
||||
@ -65,6 +65,7 @@ public abstract class AutoJs {
|
||||
mNotificationObserver = new AccessibilityNotificationObserver(mContext);
|
||||
mAccessibilityInfoProvider = new AccessibilityInfoProvider(mContext.getPackageManager());
|
||||
mScriptEngineService = buildScriptEngineService();
|
||||
ScriptEngineService.setInstance(mScriptEngineService);
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
@ -67,9 +67,7 @@ public class ScriptEngineService {
|
||||
}
|
||||
|
||||
private void onFinish(ScriptExecution execution) {
|
||||
if (execution.getEngine() instanceof JavaScriptEngine) {
|
||||
((JavaScriptEngine) execution.getEngine()).getRuntime().onExit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -87,6 +85,7 @@ public class ScriptEngineService {
|
||||
};
|
||||
|
||||
|
||||
private static ScriptEngineService sInstance;
|
||||
private final Context mContext;
|
||||
private UiHandler mUiHandler;
|
||||
private final Console mGlobalConsole;
|
||||
@ -211,6 +210,18 @@ public class ScriptEngineService {
|
||||
return mScriptExecutions.get(id);
|
||||
}
|
||||
|
||||
public static void setInstance(ScriptEngineService service) {
|
||||
if (sInstance != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
sInstance = service;
|
||||
}
|
||||
|
||||
public static ScriptEngineService getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
|
||||
private static class EngineLifecycleObserver implements ScriptEngineManager.EngineLifecycleCallback {
|
||||
|
||||
private final Set<ScriptEngineManager.EngineLifecycleCallback> mEngineLifecycleCallbacks = new LinkedHashSet<>();
|
||||
|
||||
@ -5,7 +5,6 @@ import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.dx.util.IntSet;
|
||||
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||
import com.stardust.autojs.runtime.api.Threads;
|
||||
import com.stardust.autojs.runtime.api.Timers;
|
||||
@ -33,7 +32,7 @@ public class Loopers implements MessageQueue.IdleHandler {
|
||||
private volatile ThreadLocal<Boolean> waitWhenIdle = new ThreadLocal<>();
|
||||
private volatile ThreadLocal<HashSet<Integer>> waitIds = new ThreadLocal<>();
|
||||
private volatile ThreadLocal<Integer> maxWaitId = new ThreadLocal<>();
|
||||
private volatile ThreadLocal<CopyOnWriteArrayList<LooperQuitHandler>> looperQuitHanders = new ThreadLocal<>();
|
||||
private volatile ThreadLocal<CopyOnWriteArrayList<LooperQuitHandler>> looperQuitHandlers = new ThreadLocal<>();
|
||||
private volatile Looper mServantLooper;
|
||||
private Timers mTimers;
|
||||
private ScriptRuntime mScriptRuntime;
|
||||
@ -58,17 +57,17 @@ public class Loopers implements MessageQueue.IdleHandler {
|
||||
return mMainLooper;
|
||||
}
|
||||
|
||||
public void addLooperQuiteHandler(LooperQuitHandler handler) {
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHanders.get();
|
||||
public void addLooperQuitHandler(LooperQuitHandler handler) {
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
|
||||
if (handlers == null) {
|
||||
handlers = new CopyOnWriteArrayList<>();
|
||||
looperQuitHanders.set(handlers);
|
||||
looperQuitHandlers.set(handlers);
|
||||
}
|
||||
handlers.add(handler);
|
||||
}
|
||||
|
||||
public boolean removeLooperQuiteHandler(LooperQuitHandler handler) {
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHanders.get();
|
||||
public boolean removeLooperQuitHandler(LooperQuitHandler handler) {
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
|
||||
return handlers != null && handlers.remove(handler);
|
||||
}
|
||||
|
||||
@ -82,7 +81,7 @@ public class Loopers implements MessageQueue.IdleHandler {
|
||||
if (waitWhenIdle.get() || !waitIds.get().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHanders.get();
|
||||
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
|
||||
if (handlers == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -45,6 +45,12 @@ public abstract class JavaScriptEngine extends ScriptEngine.AbstractScriptEngine
|
||||
return (ScriptSource) getTag(TAG_SOURCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void destroy() {
|
||||
mRuntime.onExit();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ScriptEngine@" + Integer.toHexString(hashCode()) + "{" +
|
||||
|
||||
@ -5,6 +5,7 @@ import android.support.annotation.CallSuper;
|
||||
import com.stardust.autojs.execution.ScriptExecution;
|
||||
import com.stardust.autojs.script.ScriptSource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -68,7 +69,7 @@ public interface ScriptEngine<S extends ScriptSource> {
|
||||
abstract class AbstractScriptEngine<S extends ScriptSource> implements ScriptEngine<S> {
|
||||
|
||||
|
||||
private Map<String, Object> mTags = new ConcurrentHashMap<>();
|
||||
private Map<String, Object> mTags = new HashMap<>();
|
||||
private OnDestroyListener mOnDestroyListener;
|
||||
private boolean mDestroyed = false;
|
||||
private Exception mUncaughtException;
|
||||
@ -76,9 +77,11 @@ public interface ScriptEngine<S extends ScriptSource> {
|
||||
|
||||
@Override
|
||||
public synchronized void setTag(String key, Object value) {
|
||||
if (value == null)
|
||||
return;
|
||||
mTags.put(key, value);
|
||||
if (value == null) {
|
||||
mTags.remove(key);
|
||||
} else {
|
||||
mTags.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -7,11 +7,13 @@ import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.stardust.autojs.ScriptEngineService;
|
||||
import com.stardust.autojs.core.eventloop.EventEmitter;
|
||||
import com.stardust.autojs.core.eventloop.SimpleEvent;
|
||||
import com.stardust.autojs.engine.JavaScriptEngine;
|
||||
@ -21,7 +23,6 @@ import com.stardust.autojs.engine.ScriptEngineManager;
|
||||
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||
import com.stardust.autojs.runtime.api.UI;
|
||||
import com.stardust.autojs.script.ScriptSource;
|
||||
import com.stardust.util.IntentExtras;
|
||||
|
||||
import org.mozilla.javascript.NativeObject;
|
||||
|
||||
@ -32,13 +33,13 @@ import org.mozilla.javascript.NativeObject;
|
||||
public class ScriptExecuteActivity extends AppCompatActivity {
|
||||
|
||||
|
||||
private static final String EXTRA_EXECUTION = ScriptExecuteActivity.class.getName() + ".execution";
|
||||
private static final String LOG_TAG = "ScriptExecuteActivity";
|
||||
private static final String EXTRA_EXECUTION_ID = ScriptExecuteActivity.class.getName() + ".execution_id";
|
||||
private Object mResult;
|
||||
private ScriptEngine mScriptEngine;
|
||||
private ScriptExecutionListener mExecutionListener;
|
||||
private ScriptSource mScriptSource;
|
||||
private ActivityScriptExecution mScriptExecution;
|
||||
private IntentExtras mIntentExtras;
|
||||
private ScriptRuntime mRuntime;
|
||||
|
||||
|
||||
@ -47,10 +48,8 @@ public class ScriptExecuteActivity extends AppCompatActivity {
|
||||
public static ActivityScriptExecution execute(Context context, ScriptEngineManager manager, ScriptExecutionTask task) {
|
||||
ActivityScriptExecution execution = new ActivityScriptExecution(manager, task);
|
||||
Intent i = new Intent(context, ScriptExecuteActivity.class)
|
||||
.putExtra(EXTRA_EXECUTION_ID, execution.getId())
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
IntentExtras.newExtras()
|
||||
.put(EXTRA_EXECUTION, execution)
|
||||
.putInIntent(i);
|
||||
context.startActivity(i);
|
||||
return execution;
|
||||
}
|
||||
@ -59,36 +58,30 @@ public class ScriptExecuteActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mIntentExtras = readIntentExtras(savedInstanceState);
|
||||
if (mIntentExtras == null || mIntentExtras.get(EXTRA_EXECUTION) == null) {
|
||||
finish();
|
||||
int executionId = getIntent().getIntExtra(EXTRA_EXECUTION_ID, ScriptExecution.NO_ID);
|
||||
if (executionId == ScriptExecution.NO_ID) {
|
||||
super.finish();
|
||||
return;
|
||||
}
|
||||
mScriptExecution = mIntentExtras.get(EXTRA_EXECUTION);
|
||||
ScriptExecution execution = ScriptEngineService.getInstance().getScriptExecution(executionId);
|
||||
if (execution == null || !(execution instanceof ActivityScriptExecution)) {
|
||||
super.finish();
|
||||
return;
|
||||
}
|
||||
mScriptExecution = (ActivityScriptExecution) execution;
|
||||
mScriptSource = mScriptExecution.getSource();
|
||||
mScriptEngine = mScriptExecution.createEngine(this);
|
||||
mExecutionListener = mScriptExecution.getListener();
|
||||
mRuntime = ((JavaScriptEngine) mScriptEngine).getRuntime();
|
||||
mEventEmitter = new EventEmitter(mRuntime.bridges);
|
||||
runScript();
|
||||
emit("create", savedInstanceState);
|
||||
}
|
||||
|
||||
public EventEmitter getEventEmitter() {
|
||||
return mEventEmitter;
|
||||
}
|
||||
|
||||
private IntentExtras readIntentExtras(Bundle savedInstanceState) {
|
||||
IntentExtras extras = IntentExtras.fromIntentAndRelease(getIntent());
|
||||
if (extras == null && savedInstanceState != null) {
|
||||
int id = savedInstanceState.getInt(IntentExtras.EXTRA_ID, -1);
|
||||
if (id == -1) {
|
||||
return null;
|
||||
}
|
||||
extras = IntentExtras.fromIdAndRelease(id);
|
||||
}
|
||||
return extras;
|
||||
}
|
||||
|
||||
private void runScript() {
|
||||
try {
|
||||
prepare();
|
||||
@ -130,6 +123,10 @@ public class ScriptExecuteActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
if (mScriptExecution == null || mExecutionListener == null) {
|
||||
super.finish();
|
||||
return;
|
||||
}
|
||||
Exception exception = mScriptEngine.getUncaughtException();
|
||||
if (exception != null) {
|
||||
onException(exception);
|
||||
@ -142,6 +139,7 @@ public class ScriptExecuteActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
Log.d(LOG_TAG, "onDestroy");
|
||||
mScriptEngine.put("activity", null);
|
||||
mScriptEngine.setTag("activity", null);
|
||||
mScriptEngine.destroy();
|
||||
@ -151,10 +149,8 @@ public class ScriptExecuteActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (mIntentExtras == null)
|
||||
return;
|
||||
IntentExtras extras = IntentExtras.newExtras().putAll(mIntentExtras);
|
||||
outState.putInt(IntentExtras.EXTRA_ID, extras.getId());
|
||||
if (mScriptExecution != null)
|
||||
outState.putInt(EXTRA_EXECUTION_ID, mScriptExecution.getId());
|
||||
emit("save_instance_state", outState);
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.stardust.app.GlobalAppContext;
|
||||
import com.stardust.autojs.R;
|
||||
@ -333,7 +334,7 @@ public class ScriptRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
public void loadDex(String path){
|
||||
public void loadDex(String path) {
|
||||
path = files.path(path);
|
||||
try {
|
||||
((AndroidClassLoader) ContextFactory.getGlobal().getApplicationClassLoader()).loadDex(new File(path));
|
||||
@ -375,6 +376,7 @@ public class ScriptRuntime {
|
||||
}
|
||||
|
||||
public void onExit() {
|
||||
Log.d(TAG, "on exit");
|
||||
//清除interrupt状态
|
||||
ThreadCompat.interrupted();
|
||||
//悬浮窗需要第一时间关闭以免出现恶意脚本全屏悬浮窗屏蔽屏幕并且在exit中写死循环的问题
|
||||
@ -398,6 +400,7 @@ public class ScriptRuntime {
|
||||
}
|
||||
ignoresException(sensors::unregisterAll);
|
||||
ignoresException(timers::recycle);
|
||||
ignoresException(ui::recycle);
|
||||
}
|
||||
|
||||
private void ignoresException(Runnable r) {
|
||||
|
||||
@ -17,7 +17,6 @@ import java.lang.reflect.Field;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/5.
|
||||
@ -92,7 +91,7 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler {
|
||||
mScriptBridges = runtime.bridges;
|
||||
mNoOpSensorEventEmitter = new SensorEventEmitter(runtime.bridges);
|
||||
mScriptRuntime = runtime;
|
||||
runtime.loopers.addLooperQuiteHandler(this);
|
||||
runtime.loopers.addLooperQuitHandler(this);
|
||||
}
|
||||
|
||||
public SensorEventEmitter register(String sensorName) {
|
||||
@ -167,5 +166,6 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler {
|
||||
}
|
||||
mSensorEventEmitters.clear();
|
||||
}
|
||||
mScriptRuntime.loopers.removeLooperQuitHandler(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,6 +113,10 @@ public class UI extends ProxyObject {
|
||||
}
|
||||
}
|
||||
|
||||
public void recycle(){
|
||||
mDynamicLayoutInflater.setContext(null);
|
||||
}
|
||||
|
||||
private class Drawables extends com.stardust.autojs.core.ui.inflater.util.Drawables {
|
||||
|
||||
@Override
|
||||
|
||||
Loading…
Reference in New Issue
Block a user