Skip to content

Commit 5d35c7a

Browse files
authored
Handle android worker classes (#1493)
1 parent fc63da7 commit 5d35c7a

File tree

5 files changed

+121
-18
lines changed

5 files changed

+121
-18
lines changed

test-app/build-tools/static-binding-generator/src/main/java/org/nativescript/staticbindinggenerator/Generator.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,10 @@ private void writeFieldsToWriter(Writer writer, JavaClass clazz) {
404404
private void writeConstructorsToWriter(Writer writer, JavaClass clazz, DataRow dataRow, String generatedClassName, GenericHierarchyView genericHierarchyView) {
405405
boolean isApplicationClass = androidClassChecker.isApplicationClass(clazz);
406406
boolean isServiceClass = androidClassChecker.isServiceClass(clazz);
407+
boolean isAndroidWorkerClass = androidClassChecker.isAndroidWorkerClass(clazz);
407408

408409
MethodSignatureReifier methodSignatureReifier = new MethodSignatureReifier(genericHierarchyView);
409-
MethodsWriter methodsWriter = new MethodsWriterImpl(writer, suppressCallJSMethodExceptions, isApplicationClass, isServiceClass);
410+
MethodsWriter methodsWriter = new MethodsWriterImpl(writer, suppressCallJSMethodExceptions, isApplicationClass, isServiceClass, isAndroidWorkerClass);
410411
ImplementationObjectChecker implementationObjectChecker = new ImplementationObjectCheckerImpl();
411412

412413
List<String> implObjectMethods = Arrays.asList(dataRow.getMethods());
@@ -425,8 +426,9 @@ private void writeConstructorsToWriter(Writer writer, JavaClass clazz, DataRow d
425426
private void writeMethodsToWriter(Writer writer, GenericHierarchyView genericHierarchyView, Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews, JavaClass clazz, List<String> userImplementedMethods, List<JavaClass> userImplementedInterfaces, String packageName) {
426427
boolean isApplicationClass = androidClassChecker.isApplicationClass(clazz);
427428
boolean isServiceClass = androidClassChecker.isServiceClass(clazz);
429+
boolean isAndroidWorkerClass = androidClassChecker.isAndroidWorkerClass(clazz);
428430

429-
MethodsWriter methodsWriter = new MethodsWriterImpl(writer, suppressCallJSMethodExceptions, isApplicationClass, isServiceClass);
431+
MethodsWriter methodsWriter = new MethodsWriterImpl(writer, suppressCallJSMethodExceptions, isApplicationClass, isServiceClass, isAndroidWorkerClass);
430432

431433
InheritedMethodsCollector inheritedMethodsCollector = new InheritedMethodsCollectorImpl.Builder()
432434
.forJavaClass(clazz)

test-app/build-tools/static-binding-generator/src/main/java/org/nativescript/staticbindinggenerator/generating/parsing/checkers/AndroidClassChecker.java

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ public interface AndroidClassChecker {
66
boolean isActivityClass(JavaClass clazz);
77
boolean isApplicationClass(JavaClass clazz);
88
boolean isServiceClass(JavaClass javaClass);
9+
boolean isAndroidWorkerClass(JavaClass javaClass);
910
}

test-app/build-tools/static-binding-generator/src/main/java/org/nativescript/staticbindinggenerator/generating/parsing/checkers/impl/AndroidClassCheckerImpl.java

+11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class AndroidClassCheckerImpl implements AndroidClassChecker {
1414

1515
private static final String APPLICATION_CLASS_NAME = "android.app.Application";
1616
private static final String SERVICE_CLASS_NAME = "android.app.Service";
17+
private static final String ANDROID_WORKER_CLASS_NAME = "androidx.work.ListenableWorker";
1718
private static final List<String> ACTIVITY_TYPES = Arrays.asList("android.app.Activity","android.support.v7.app.AppCompatActivity","androidx.appcompat.app.AppCompatActivity");
1819

1920
private final ClassHierarchyParser classHierarchyParser;
@@ -58,4 +59,14 @@ public boolean isServiceClass(JavaClass javaClass){
5859
HierarchyView hierarchyView = classHierarchyParser.getClassHierarchy(javaClass);
5960
return hierarchyView.getAllParentClassesNames().contains(SERVICE_CLASS_NAME);
6061
}
62+
63+
@Override
64+
public boolean isAndroidWorkerClass(JavaClass javaClass) {
65+
if(javaClass.getClass().equals(ANDROID_WORKER_CLASS_NAME)){
66+
return true;
67+
}
68+
69+
HierarchyView hierarchyView = classHierarchyParser.getClassHierarchy(javaClass);
70+
return hierarchyView.getAllParentClassesNames().contains(ANDROID_WORKER_CLASS_NAME);
71+
}
6172
}

test-app/build-tools/static-binding-generator/src/main/java/org/nativescript/staticbindinggenerator/generating/writing/impl/MethodsWriterImpl.java

+28-7
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
22

33
import org.apache.bcel.generic.Type;
44
import org.nativescript.staticbindinggenerator.DefaultValues;
5-
import org.nativescript.staticbindinggenerator.Generator;
65
import org.nativescript.staticbindinggenerator.InputParameters;
76
import org.nativescript.staticbindinggenerator.Writer;
87
import org.nativescript.staticbindinggenerator.generating.parsing.methods.ReifiedJavaMethod;
98
import org.nativescript.staticbindinggenerator.generating.writing.MethodsWriter;
109
import org.nativescript.staticbindinggenerator.naming.BcelNamingUtil;
1110

12-
import java.util.Arrays;
1311
import java.util.List;
1412

1513
public class MethodsWriterImpl implements MethodsWriter {
@@ -24,6 +22,8 @@ public class MethodsWriterImpl implements MethodsWriter {
2422
private static final String GET_INSTANCE_METHOD_SIGNATURE_PATTERN = "public static %s getInstance()";
2523
private static final String RUNTIME_CALL_JS_METHOD_CALL_PATTERN = "com.tns.Runtime.callJSMethod(this, \"%s\", %s.class, " + ARGS_VARIABLE_NAME + ")";
2624
private static final String RUNTIME_CALL_JS_CONSTRUCTOR_METHOD_CALL_PATTERN = "com.tns.Runtime.callJSMethod(this, \"%s\", %s.class, true," + ARGS_VARIABLE_NAME + ")";
25+
private static final String RUNTIME_CALL_JS_METHOD_FROM_POSSIBLE_NON_MAIN_THREAD_CALL_PATTERN = "com.tns.Runtime.callJSMethodFromPossibleNonMainThread(this, \"%s\", %s.class, " + ARGS_VARIABLE_NAME + ")";
26+
private static final String RUNTIME_CALL_JS_CONSTRUCTOR_METHOD_FROM_POSSIBLE_NON_MAIN_THREAD_CALL_PATTERN = "com.tns.Runtime.callJSMethodFromPossibleNonMainThread(this, \"%s\", %s.class, true," + ARGS_VARIABLE_NAME + ")";
2727
private static final String ARGS_VARIABLE_PATTERN = "java.lang.Object[] " + ARGS_VARIABLE_NAME + " = new java.lang.Object[%d];";
2828

2929
private static final String THROWS_DECLARATION_BEGINNING = " throws ";
@@ -41,6 +41,7 @@ public class MethodsWriterImpl implements MethodsWriter {
4141
private static final String RUNTIME_INIT_METHOD_CALL_STATEMENT = "com.tns.Runtime " + RUNTIME_VARIABLE_NAME + " = com.tns.RuntimeHelper.initRuntime(this);";
4242
private static final String RUNTIME_RUN_METHOD_CALL_STATEMENT = RUNTIME_VARIABLE_NAME + ".run();";
4343
private static final String RUNTIME_INIT_INSTANCE_METHOD_CALL_STATEMENT = "com.tns.Runtime.initInstance(this);";
44+
private static final String RUNTIME_INIT_INSTANCE_FROM_POSSIBLE_NON_MAIN_THREAD_METHOD_CALL_STATEMENT = "com.tns.Runtime.initInstanceFromPossibleNonMainThread(this);";
4445

4546
private static final String RUNTIME_IS_INITIALIZED_METHOD_CALL = "com.tns.Runtime.isInitialized()";
4647

@@ -60,13 +61,15 @@ public class MethodsWriterImpl implements MethodsWriter {
6061
private final boolean shouldSuppressCallJsMethodExceptions;
6162
private final boolean isForApplicationClass;
6263
private final boolean isForServiceClass;
64+
private final boolean isForAndroidWorkerClass;
6365

6466

65-
public MethodsWriterImpl(final Writer writer, boolean shouldSuppressCallJsMethodExceptions, boolean isForApplicationClass, boolean isForServiceClass) {
67+
public MethodsWriterImpl(final Writer writer, boolean shouldSuppressCallJsMethodExceptions, boolean isForApplicationClass, boolean isForServiceClass, boolean isForAndroidWorkerClass) {
6668
this.writer = writer;
6769
this.shouldSuppressCallJsMethodExceptions = shouldSuppressCallJsMethodExceptions;
6870
this.isForApplicationClass = isForApplicationClass;
6971
this.isForServiceClass = isForServiceClass;
72+
this.isForAndroidWorkerClass = isForAndroidWorkerClass;
7073
}
7174

7275
@Override
@@ -106,8 +109,10 @@ public void writeConstructor(String className, ReifiedJavaMethod method, boolean
106109
writer.write(CLOSING_ROUND_BRACKET_LITERAL);
107110
writer.write(END_OF_STATEMENT_LITERAL);
108111

109-
if (!isForApplicationClass && !isForServiceClass) {
112+
if (!isForApplicationClass && !isForServiceClass && !isForAndroidWorkerClass) {
110113
writer.write(RUNTIME_INIT_INSTANCE_METHOD_CALL_STATEMENT);
114+
} else if (isForAndroidWorkerClass) {
115+
writer.write(RUNTIME_INIT_INSTANCE_FROM_POSSIBLE_NON_MAIN_THREAD_METHOD_CALL_STATEMENT);
111116
}
112117

113118
if (hasUserImplementedInitMethod) {
@@ -236,9 +241,9 @@ private void writeSuppressDeprecationsToWriter() {
236241
}
237242

238243
private void writeMethodSignature(ReifiedJavaMethod method, boolean isUserImplemented) {
239-
if(method.isDeprecated() &&
244+
if (method.isDeprecated() &&
240245
(!isUserImplemented // we want to show warnings only for methods implemented by the user, but not for the SBG auto implemented abstract methods
241-
|| InputParameters.getCurrent().getSuppressDeprecationWarnings())) {
246+
|| InputParameters.getCurrent().getSuppressDeprecationWarnings())) {
242247
writeSuppressDeprecationsToWriter();
243248
}
244249

@@ -279,7 +284,7 @@ private void writeMethodBody(ReifiedJavaMethod method) {
279284
writeArgumentsVariableForMethodCall(method);
280285
writeCallJsMethodExceptionsSuppressBlockBeginningIfNecessary();
281286

282-
String methodCallPattern = method.isConstructor() ? RUNTIME_CALL_JS_CONSTRUCTOR_METHOD_CALL_PATTERN : RUNTIME_CALL_JS_METHOD_CALL_PATTERN;
287+
String methodCallPattern = getMethodCallPattern(method);
283288
String runtimeCallJsMethodCall = String.format(methodCallPattern, getMethodName(method), BcelNamingUtil.resolveBcelTypeName(returnType));
284289

285290
if (!returnType.equals(Type.VOID)) {
@@ -292,6 +297,22 @@ private void writeMethodBody(ReifiedJavaMethod method) {
292297
writeCallJsMethodExceptionsSuppressBlockClosingIfNecessary(returnType, getMethodName(method));
293298
}
294299

300+
private String getMethodCallPattern(ReifiedJavaMethod method) {
301+
if (method.isConstructor()) {
302+
if (!isForAndroidWorkerClass) {
303+
return RUNTIME_CALL_JS_CONSTRUCTOR_METHOD_CALL_PATTERN;
304+
} else {
305+
return RUNTIME_CALL_JS_CONSTRUCTOR_METHOD_FROM_POSSIBLE_NON_MAIN_THREAD_CALL_PATTERN;
306+
}
307+
} else {
308+
if (!isForAndroidWorkerClass) {
309+
return RUNTIME_CALL_JS_METHOD_CALL_PATTERN;
310+
} else {
311+
return RUNTIME_CALL_JS_METHOD_FROM_POSSIBLE_NON_MAIN_THREAD_CALL_PATTERN;
312+
}
313+
}
314+
}
315+
295316
private String getMethodName(ReifiedJavaMethod method) {
296317
if (method.isConstructor()) {
297318
return "init";

test-app/runtime/src/main/java/com/tns/Runtime.java

+77-9
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@
2828
import java.util.HashMap;
2929
import java.util.Map;
3030
import java.util.Queue;
31+
import java.util.concurrent.Callable;
3132
import java.util.concurrent.ConcurrentHashMap;
3233
import java.util.concurrent.ConcurrentLinkedQueue;
34+
import java.util.concurrent.ExecutionException;
35+
import java.util.concurrent.FutureTask;
36+
import java.util.concurrent.RunnableFuture;
3337
import java.util.concurrent.atomic.AtomicInteger;
3438

3539
public class Runtime {
@@ -551,7 +555,6 @@ public static Runtime initializeRuntimeWithConfiguration(StaticConfiguration con
551555
WorkThreadScheduler mainThreadScheduler = new WorkThreadScheduler(new MainThreadHandler(Looper.myLooper()));
552556
DynamicConfiguration dynamicConfiguration = new DynamicConfiguration(0, mainThreadScheduler, null);
553557
Runtime runtime = initRuntime(dynamicConfiguration);
554-
555558
return runtime;
556559
}
557560

@@ -737,6 +740,37 @@ public void unlock() {
737740
unlock(runtimeId);
738741
}
739742

743+
public static void initInstanceFromPossibleNonMainThread(final Object instance) {
744+
if (isNotOnMainThread()) {
745+
Runnable runnable = new Runnable() {
746+
@Override
747+
public void run() {
748+
initInstance(instance);
749+
}
750+
};
751+
752+
RunnableFuture<Void> task = new FutureTask<>(runnable, null);
753+
getMainThreadHandler().post(task);
754+
755+
try {
756+
task.get(); // this will block until Runnable completes
757+
} catch (InterruptedException | ExecutionException e) {
758+
throw new RuntimeException(e);
759+
}
760+
761+
} else {
762+
initInstance(instance);
763+
}
764+
}
765+
766+
private static Handler getMainThreadHandler() {
767+
return new Handler(Looper.getMainLooper());
768+
}
769+
770+
private static boolean isNotOnMainThread() {
771+
return Looper.myLooper() != Looper.getMainLooper();
772+
}
773+
740774
public static void initInstance(Object instance) {
741775
ManualInstrumentation.Frame frame = ManualInstrumentation.start("Runtime.initInstance");
742776
try {
@@ -1049,26 +1083,60 @@ private int getOrCreateJavaObjectID(Object obj) {
10491083
return result;
10501084
}
10511085

1086+
public static Object callJSMethodFromPossibleNonMainThread(Object javaObject, String methodName, Class<?> retType, Object... args) throws NativeScriptException {
1087+
return callJSMethodFromPossibleNonMainThread(javaObject, methodName, retType, false /* isConstructor */, args);
1088+
}
1089+
1090+
public static Object callJSMethodFromPossibleNonMainThread(Object javaObject, String methodName, Class<?> retType, boolean isConstructor, Object... args) throws NativeScriptException {
1091+
return callJSMethodFromPossibleNonMainThread(javaObject, methodName, retType, isConstructor, 0, args);
1092+
}
1093+
1094+
public static Object callJSMethodFromPossibleNonMainThread(Object javaObject, String methodName, boolean isConstructor, Object... args) throws NativeScriptException {
1095+
return callJSMethodFromPossibleNonMainThread(javaObject, methodName, void.class, isConstructor, 0, args);
1096+
}
1097+
1098+
public static Object callJSMethodFromPossibleNonMainThread(final Object javaObject, final String methodName, final Class<?> retType, final boolean isConstructor, final long delay, final Object... args) throws NativeScriptException {
1099+
if (isNotOnMainThread()) {
1100+
Callable<Object> callable = new Callable<Object>() {
1101+
@Override
1102+
public Object call() {
1103+
return callJSMethod(javaObject, methodName, retType, isConstructor, delay, args);
1104+
}
1105+
};
1106+
1107+
RunnableFuture<Object> task = new FutureTask<>(callable);
1108+
getMainThreadHandler().post(task);
1109+
1110+
try {
1111+
return task.get(); // this will block until Runnable completes
1112+
} catch (InterruptedException | ExecutionException e) {
1113+
throw new RuntimeException(e);
1114+
}
1115+
1116+
} else {
1117+
return callJSMethod(javaObject, methodName, retType, isConstructor, delay, args);
1118+
}
1119+
}
1120+
1121+
10521122
// sends args in pairs (typeID, value, null) except for objects where its
10531123
// (typeid, javaObjectID, javaJNIClassPath)
10541124
public static Object callJSMethod(Object javaObject, String methodName, Class<?> retType, Object... args) throws NativeScriptException {
10551125
return callJSMethod(javaObject, methodName, retType, false /* isConstructor */, args);
10561126
}
10571127

1058-
public static Object callJSMethodWithDelay(Object javaObject, String methodName, Class<?> retType, long delay, Object... args) throws NativeScriptException {
1059-
return callJSMethod(javaObject, methodName, retType, false /* isConstructor */, delay, args);
1060-
}
1061-
10621128
public static Object callJSMethod(Object javaObject, String methodName, Class<?> retType, boolean isConstructor, Object... args) throws NativeScriptException {
1063-
Object ret = callJSMethod(javaObject, methodName, retType, isConstructor, 0, args);
1064-
1065-
return ret;
1129+
return callJSMethod(javaObject, methodName, retType, isConstructor, 0, args);
10661130
}
10671131

10681132
public static Object callJSMethod(Object javaObject, String methodName, boolean isConstructor, Object... args) throws NativeScriptException {
10691133
return callJSMethod(javaObject, methodName, void.class, isConstructor, 0, args);
10701134
}
10711135

1136+
public static Object callJSMethodWithDelay(Object javaObject, String methodName, Class<?> retType, long delay, Object... args) throws NativeScriptException {
1137+
return callJSMethod(javaObject, methodName, retType, false /* isConstructor */, delay, args);
1138+
}
1139+
10721140
public static Object callJSMethod(Object javaObject, String methodName, Class<?> retType, boolean isConstructor, long delay, Object... args) throws NativeScriptException {
10731141
Runtime runtime = Runtime.getCurrentRuntime();
10741142

@@ -1278,7 +1346,7 @@ private static Class<?> getCachedClass(String className) {
12781346
try {
12791347
clazz = classStorageService.retrieveClass(className);
12801348
return clazz;
1281-
} catch (RuntimeException e){
1349+
} catch (RuntimeException e) {
12821350
return null;
12831351
}
12841352
}

0 commit comments

Comments
 (0)