fix: app crash when longClick throws exception

This commit is contained in:
hyb1996
2018-01-04 21:13:12 +08:00
parent 2d287f720c
commit a51b9c24ed
11 changed files with 120 additions and 61 deletions

View File

@@ -69,8 +69,7 @@ public class App extends MultiDexApplication {
return; return;
} }
LeakCanary.install(this); LeakCanary.install(this);
if (!BuildConfig.DEBUG) Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(ErrorReportActivity.class));
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(ErrorReportActivity.class));
} }
private void init() { private void init() {

View File

@@ -12,6 +12,7 @@ import android.view.WindowManager;
import android.widget.Toast; import android.widget.Toast;
import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.R;
import com.stardust.util.IntentUtil; import com.stardust.util.IntentUtil;
import com.stardust.view.accessibility.AccessibilityService; import com.stardust.view.accessibility.AccessibilityService;
@@ -25,32 +26,37 @@ public class CrashHandler implements UncaughtExceptionHandler {
private static int crashCount = 0; private static int crashCount = 0;
private static long firstCrashMillis = 0; private static long firstCrashMillis = 0;
private final Class<?> mErrorReportClass; private final Class<?> mErrorReportClass;
private UncaughtExceptionHandler mDefaultHandler;
public CrashHandler(Class<?> errorReportClass) { public CrashHandler(Class<?> errorReportClass) {
this.mErrorReportClass = errorReportClass; this.mErrorReportClass = errorReportClass;
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
} }
public void uncaughtException(Thread thread, Throwable ex) { public void uncaughtException(Thread thread, Throwable ex) {
AccessibilityService service = AccessibilityService.getInstance();
if (service != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
service.disableSelf();
}
if(BuildConfig.DEBUG){
mDefaultHandler.uncaughtException(thread, ex);
return;
}
if (causedByBadWindowToken(ex)) { if (causedByBadWindowToken(ex)) {
Toast.makeText(App.getApp(), R.string.text_no_floating_window_permission, Toast.LENGTH_SHORT).show(); Toast.makeText(App.getApp(), R.string.text_no_floating_window_permission, Toast.LENGTH_SHORT).show();
IntentUtil.goToAppDetailSettings(App.getApp()); IntentUtil.goToAppDetailSettings(App.getApp());
return; }else {
} try {
try { Log.e(TAG, "Uncaught Exception", ex);
Log.e(TAG, "Uncaught Exception", ex); if (crashTooManyTimes())
AccessibilityService service = AccessibilityService.getInstance(); return;
if (service != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { String msg = App.getApp().getString(R.string.sorry_for_crash) + ex.toString();
service.disableSelf(); startErrorReportActivity(msg, throwableToString(ex));
System.exit(1);
} catch (Throwable throwable) {
throwable.printStackTrace();
} }
if (crashTooManyTimes())
return;
String msg = App.getApp().getString(R.string.sorry_for_crash) + ex.toString();
startErrorReportActivity(msg, throwableToString(ex));
System.exit(0);
} catch (Throwable throwable) {
throwable.printStackTrace();
} }
} }
private static boolean causedByBadWindowToken(Throwable e) { private static boolean causedByBadWindowToken(Throwable e) {

View File

@@ -10,10 +10,6 @@ var importClass = function(pack){
} }
} }
var loadJar = function(path){
__runtime__.loadJar(path);
}
__runtime__.bridges.setBridges({ __runtime__.bridges.setBridges({
call: function(func, target, args){ call: function(func, target, args){
var arr = []; var arr = [];
@@ -59,7 +55,24 @@ var __asGlobal__ = function(obj, functions){
} }
} }
require("__general__")(__runtime__, this); var __exitIfError__ = function(action, defReturnValue){
try{
return action();
}catch(err){
log(err.toString());
if(err instanceof java.lang.Throwable){
exit(err);
}else if(err instanceof Error){
exit(new org.mozilla.javascript.EvaluatorException(err.name + ": " + err.message, err.fileName, err.lineNumber));
//new java.lang.RuntimeException(err.name + ": " + err.message + "\n" + err.stack));
}else{
exit();
}
return defReturnValue;
}
};
require("__globals__")(__runtime__, this);
(function(scope){ (function(scope){

View File

@@ -23,9 +23,8 @@ module.exports = function(__runtime__, scope){
scope.isRunning = scope.notStopped; scope.isRunning = scope.notStopped;
scope.exit = function(){ scope.exit = __runtime__.exit.bind(__runtime__);
__runtime__.exit();
}
scope.stop = scope.exit; scope.stop = scope.exit;

View File

@@ -15,29 +15,47 @@ module.exports = function(__runtime__, scope){
}); });
} }
ui.id = function(id){ ui.findById = function(id){
if(!ui.view) if(!ui.view)
return null; return null;
var v = ui.findViewByStringId(ui.view.getChildAt(0), id); var v = ui.findByStringId(ui.view.getChildAt(0), id);
if(v){ if(v){
v = decorate(v); v = decorate(v);
} }
return v; return v;
} }
ui.isUiThread = function(){
importClass(android.os.Looper);
return Looper.myLooper() == Looper.getMainLooper();
}
ui.run = function(action){ ui.run = function(action){
__runtime__.uiHandler.post(action); if(ui.isUiThread()){
} return action();
ui.nonUi = function(action){
if(!ui.__executor__){
ui.__executor__ = java.util.concurrent.Executors.newSingleThreadExecutor();
} }
ui.__executor__.submit(action); var err = null;
var result;
var disposable = scope.threads.disposable();
__runtime__.uiHandler.post(function(){
try{
result = action();
disposable.setAndNotify(true);
}catch(e){
err = e;
disposable.setAndNotify(true);
}
});
disposable.blockedGet();
if(err){
throw err;
}
return result;
} }
ui.postDelay = function(action, delay){ ui.post = function(action, delay){
__runtime__.getUiHandler().postDelay(action, delay); delay = delay || 0;
__runtime__.getUiHandler().postDelay(wrapUiAction(action), delay);
} }
ui.statusBarColor = function(color){ ui.statusBarColor = function(color){
@@ -57,33 +75,25 @@ module.exports = function(__runtime__, scope){
}); });
} }
ui.findViewByStringId = function(view, id){ ui.findByStringId = function(view, id){
return com.stardust.autojs.core.ui.JsViewHelper.findViewByStringId(view, id); return com.stardust.autojs.core.ui.JsViewHelper.findViewByStringId(view, id);
} }
function decorate(view){ function decorate(view){
var view = Object.create(view); var view = Object.create(view);
view._id = function(id){ view._id = function(id){
return ui.findViewByStringId(view, id); return ui.findByStringId(view, id);
} }
view.click = function(listener){ view.click = function(listener){
if(listener){ if(listener){
view.setOnClickListener(new android.view.View.OnClickListener(listener)); view.setOnClickListener(new android.view.View.OnClickListener(wrapUiAction(listener)));
}else{ }else{
view.performClick(); view.performClick();
} }
} }
view.longClick = function(listener){ view.longClick = function(listener){
if(listener){ if(listener){
view.setOnLongClickListener(new android.view.View.OnLongClickListener(function(view){ view.setOnLongClickListener(wrapUiAction(listener, false));
try{
var r = listener(view);
return !!r;
}catch(e){
console.error(e.getMessage());
return false;
}
}));
}else{ }else{
view.performLongClick(); view.performLongClick();
} }
@@ -93,6 +103,15 @@ module.exports = function(__runtime__, scope){
ui.__decorate__ = decorate; ui.__decorate__ = decorate;
function wrapUiAction(action, defReturnValue){
if(typeof(activity) != 'undefined'){
return function(){return action();};
}
return function(){
return __exitIfError__(action, defReturnValue);
}
}
var proxy = __runtime__.ui; var proxy = __runtime__.ui;
proxy.__proxy__ = { proxy.__proxy__ = {
set: function(name, value){ set: function(name, value){
@@ -104,7 +123,7 @@ module.exports = function(__runtime__, scope){
if(cacheView){ if(cacheView){
return cacheView; return cacheView;
} }
cacheView = ui.id(name); cacheView = ui.findById(name);
if(cacheView){ if(cacheView){
ui.__view_cache__[name] = cacheView; ui.__view_cache__[name] = cacheView;
return cacheView; return cacheView;

View File

@@ -110,7 +110,7 @@ public class Drawables {
} else if(value.startsWith("data:")) { } else if(value.startsWith("data:")) {
loadDataInto(view, value); loadDataInto(view, value);
}else { }else {
view.setImageDrawable(com.stardust.autojs.core.ui.inflater.util.Drawables.parse(view, value)); view.setImageDrawable(Drawables.parse(view, value));
} }
} }

View File

@@ -4,9 +4,7 @@ import android.os.Looper;
import android.util.Log; import android.util.Log;
import com.stardust.autojs.BuildConfig; import com.stardust.autojs.BuildConfig;
import com.stardust.autojs.execution.ScriptExecutionListener;
import com.stardust.autojs.rhino.AndroidContextFactory; import com.stardust.autojs.rhino.AndroidContextFactory;
import com.stardust.autojs.rhino.RhinoAndroidHelper;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException; import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.autojs.script.JavaScriptSource; import com.stardust.autojs.script.JavaScriptSource;
import com.stardust.autojs.script.StringScriptSource; import com.stardust.autojs.script.StringScriptSource;
@@ -16,8 +14,6 @@ import com.stardust.pio.UncheckedIOException;
import org.mozilla.javascript.Callable; import org.mozilla.javascript.Callable;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.ImporterTopLevel; import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.ScriptableObject;
@@ -48,7 +44,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
private Scriptable mScriptable; private Scriptable mScriptable;
private Thread mThread; private Thread mThread;
private android.content.Context mAndroidContext; private android.content.Context mAndroidContext;
private Thread.UncaughtExceptionHandler mUiThreadExceptionHandler; private Thread.UncaughtExceptionHandler mUncaughtExceptionHandler;
public RhinoJavaScriptEngine(android.content.Context context) { public RhinoJavaScriptEngine(android.content.Context context) {
mAndroidContext = context; mAndroidContext = context;
@@ -165,8 +161,12 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
context.setWrapFactory(new WrapFactory()); context.setWrapFactory(new WrapFactory());
} }
public void setUiThreadExceptionHandler(Thread.UncaughtExceptionHandler uiThreadExceptionHandler) { public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler uiThreadExceptionHandler) {
mUiThreadExceptionHandler = uiThreadExceptionHandler; mUncaughtExceptionHandler = uiThreadExceptionHandler;
}
public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
return mUncaughtExceptionHandler;
} }
private class WrapFactory extends org.mozilla.javascript.WrapFactory { private class WrapFactory extends org.mozilla.javascript.WrapFactory {
@@ -212,7 +212,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
try { try {
return super.doTopCall(callable, cx, scope, thisObj, args); return super.doTopCall(callable, cx, scope, thisObj, args);
} catch (Exception e) { } catch (Exception e) {
mUiThreadExceptionHandler.uncaughtException(Thread.currentThread(), e); mUncaughtExceptionHandler.uncaughtException(Thread.currentThread(), e);
return null; return null;
} }
} }

View File

@@ -23,7 +23,7 @@ public class LoopedBasedJavaScriptExecution extends RunnableScriptExecution {
long delay = getConfig().delay; long delay = getConfig().delay;
sleep(delay); sleep(delay);
final LoopBasedJavaScriptEngine javaScriptEngine = (LoopBasedJavaScriptEngine) engine; final LoopBasedJavaScriptEngine javaScriptEngine = (LoopBasedJavaScriptEngine) engine;
javaScriptEngine.setUiThreadExceptionHandler((t, e) -> { javaScriptEngine.setUncaughtExceptionHandler((t, e) -> {
javaScriptEngine.forceStop(); javaScriptEngine.forceStop();
getListener().onException(this, (Exception) e); getListener().onException(this, (Exception) e);
}); });

View File

@@ -49,7 +49,7 @@ public class ScriptExecuteActivity extends AppCompatActivity implements Thread.U
mScriptSource = mScriptExecution.getSource(); mScriptSource = mScriptExecution.getSource();
mScriptEngine = mScriptExecution.getEngine(); mScriptEngine = mScriptExecution.getEngine();
mExecutionListener = mScriptExecution.getListener(); mExecutionListener = mScriptExecution.getListener();
((RhinoJavaScriptEngine) mScriptEngine).setUiThreadExceptionHandler((t, e) -> onException((Exception) e)); ((RhinoJavaScriptEngine) mScriptEngine).setUncaughtExceptionHandler((t, e) -> onException((Exception) e));
runScript(); runScript();
} }

View File

@@ -9,6 +9,7 @@ import com.stardust.autojs.ScriptEngineService;
import com.stardust.autojs.annotation.ScriptVariable; import com.stardust.autojs.annotation.ScriptVariable;
import com.stardust.autojs.core.accessibility.AccessibilityBridge; import com.stardust.autojs.core.accessibility.AccessibilityBridge;
import com.stardust.autojs.core.image.Colors; import com.stardust.autojs.core.image.Colors;
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
import com.stardust.autojs.engine.ScriptEngine; import com.stardust.autojs.engine.ScriptEngine;
import com.stardust.autojs.rhino.AndroidClassLoader; import com.stardust.autojs.rhino.AndroidClassLoader;
import com.stardust.autojs.runtime.api.AbstractShell; import com.stardust.autojs.runtime.api.AbstractShell;
@@ -35,6 +36,7 @@ import com.stardust.lang.ThreadCompat;
import com.stardust.pio.UncheckedIOException; import com.stardust.pio.UncheckedIOException;
import com.stardust.util.ClipboardUtil; import com.stardust.util.ClipboardUtil;
import com.stardust.autojs.core.util.ProcessShell; import com.stardust.autojs.core.util.ProcessShell;
import com.stardust.util.Objects;
import com.stardust.util.ScreenMetrics; import com.stardust.util.ScreenMetrics;
import com.stardust.util.SdkVersionUtil; import com.stardust.util.SdkVersionUtil;
import com.stardust.util.Supplier; import com.stardust.util.Supplier;
@@ -42,6 +44,7 @@ import com.stardust.util.UiHandler;
import com.stardust.view.accessibility.AccessibilityInfoProvider; import com.stardust.view.accessibility.AccessibilityInfoProvider;
import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.RhinoException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -305,6 +308,25 @@ public class ScriptRuntime {
} }
} }
public void exit(Object obj) throws Throwable {
mThread.interrupt();
if (!(obj instanceof Throwable)) {
console.error(obj);
return;
}
Throwable e = (Exception) obj;
if (Looper.myLooper() != Looper.getMainLooper()) {
throw e;
} else {
Thread.UncaughtExceptionHandler handler = ((RhinoJavaScriptEngine) engines.myEngine()).getUncaughtExceptionHandler();
if (handler != null) {
handler.uncaughtException(Thread.currentThread(), e);
} else {
console.error(e);
}
}
}
@Deprecated @Deprecated
public void stop() { public void stop() {
exit(); exit();

View File

@@ -20,6 +20,7 @@ import com.stardust.util.ViewUtil;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/** /**
* Created by Stardust on 2017/12/5. * Created by Stardust on 2017/12/5.
@@ -30,7 +31,7 @@ public class Floaty {
private JsLayoutInflater mJsLayoutInflater; private JsLayoutInflater mJsLayoutInflater;
private Context mContext; private Context mContext;
private UiHandler mUiHandler; private UiHandler mUiHandler;
private Set<JsFloatyWindow> mWindows = new HashSet<>(); private Set<JsFloatyWindow> mWindows = new CopyOnWriteArraySet<>();
private ScriptRuntime mRuntime; private ScriptRuntime mRuntime;
public Floaty(UiHandler uiHandler, UI ui, ScriptRuntime runtime) { public Floaty(UiHandler uiHandler, UI ui, ScriptRuntime runtime) {