cnlohr / rawdrawandroid

Build android apps without any java, entirely in C and Make
MIT License
3.55k stars 226 forks source link

How to use Java classes in the JNI as an Android NativeActivity #68

Open cnlohr opened 1 year ago

cnlohr commented 1 year ago

You can create a java class:

package org.yourorg.cnfgtest;
import android.app.NativeActivity;
public class MyOtherNativeActivity extends android.app.NativeActivity {}

Then you can compile it into your project as a dex.

javac -source 1.7 -target 1.7 -d bin/org/yourorg/cnfgtest/ -classpath /home/charlesl/android-sdk/platforms/android-30/android.jar -sourcepath src src/org/yourorg/cnfgtest/MyOtherNativeActivity.java
~/android-sdk/build-tools/30.0.2/dx --output=makecapk/ --dex ./bin

And make sure your bin is in the main project (rawdrawandroid does this by default)

You will need to create your java class, then create a Dalvak Class Loader

    jclass DexClassLoaderClass = env->FindClass( envptr, "dalvik/system/DexClassLoader" );
    jmethodID DexClassLoaderConstructorID =  env->GetMethodID( envptr, DexClassLoaderClass,
        "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V" );
    jobject activity = gapp->activity->clazz;
    jclass activityClass = env->GetObjectClass(envptr, activity);
    jmethodID classLoaderID = env->GetMethodID( envptr, activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;" );
    jclass appInfoClass = env->FindClass( envptr, "android/content/pm/ApplicationInfo" );
    jmethodID getAppInfo = env->GetMethodID( envptr, activityClass, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;" );
    jobject appInfo = env->CallObjectMethod( envptr, activity, getAppInfo );
    jfieldID sourceDirID = env->GetFieldID( envptr, appInfoClass, "sourceDir", "Ljava/lang/String;" );
    jfieldID dataDirID = env->GetFieldID( envptr, appInfoClass, "dataDir", "Ljava/lang/String;" );
    jfieldID nativeLibraryDirID = env->GetFieldID( envptr, appInfoClass, "nativeLibraryDir", "Ljava/lang/String;" );
    jobject  sourceDir = env->GetObjectField( envptr, appInfo, sourceDirID );
    jobject  dataDir = env->GetObjectField( envptr, appInfo, dataDirID );
    jobject  nativeLibraryDir = env->GetObjectField( envptr, appInfo, nativeLibraryDirID );
    jobject classLoader = env->CallObjectMethod( envptr, activity, classLoaderID );
    printf( "CL: %p\n", classLoader );
    jobject dcl = env->NewObject( envptr, DexClassLoaderClass, DexClassLoaderConstructorID, 
        sourceDir, dataDir, nativeLibraryDir, classLoader );
    printf( "DCL********************* %p\n", dcl );
    jclass dclClass = env->GetObjectClass( envptr, dcl );
    jmethodID loadClassID = env->GetMethodID( envptr, dclClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;" );

From there, you can just

    //loadClass( java.lang.String ) -> java.lang.Class<?>
    jmethodID loadClassID = env->GetMethodID( envptr, dclClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;" );
// DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) 
    jstring myClassName = env->NewStringUTF( envptr,  "src/org/yourorg/cnfgtest/MyOtherNativeActivity" );
    jclass AccessoryClass = env->CallObjectMethod(envptr, dcl, loadClassID, myClassName );
//DumpObjectClassProperties( AccessoryClass );

Voila! Easy Peasy. 🙄

VadimBoev commented 1 year ago

thx