JNI DETECTED ClassNotFoundException - but classes ARE visible in .apk/.dex?

2

I've run into a problem that I really cannot wrap my head around.

First, I compiled the openFrameworks androidVideoExample for Android on Ubuntu 14.04 (I used gradlew assembleDebug on the command line for that); and then, deployed the resulting .apk on Android device using adb install ./build/outputs/apk/androidVideoExample-debug.apk. I've deployed on Android 4.2.2 device, and Android 6.0.1 device - it works on both without crashing.

Then, I tried using the video component used in that example, the openFrameworks ofVideoPlayer, in a code for my test app (myApp), which previously built and ran fine on either of these devices. When I built it - again with gradlew assembleDebug - it builds fine, and also installs fine using adb install ./build/outputs/apk/myApp-debug.apk on both devices. However, when I run it, it crashes on startup on both devices.

So, I inspected with adb logcat - the 4.2.2 device does not give me a useful trace; however, the 6.0.1 eventually has something like this:

--------- beginning of crash
01-29 23:56:22.551  8938  8973 F libc    : Fatal signal 6 (SIGABRT), code -6 in tid 8973 (Thread-21892)
01-29 23:56:22.653   383   383 D clmlib  : Got activities:0x0000000E
01-29 23:56:22.654   383   383 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-29 23:56:22.654   383   383 F DEBUG   : UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
01-29 23:56:22.655   383   383 F DEBUG   : Build fingerprint: 'XXXXXXXXXXXXXXX:user/release-keys'
01-29 23:56:22.655   383   383 F DEBUG   : Revision: '0'
01-29 23:56:22.655   383   383 F DEBUG   : ABI: 'arm'
01-29 23:56:22.655   383   383 F DEBUG   : pid: 8938, tid: 8973, name: Thread-21892  >>> cc.openframeworks.myApp <<<
01-29 23:56:22.655   383   383 F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
01-29 23:56:22.694   383   383 F DEBUG   : Abort message: 'art/runtime/java_vm_ext.cc:410] JNI DETECTED ERROR IN APPLICATION: JNI NewGlobalRef called with pending exception java.lang.ClassNotFoundException: Didn't find class "cc.openframeworks.OFAndroidVideoPlayer" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]'
01-29 23:56:22.694   383   383 F DEBUG   :     r0 00000000  r1 0000230d  r2 00000006  r3 967e6978
01-29 23:56:22.694   383   383 F DEBUG   :     r4 967e6980  r5 967e6930  r6 0000000b  r7 0000010c
...

First, note these grep snippets about the relationships of these video objects:

openFrameworks/libs/openFrameworks/video/ofVideoPlayer.h:   #include "ofxAndroidVideoPlayer.h"

openFrameworks/addons/ofxAndroid/src/ofxAndroidVideoPlayer.cpp: jclass localClass = env->FindClass("cc/openframeworks/OFAndroidVideoPlayer");

