03_Android NDK中C语言调用Java代码,javah的使用,javap的使用以及生成签名,Android.mk的编写,C代码的编写...
1 案例场景,通过C语言回调Java的代码,案例的最终界面:
2 案例的代码结构如下:
3 编写DataProvider的代码:
package com.example.ndkcallback;
public class DataProvider {
//C调用java空方法
public void helloFromJava(){
System.out.println("哈哈哈 我被调用了");
}
//C调用java中的带两个int参数的方法
public int Add(int x,int y){
int result=x+y;
System.out.println("result:"+result);
return result;
}
//C调用java中参数为string的方法
public void printString(String s) {
System.out.println(s);
}
public static void demo(){
System.out.println("哈哈哈,我是静态方法");
}
public native void callMethod1();
public native void callMethod2();
public native void callMethod3();
public native void callMethod4();
public native void callMethod5();
}
4 通过DataProvider获得头文件
接着带有header的结构如下:
5 编写MainActivity代码:
package com.example.ndkcallback;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
public class MainActivity extends ActionBarActivity {
DataProvider dp;
static {
System.loadLibrary("hello");
}
public void helloFromJava() {
System.out.print("哈哈哈,我被调用了");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dp = new DataProvider();
}
public void click1(View view){
dp.callMethod1();
}
public void click2(View view){
dp.callMethod2();
}
public void click3(View view){
dp.callMethod3();
}
public void click4(View view){
dp.callMethod4();
}
public void click5(View view){
dp.callMethod5();
}
}
6 编写布局文件activity_main.xml:
<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"
tools:context=".MainActivity" >
<Button
android:onClick="click1"
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="回调java中helloFromJava" />
<Button
android:onClick="click2"
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/button1"
android:text="回调java中Add" />
<Button
android:onClick="click3"
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/button2"
android:text="回调java中PrintString" />
<Button
android:onClick="click4"
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/button3"
android:text="回调java中其它类的方法" />
<Button
android:onClick="click5"
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/button4"
android:text="回调java中静态的方法" />
</RelativeLayout>
7 接下来,通过javap命令获得DataProvider的方法签名,在cygwin上进入/NdkCallBack/bin/classes.
命令如下:
[email protected] /cygdrive/e/workspace/Android/NdkCallBack/bin/classes
$ javap -scom.example.ndkcallback.DataProvider
上面红框圈的分别是执行命令和方法签名。这个签名在hello.c中会用到。
7 接下来编写hello.c
#include "com_example_ndkcallback_DataProvider.h"
/**
* 调用:DataProvider中的 public void helloFromJava();
*/
JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod1
(JNIEnv *env, jobject jobject) {
/**
* Class<?> forName = Class.forName("com.example.ndkcallback.DataProvider");
* Method declaredMethod = forName.getDeclaredMethod("helloFromJava", new Class[]{});
* declaredMethod.invoke(forName.newInstance(), new Object[]{});
*/
//jclass (*FindClass)(JNIEnv*, const char*);
jclass clazz = (*env)->FindClass(env,"com/example/ndkcallback/DataProvider");
//jmethodID (*GetMethodID)(JNIEnv*,jclass,const char*,const char*)
//方法签名 参数和返回值
//GetMethodID中参数分别是env指针,class,方法名,方法签名
jmethodID methodId=(*env)->GetMethodID(env,clazz,"helloFromJava","()V");
//通过 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);来调用Java的代码
(*env)->CallVoidMethod(env,jobject,methodId);
}
/**
* 调用:DataProvider中的 public int Add(int, int);
*/
JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod2
(JNIEnv *env, jobject jobject) {
jclass clazz=(*env)->FindClass(env,"com/example/ndkcallback/DataProvider");
//这里的方法签名中有几个I表示有几个参数
jmethodID methodId=(*env)->GetMethodID(env,clazz,"Add","(II)I");
// jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)->CallIntMethod(env,jobject,methodId,3,5);
}
/**
* 调用:DataProvider中的 public void printString(java.lang.String);
*/
JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod3
(JNIEnv *env, jobject jobject) { // 参数 object 就是native方法所在的类
jclass clazz=(*env)->FindClass(env,"com/example/ndkcallback/DataProvider");
jmethodID methodId=(*env)->GetMethodID(env,clazz,"printString","(Ljava/lang/String;)V");
// jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
jstring str=(*env)->NewStringUTF(env,"hello");
(*env)->CallVoidMethod(env,jobject,methodId,str);
}
JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod4
(JNIEnv * env, jobject j){
jclass clazz=(*env)->FindClass(env,"com/example/ndkcallback/MainActivity");
// jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
// 方法签名 参数和返回值
jmethodID methodId=(*env)->GetMethodID(env,clazz,"helloFromJava","()V");
// void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
// 需要创建DataProvider的 对象
// jobject (*AllocObject)(JNIEnv*, jclass);
jobject obj=(*env)->AllocObject(env,clazz); // new MainActivity();
(*env)->CallVoidMethod(env,obj,methodId);
}
JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod5
(JNIEnv * env, jobject j){
jclass clazz=(*env)->FindClass(env,"com/example/ndkcallback/DataProvider");
// jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID methodid=(*env)->GetStaticMethodID(env,clazz,"demo","()V");
//void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
(*env)->CallStaticVoidMethod(env,clazz,methodid);
}
8 编写Android.mk文件,内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libhello
LOCAL_SRC_FILES := Hello.c
include $(BUILD_SHARED_LIBRARY)
9 编写Android的清单文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ndkcallback"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.ndkcallback.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
10 交叉编译,生成.so文件
成功之后,查看Android控制台打印的结果