How to get the Android context instance when calling JNI method?
Asked Answered
C

2

6

I have already read some relevant answers on stackoverflow,but seems no one has answered my question.I will get the android ID from native code,i.e. calling the method getAndroidIDfromNativeCode in C code,(so the JVM is inited in the native code by method create_vm),you know that when calling the method getContentResolver,you must use a Android Context instance to call it,so how to get this Context instance?

    static jstring
    native_code_getAndroidID(JNIEnv *env, jobject thiz)
    {
        jclass c_settings_secure = (*env)->FindClass(env, "android/provider/Settings$Secure");
        jclass c_context = (*env)->FindClass(env,"android/content/Context");
        if(c_settings_secure == NULL || c_context == NULL){
            return NULL;
        }
        //Get the getContentResolver method
        jmethodID m_get_content_resolver = (*env)->GetMethodID(env, c_context, "getContentResolver",
                                                               "()Landroid/content/ContentResolver;");
        if(m_get_content_resolver == NULL){
            return NULL;
        }
        //Get the Settings.Secure.ANDROID_ID constant
        jfieldID f_android_id = (*env)->GetStaticFieldID(env, c_settings_secure, "ANDROID_ID", "Ljava/lang/String;");

        if(f_android_id == NULL){
            return NULL;
        }
        jstring s_android_id = (*env)->GetStaticObjectField(env, c_settings_secure, f_android_id);

        //create a ContentResolver instance context.getContentResolver()
        /*
          where can I get the context instance from Anroid APP??
        */
        jobject o_content_resolver = (*env)->CallObjectMethod(env, context, m_get_content_resolver);
        if(o_content_resolver == NULL || s_android_id == NULL){
            return NULL;
        }
        //get the method getString
        jmethodID m_get_string = (*env)->GetStaticMethodID(env, c_settings_secure, "getString",
                                                           "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;");

        if(m_get_string == NULL){
            return NULL;
        }
        //get the Android ID
        jstring android_id = (*env)->CallStaticObjectMethod(env, c_settings_secure,
                                                            m_get_string,
                                                            o_content_resolver,
                                                            s_android_id);
        return android_id;
    }

    JNIEnv* create_vm() {
      JavaVM* jvm;
      JNIEnv* env;
      JavaVMInitArgs args;
      JavaVMOption options[1];

      /* There is a new JNI_VERSION_1_4, but it doesn't add anything for the purposes of our example. */
      args.version = JNI_VERSION_1_4;
      args.nOptions = 1;
      options[0].optionString = "-Djava.class.path=-jar-path";
      args.options = options;
      args.ignoreUnrecognized = JNI_FALSE;

      JNI_CreateJavaVM(&jvm, (void **)&env, &args);
      return env;
    }

    char *jstringTostr(jstring android)
    {
      ...
    }

    //I will call this function from native code.

    char *getAndroidIDfromNativeCode()
    {

      JNIEnv* env = NULL;

      env = create_vm();
      jstring androidID = native_code_getAndroidID(env,NULL);
      return jstringTostr(androidID);

    }
Cheery answered 22/10, 2017 at 2:21 Comment(4)
Just curious, how many native code are you deploying for different Android archs?Castera
@UsagiMiyamoto just improve some android media player native codes,it is open source,there are a lot.Cheery
What i meant is that i myself know of at least 2 different archs for Android: Intel Atom and ARM7+... You plan to provide your native code for each of them?Castera
there are 5 provided by open source codes,arm64,armv5,armv7a x86,x86_64 ,and I only update codes for armv7a.Cheery
C
8

The following method can get a Context instance.

static jobject getGlobalContext(JNIEnv *env)
{   
    jclass activityThread = (*env)->FindClass(env,"android/app/ActivityThread");
    jmethodID currentActivityThread = (*env)->GetStaticMethodID(env, activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;");
    jobject activityThreadObj = (*env)->CallStaticObjectMethod(env, activityThread, currentActivityThread);
    
    jmethodID getApplication = (*env)->GetMethodID(env, activityThread, "getApplication", "()Landroid/app/Application;");
    jobject context = (*env)->CallObjectMethod(env, activityThreadObj, getApplication);
    return context;
}
Cheery answered 22/10, 2017 at 6:12 Comment(3)
I don't believe this will work for service classes that are not in the same process as the main activity.Titbit
@Titbit I used the above codes under single process program, and have not done some tests on multi-processes program yet.So what about get the context instance in the main activity process and pass the value to other processes?Cheery
If I call this method in JNI_OnLoad and the System.loadLibrary() is under the attachBaseContext() of Application class, the context will be NULL. How to solve it?Reserve
L
0

The last parameter of type jobject is the instance of the class in which you are keeping the Native method declaration.

So if you declare your method inside an activity, you can directly use the last parameter thiz as an instance of your context.

Otherwise you will need to add another parameter of type Context in your native declaration and of type jobject in the method defination.

Lent answered 22/10, 2017 at 2:32 Comment(1)
sorry,what I mean is that what value should I pass to jobject or the "another parameter";when java call c ,we can get the thiz automatically,but when c call java,we need to pass the parameter ourselves.Cheery

© 2022 - 2024 — McMap. All rights reserved.