【学习Android NDK开发】native code通过JNI调用Java方法

来源:互联网 发布:4维旋转矩阵 编辑:程序博客网 时间:2024/04/28 23:42

转载自:http://www.cnblogs.com/xitang/p/4174619.html

转载自:http://www.cnblogs.com/dyingbleed/archive/2012/10/12/2721781.html

一、简易入门型:



1、建立Android应用

application name: CallJavaMethod

package name: com.example.cjm

main Activity: MainActivity

main Activity layout: activity_main

 

2、Java实现

打开layout/activity_main.xml布局文件,添加按钮控件,ID为“display_button_activity_main”

复制代码
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <Button        android:id="@+id/display_button_activity_main"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerHorizontal="true"        android:layout_centerVertical="true"        android:text="Display"        tools:context=".MainActivity" /></RelativeLayout>
复制代码

 

新建接口CJMListener,定义接口方法displaymessage

package com.example.cjm;public interface CJMListener {    void displayMessage(String message);}

 

新建类CJM,实现接口CJMListener

定义native public方法displaySomething

复制代码
package com.example.cjm;import android.os.Handler;public class CJM implements CJMListener {        static {        System.loadLibrary("cjm");    }        private Handler handler;    private CJMListener listener;        public CJM(CJMListener listener) {        handler = new Handler();        this.listener = listener;    }    @Override    public void displayMessage(final String message) {        handler.post(new Runnable() {            @Override            public void run() {                listener.displayMessage(message);            }        });    }        public native void displaySomething();}
复制代码

 

为MainActivity类,实现CJMListener,添加CMJ对象域

添加Button对象域,引用在布局文件中定义ID为“display_button_activity_main”的按钮控件

public class MainActivity extends AppCompatActivity implements CJMListener{    private Button mDisplayBtn;    private CJM  cjm;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mDisplayBtn=(Button)findViewById(R.id.display_button);        mDisplayBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                cjm.displaySomething();            }        });        cjm=new CJM(this);    }    @Override    public void displayMessage(String message) {        Toast.makeText(this,message,Toast.LENGTH_SHORT).show();    }

当用户按下按钮,执行CJM对象的displaySomething方法

类CJM的displaySomething方法使用native关键字进行声明,将使用native code实现

实现上,在nativie code中,会回调CJM对象的displayerMessage方法,并传递String类型的消息,用于显示

注意,在CJM类中displayerMessage的实现,由于native code并不是在主线程中执行,所以使用了Android的Handler执行MainActivity的方法

 

3、C实现

使用javah为CJM的native方法生成头文件(步骤省略……)

新建.c文件,实现该头文件的方法

复制代码
#include "com_example_cjm_CJM.h"JNIEXPORT void JNICALL Java_com_example_cjm_CJM_displaySomething  (JNIEnv *env, jobject thiz) {    jclass ClassCJM = (*env)->FindClass(env, "com/example/cjm/CJM");    jmethodID MethodDisplayMessage = (*env)->GetMethodID(env, ClassCJM, "displayMessage", "(Ljava/lang/String;)V");    jstring value = (*env)->NewStringUTF(env, "Hello World!");    (*env)->CallVoidMethod(env, thiz, MethodDisplayMessage, value);}

二、深入理解

背景需求

  我们需要在JNI的C代码调用Java代码。实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用。

JNI关键方法讲解。

1. 在同一个类中,调用其他方法