openFrameworks/addons/ofxAndroid/ofAndroidLib/src/cc/openframeworks/OFAndroidVideoPlayer.java:public class OFAndroidVideoPlayer extends OFAndroidObject implements OnFrameAvailableListener {

Ok, so I thought the message 'Didn't find class "cc.openframeworks.OFAndroidVideoPlayer"' means I'm missing the classes somehow from my .apk; however, I tried this:

$ zipgrep OFAndroidVideoPlayer /path/to/build/outputs/apk/myApp-debug.apk
classes.dex:Binary file (standard input) matches
lib/armeabi-v7a/libOFAndroidApp.so:Binary file (standard input) matches

Well, looks like they are there? Tried looking a bit closer, also using dexdump:

$ cd /tmp
$ mkdir unpack
$ cd unpack
$ unzip /path/to/build/outputs/apk/myApp-debug.apk
$ /path/to/sdk/build-tools/19.1.0/dexdump ./classes.dex | grep OFAndroidVideoPlayer
  Class descriptor  : 'Lcc/openframeworks/OFAndroidVideoPlayer$1;'
    #0              : (in Lcc/openframeworks/OFAndroidVideoPlayer$1;)
      type          : 'Lcc/openframeworks/OFAndroidVideoPlayer;'
    #0              : (in Lcc/openframeworks/OFAndroidVideoPlayer$1;)
      type          : '(Lcc/openframeworks/OFAndroidVideoPlayer;)V'
        0x0000 - 0x0006 reg=0 this Lcc/openframeworks/OFAndroidVideoPlayer$1;
    #0              : (in Lcc/openframeworks/OFAndroidVideoPlayer$1;)
        0x0000 - 0x0025 reg=2 this Lcc/openframeworks/OFAndroidVideoPlayer$1;
  source_file_idx   : 358 (OFAndroidVideoPlayer.java)
  Class descriptor  : 'Lcc/openframeworks/OFAndroidVideoPlayer$2;'
    #0              : (in Lcc/openframeworks/OFAndroidVideoPlayer$2;)
      type          : 'Lcc/openframeworks/OFAndroidVideoPlayer;'
    #0              : (in Lcc/openframeworks/OFAndroidVideoPlayer$2;)
      type          : '(Lcc/openframeworks/OFAndroidVideoPlayer;)V'
        0x0000 - 0x0006 reg=0 this Lcc/openframeworks/OFAndroidVideoPlayer$2;
    #0              : (in Lcc/openframeworks/OFAndroidVideoPlayer$2;)
        0x0000 - 0x0001 reg=0 this Lcc/openframeworks/OFAndroidVideoPlayer$2;
  source_file_idx   : 358 (OFAndroidVideoPlayer.java)
  Class descriptor  : 'Lcc/openframeworks/OFAndroidVideoPlayer$3;'
    #0              : (in Lcc/openframeworks/OFAndroidVideoPlayer$3;)
      type          : 'Lcc/openframeworks/OFAndroidVideoPlayer;'
    #0              : (in Lcc/openframeworks/OFAndroidVideoPlayer$3;)
      type          : '(Lcc/openframeworks/OFAndroidVideoPlayer;)V'
        0x0000 - 0x0006 reg=0 this Lcc/openframeworks/OFAndroidVideoPlayer$3;
    #0              : (in Lcc/openframeworks/OFAndroidVideoPlayer$3;)
        0x0000 - 0x0007 reg=2 this Lcc/openframeworks/OFAndroidVideoPlayer$3;
  source_file_idx   : 358 (OFAndroidVideoPlayer.java)
...
    #28              : (in Lcc/openframeworks/OFAndroidVideoPlayer;)
        0x0000 - 0x001d reg=2 this Lcc/openframeworks/OFAndroidVideoPlayer;
  source_file_idx   : 358 (OFAndroidVideoPlayer.java)

Well - at least this says cc/openframeworks/OFAndroidVideoPlayer is in classes.dex, which is in my .apk - so that should mean that the class is there? And in fact, the dexdump ./classes.dex ... | grep OFAndroidVideoPlayer output is identical for myApp-debug.apk and for androidVideoExample-debug.apk!

I also tried dex-method-counts, and for myApp-debug.apk "Overall method count: 710", and so it is far below the 65536 limit which would require multidex.

Otherwise, these classes are compiled by openFrameworks here:

$ find /path/to/openFrameworks/ -name '*AndroidVideoPla*'
...
/path/to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/debug/cc/openframeworks/OFAndroidVideoPlayer$3.class
/path/to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/debug/cc/openframeworks/OFAndroidVideoPlayer.class
/path/to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/debug/cc/openframeworks/OFAndroidVideoPlayer$2.class
/path/to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/debug/cc/openframeworks/OFAndroidVideoPlayer$1.class
/path/to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/release/cc/openframeworks/OFAndroidVideoPlayer$3.class
/path/to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/release/cc/openframeworks/OFAndroidVideoPlayer.class
/path/to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/release/cc/openframeworks/OFAndroidVideoPlayer$2.class
/path/to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/release/cc/openframeworks/OFAndroidVideoPlayer$1.class

So, 4 java classes for debug, and 4 java classes for release - I'm guessing these are the ones that end up in classes.dex.

Finally, I started grepping in the source folders of these projects in binary mode, hoping to find a difference:

grep -rao .......OFAndroidVideoPlayer...... . | tr -cd '\11\12\40-\176' > /tmp/a2

... and I compared these outputs with meld. The only thing I could find, was that the myApp source folder had these as duplicate entries:

 ./.gradle/2.4/taskArtifacts/fileSnapshots.bin: /to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/release/cc/openframeworks/OFAndroidVideoPlayer.class
 ./.gradle/2.4/taskArtifacts/fileSnapshots.bin: /to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/release/cc/openframeworks/OFAndroidVideoPlayer$1.cla
 ./.gradle/2.4/taskArtifacts/fileSnapshots.bin: /to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/release/cc/openframeworks/OFAndroidVideoPlayer$3.cla
 ./.gradle/2.4/taskArtifacts/fileSnapshots.bin: /to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/release/cc/openframeworks/OFAndroidVideoPlayer$2.cla
 ./.gradle/2.4/taskArtifacts/fileSnapshots.bin: /to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/debug/cc/openframeworks/OFAndroidVideoPlayer.class
 ./.gradle/2.4/taskArtifacts/fileSnapshots.bin: /to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/debug/cc/openframeworks/OFAndroidVideoPlayer$2.cla
 ./.gradle/2.4/taskArtifacts/fileSnapshots.bin: /to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/debug/cc/openframeworks/OFAndroidVideoPlayer$3.cla
 ./.gradle/2.4/taskArtifacts/fileSnapshots.bin: /to/openFrameworks/addons/ofxAndroid/ofAndroidLib/build/intermediates/classes/debug/cc/openframeworks/OFAndroidVideoPlayer$1.cla

Now these are duplicates in gradlew's fileSnapshots.bin - not in the .apk per se - and so I don't get duplicate class errors (which they get, say, here). And also, these were duplicates in addition to the already existing entries for OFAndroidVideoPlayer in fileSnapshots.bin in both projects. Then I just simply deleted the contents of .gradle/2.4/taskArtifacts/ directory:

rm -rfv .gradle/2.4/taskArtifacts/*

... and then I did gradlew assembleDebug to rebuild myApp again - and this time, no duplicates were present in fileSnapshots.bin (so myApp's fileSnapshots.bin had the same number of references to OFAndroidVideoPlayer as the androidVideoExample). However, the app still crashes on startup, with the exactly same error.

So, as far as I can see, the OFAndroidVideoPlayer classes are there in the .dex in the .apk for myApp - but JNI still cannot see them at runtime, during application startup? What could possibly be the reason for this - and how could I debug this further?

For instance, what other ways I have to compare the differences between the working androidVideoExample, and the crashing myApp, in respect to the "missing" cc.openframeworks.OFAndroidVideoPlayer Java Class ?

java
android
android-ndk
asked on Stack Overflow Jan 30, 2018 by sdaau

1 Answer

1

Notice that there is no dex files in search path:

...on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]'

Typically this occurs when someone attaches some thread started in native and then tries to load Java classes. Default class loader for such threads doesn't know about your dex'es. You may try to perform that lookup from any thread that was initially created in java. Doing this from JNI_OnLoad() would be ideal.

answered on Stack Overflow Jan 30, 2018 by Sergio

User contributions licensed under CC BY-SA 3.0