clojure-android/lein-droid

startActivityForResult => Exception raised on UI thread (for :target-version "23", device 22)

Opened this issue · 3 comments

Reproduction steps:

  1. Create project with :target-sdk 23:
 lein new droid coa-problem org.example.coaproblem :target-sdk 23
  1. Edit src/clojure/org/example/coaproblem/main.clj to use
    startActivityForResult, e.g. via a button

  2. Appropriately connect device with Android 5.1.1 (22) and compile/install/run:

 lein droid doall
  1. After app launches press button and observe toast indicating error,
    and observe device logs:
 adb logcat

E/neko.debug(29773): Exception raised on UI thread.
E/neko.debug(29773): java.lang.NoClassDefFoundError: android.view.SearchEvent
E/neko.debug(29773): at libcore.reflect.InternalNames.getClass(InternalNames.java:55)
E/neko.debug(29773): at java.lang.Class.getDexCacheType(Class.java:479)
E/neko.debug(29773): at java.lang.reflect.ArtMethod.getDexCacheType(ArtMethod.java:191)
E/neko.debug(29773): at java.lang.reflect.ArtMethod.getParameterTypes(ArtMethod.java:136)
E/neko.debug(29773): at java.lang.reflect.Method.getParameterTypes(Method.java:174)
E/neko.debug(29773): at java.lang.reflect.Method$1.compare(Method.java:60)
E/neko.debug(29773): at java.lang.reflect.Method$1.compare(Method.java:53)
E/neko.debug(29773): at java.util.TimSort.mergeHi(TimSort.java:802)
E/neko.debug(29773): at java.util.TimSort.mergeAt(TimSort.java:481)
E/neko.debug(29773): at java.util.TimSort.mergeForceCollapse(TimSort.java:422)
E/neko.debug(29773): at java.util.TimSort.sort(TimSort.java:219)
E/neko.debug(29773): at java.util.TimSort.sort(TimSort.java:169)
E/neko.debug(29773): at java.util.Arrays.sort(Arrays.java:2010)
E/neko.debug(29773): at java.util.Collections.sort(Collections.java:1883)
E/neko.debug(29773): at libcore.util.CollectionUtils.removeDuplicates(CollectionUtils.java:86)
E/neko.debug(29773): at java.lang.Class.getMethods(Class.java:830)
E/neko.debug(29773): at clojure.lang.Reflector.getMethods(Reflector.java:373)
E/neko.debug(29773): at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:27)
E/neko.debug(29773): at org.example.coaproblem.main$MainActivity_onCreate$fn__1725.invoke(main.clj:24)
E/neko.debug(29773): at neko.listeners.view$on_click_call$reify__1201.onClick(view.clj:26)
E/neko.debug(29773): at android.view.View.performClick(View.java:4789)
E/neko.debug(29773): at android.view.View$PerformClick.run(View.java:19881)
E/neko.debug(29773): at android.os.Handler.handleCallback(Handler.java:739)
E/neko.debug(29773): at android.os.Handler.dispatchMessage(Handler.java:95)
E/neko.debug(29773): at android.os.Looper.loop(Looper.java:135)
E/neko.debug(29773): at android.app.ActivityThread.main(ActivityThread.java:5294)
E/neko.debug(29773): at java.lang.reflect.Method.invoke(Native Method)
E/neko.debug(29773): at java.lang.reflect.Method.invoke(Method.java:372)
E/neko.debug(29773): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
E/neko.debug(29773): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)
E/neko.debug(29773): Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.SearchEvent" on path: DexPathList[[zip file "/data/app/org.example.coaproblem.debug-2/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
E/neko.debug(29773): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
E/neko.debug(29773): at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
E/neko.debug(29773): at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
E/neko.debug(29773): at libcore.reflect.InternalNames.getClass(InternalNames.java:53)
E/neko.debug(29773): ... 29 more
E/neko.debug(29773): Suppressed: java.lang.ClassNotFoundException: android.view.SearchEvent
E/neko.debug(29773): at java.lang.Class.classForName(Native Method)
E/neko.debug(29773): at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
E/neko.debug(29773): at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
E/neko.debug(29773): at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
E/neko.debug(29773): ... 31 more
E/neko.debug(29773): Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available

Notes:

-lein droid is 0.4.3

-AndroidManifest.template.xml indicates:

<uses-sdk android:minSdkVersion="15"
android:targetSdkVersion="{{target-version}}" />

-Changing :target-sdk to "22" in project.clj and following subsequent steps does not
result in an exception.

Hi, i had this issue before and the workaround is to create manually a SearchEvent class.

cd your-project-path
mkdir -p src/java/android/view
touch src/java/android/view/SearchEvent.java

And put the code as in https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/SearchEvent.java in the just created SearchEvent.java file. Then rebuild (lein droid doall) your project.

Let me know if there is other solution. Thank you.

@satchit8 @fnd009 Damn, I get what the problem is now.

The default behavior of gen-class is to proxy absolutely every parent method. This allows you to overload methods "on the fly" when you are using gen-class, to call .superOnCreate and others at will — all this without rebuilding the project. So, when you build your project against target 23, the activity you are defining gets included methods like public boolean onSearchRequested(SearchEvent searchEvent). On an older device these methods lie dormant up until you invoke the very first reflection call. When reflection hits, it scans the compiled class for every method, trying to resolve its arguments' types, and that's where it can't find SearchEvent.

Neko contains a workaround for this, but only for :build-type :release. In release, defactivity (which wraps gen-class) only produces those overloaded methods which are actually used inside defactivity macro. But this wouldn't work so well in debug mode, because, e.g. you wouldn't be able to add onResume handler from the REPL, you'd have to recompile the project anew.

I guess the workaround @fnd009 provided is the only way right now to be able to develop against a newer SDK without having a matching device.

@fnd009 Thanks for the work-around!

@alexander-yakushev Thanks for the explanation.

FWIW, I originally encountered this trying to use external libraries that specified 23 for compileSdkVersion in their respective build.gradle files.