复制代码
JNIEXPORT void JNICALL Java_cn_itcast_ndkcallback_DataProvider_callmethod1  (JNIEnv * env, jobject obj){    //在c代码里面调用java代码里面的方法    // java 反射    //1 . 找到java代码的 class文件    //    jclass      (*FindClass)(JNIEnv*, const char*);    jclass dpclazz = (*env)->FindClass(env,"cn/itcast/ndkcallback/DataProvider");    if(dpclazz==0){        LOGI("find class error");        return;    }    LOGI("find class ");    //2 寻找class里面的方法    //   jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);    jmethodID method1 = (*env)->GetMethodID(env,dpclazz,"helloFromJava","()V");    if(method1==0){        LOGI("find method1 error");        return;    }    LOGI("find method1 ");    //3 .调用这个方法    //    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);    (*env)->CallVoidMethod(env,obj,method1);}
复制代码

 注意: 看红色的内容,如何获得呢? 这个是函数的签名。函数签名借住命令 javap -p -s(这两个参数一定要加入)来获得,放到第二个参数即可。

注意:

1. 要加入上面的参数 -p -s

2. signature后面有时候带“;”,不要丢掉。 主要要仔细检查

示例:

复制代码
void notifyOnStatusReport(int32_t status){    if (curEnv != NULL && curObj != NULL) {        jclass clsstring = curEnv->FindClass("com/baidu/tieba/liveSdk/publisher/LiveNativeSender");        jfieldID mUsercommandCallbackEventListnerFieldId = curEnv->GetFieldID(clsstring,"mStatusEventListener","Lcom/baidu/tieba/liveSdk/publisher/OnStatusEventListener;");        jobject mUsercommandCallbackEventListner = curEnv->GetObjectField(curObj,mUsercommandCallbackEventListnerFieldId);                jclass onStatusEventListenerClsstring = curEnv->FindClass("com/baidu/tieba/liveSdk/publisher/OnStatusEventListener");        jmethodID onStatusReport = curEnv->GetMethodID(onStatusEventListenerClsstring, "onStatusReport",                                         "(I)V");        curEnv->CallVoidMethod(mUsercommandCallbackEventListner, onStatusReport, status);    }}
复制代码

 

这样就可以调用DataProvider中的helloFromJava方法了。

2. 上面的方法是调用的返回值为void的java方法。如果想调用其他类型的。JNI中还提供的许多其他返回类型的方法。

复制代码
    jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);    jobject     (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);    jobject     (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);    jboolean    (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);    jboolean    (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);    jboolean    (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);    jbyte       (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);    jbyte       (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);    jbyte       (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);    jchar       (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);    jchar       (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);    jchar       (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);    jshort      (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);    jshort      (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);    jshort      (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);    jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);    jint        (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);    jint        (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);    jlong       (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);    jlong       (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);    jlong       (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);    jfloat      (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;    jfloat      (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;    jfloat      (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;    jdouble     (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;    jdouble     (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;    jdouble     (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);    void        (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);    void        (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
复制代码

 

3. 如果java中的方法是静态的,就需要调用GetStaticMethodID 和 CallStaticVoidMethod 方法。

复制代码
JNIEXPORT void JNICALL Java_cn_itcast_ndkcallback_DataProvider_callmethod4  (JNIEnv * env, jobject obj){      //1 . 找到java代码的 class文件        //    jclass      (*FindClass)(JNIEnv*, const char*);        jclass dpclazz = (*env)->FindClass(env,"cn/itcast/ndkcallback/DataProvider");        if(dpclazz==0){            LOGI("find class error");            return;        }        LOGI("find class ");        //2 寻找class里面的方法        //   jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);        // 注意 :如果要寻找的方法是静态的方法 那就不能直接去获取methodid        //jmethodID method4 = (*env)->GetMethodID(env,dpclazz,"printStaticStr","(Ljava/lang/String;)V");        //    jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);        jmethodID method4 = (*env)->GetStaticMethodID(env,dpclazz,"printStaticStr","(Ljava/lang/String;)V");        if(method4==0){            LOGI("find method4 error");            return;        }        LOGI("find method4 ");        //3.调用一个静态的java方法        //    void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);        (*env)->CallStaticVoidMethod(env,dpclazz,method4,(*env)->NewStringUTF(env,"static haha in c"));}
复制代码

 

4. 如果C调用的Java方法不在一个类中。

分析:JNI提供的方法都有两个参数:(JNIEnv *env , jobject obj)。 env是JNI提供的方法集合。 obj是上线文。下面的例子的obj不是所需要的上下午,所以要重新创建。

 

复制代码
//obj DemoActivity JNIEXPORT void JNICALL Java_cn_itcast_ndkcallback_DemoActivity_call_1dp_1method1  (JNIEnv * env, jobject obj){    //在c代码里面调用java代码里面的方法        // java 反射        //1 . 找到java代码的 class文件        //    jclass      (*FindClass)(JNIEnv*, const char*);        jclass dpclazz = (*env)->FindClass(env,"cn/itcast/ndkcallback/DataProvider");        if(dpclazz==0){            LOGI("find class error");            return;        }        LOGI("find class ");        //2 寻找class里面的方法        //   jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);        jmethodID method1 = (*env)->GetMethodID(env,dpclazz,"helloFromJava","()V");        if(method1==0){            LOGI("find method1 error");            return;        }        LOGI("find method1 ");        //3 .调用这个方法        //    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);        //    jobject     (*NewObject)(JNIEnv*, jclass, jmethodID, ...);        //  jobject     (*AllocObject)(JNIEnv*, jclass);        jobject dpobj= (*env)->AllocObject(env,dpclazz);        (*env)->CallVoidMethod(env,dpobj,method1);}
复制代码

 

5. 提示

为了避免4中的内容,我们尽量让C要调用的Java方法在同一个类中



0 0