In this week, I found a great POC to run a pure Java standalone app (command line tool, no apk) on Android. But what about running a standalone application using JNI (with .so files) on Android like this?
Java app with JNI
Imagine there is a Java program that loads the JNI shared native library to run and use some Android APIs :
if (!version) { LOGE("Unable to get version string"); } else { LOGI("Build Version - %s\n", version); (*env)->ReleaseStringUTFChars(env, buildVersion, version); } (*env)->DeleteLocalRef(env, buildVersion);
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI "."); } // ...
The working directory structure
1 2 3
. ├── Helloworld.java └── hello-jni.c
Compile and deploy
Now we need to compile both the Java and C sources for Android.
Using javac and dx to compile for a jar file which Android can read:
1 2 3 4 5 6 7
export BUILD_DIR=$PWD/build export JARFILE=helloworld.jar export JAVAC_OPTS=-source 1.8 -target 1.8 -cp .:$ANDROID_HOME/platforms/android-30/android.jar # Compile .java to .class javac $JAVAC_OPTS -d $BUILD_DIR/classes Helloworld.java # Convert .class file into a dex file and embedded in a jar file $ANDROID_HOME/build-tools/30.0.2/dx --output=$BUILD_DIR/$JARFILE --dex ./$BUILD_DIR/classes
Cross-compile the C to Android shared native library via NDK:
adb shell CLASSPATH="/data/local/tmp/helloworld/helloworld.jar" \ LD_LIBRARY_PATH=/data/local/tmp/helloworld \ app_process \ /data/local/tmp/helloworld \ com.example.Helloworld # output Hello from JNI ! Compiled with ABI arm64-v8a. DONE.
Logging in adb logcat:
1 2 3 4 5 6 7 8
07-06 07:57:38.911 21599 21599 D AndroidRuntime: >>>>>> START com.android.internal.os.RuntimeInit uid 2000 <<<<<< 07-06 07:57:38.915 21599 21599 I AndroidRuntime: Using default boot image 07-06 07:57:39.025 21599 21599 D AndroidRuntime: Calling main entry com.example.Helloworld 07-06 07:57:39.027 21599 21599 I @@ : Hello world, vivo V2219A! 07-06 07:57:39.027 21599 21599 I hello-jni: Build Version - 12 07-06 07:57:39.027 21599 21599 I @@ : Hello from JNI ! Compiled with ABI arm64-v8a. 07-06 07:57:39.027 21599 21599 I hello-jni: Build Version - 12 07-06 07:57:39.028 21599 21599 D AndroidRuntime: Shutting down VM
Startup shell script
We can create a startup shell script for this tool:
helloworld
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#!/system/bin/sh HERE="$(cd "$(dirname "$0")" && pwd)" export CLASSPATH=$HERE/helloworld.jar export ANDROID_DATA=$HERE export LD_LIBRARY_PATH="$HERE" if [ -f "$HERE/libc++_shared.so" ]; then # Workaround for https://github.com/android-ndk/ndk/issues/988. export LD_PRELOAD="$HERE/libc++_shared.so" fi echo "try com.example.Helloworld with LD_LIBRARY_PATH=${LD_LIBRARY_PATH}" cmd="app_process $HERE com.example.Helloworld $@" echo "run: $cmd" exec $cmd