1:配置NDK环境变量,将ndk根目录配置到path当中。
2:新建Android工程,在local.properties中配置ndk路径,比如:
ndk.dir=H\:\\opt\\android-sdk-windows19-24\\ndk-bundle
在gradle.properties文件中加上:
android.useDeprecatedNdk=true(为了适配低版本的ndk,不加编译可能会不过),Android Studio3.0之后改成
android.useDeprecatedNdk=true
3:新建Jni类,并写好本地方法,比如:
package com.zhy.youku; /** * 生成头文件之前要运行 set classpath=E:\Android\Studio_App\Jni_Demo\app\src\main\java * Created by kzp on 2017/3/7. */ public class Jni { public native int add(int x, int y); }
4:在cmd中,到工程的java目录(src\main\java)执行 javah com.zhy.youku.Jni,则会生成一个头文件,如:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_zhy_youku_Jni */ #ifndef _Included_com_zhy_youku_Jni #define _Included_com_zhy_youku_Jni #ifdef __cplusplus extern "C" { #endif /* * Class: com_zhy_youku_Jni * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_zhy_youku_Jni_add (JNIEnv *, jobject, jint, jint); #ifdef __cplusplus } #endif #endif
新建一个文件夹,取名jni,并将编译好的头文件剪切进去。(生成头文件的另一个方法:make后在build\intermediates\classes\debug目录下执行javah -classpath . -jni 包名.类名)即可生成。
5:在jni文件夹中,新建一个C的资源文件,文件名字任意取,并include头文件, 完成C方法,如图:
#include "com_zhy_youku_Jni.h" JNIEXPORT jint JNICALL Java_com_zhy_youku_Jni_add (JNIEnv *env, jobject obj, jint jint1, jint jint2){ return jint1 + jint2; }
6:在项目的build.gradle文件中添加ndk配置:
apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "com.zhy.youku" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" ndk{ moduleName "jni_test" //动态库的名称 abiFilters "armeabi-v7a", "x86", "armeabi" //支持的CPU版本 ldLibs "log" //增加打印信息 } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:25.2.0' }
7:最后new Jni(),就可以调用C方法:(不要忘了在调用之前要动态加载本地库,库名要和build.gradle中配置的名字相同);
static { System.loadLibrary("jni_test"); }
调用:
Log.e("m_tag", "result=" + new Jni().add(1,1));
正确打印结果:03-07 14:35:50.186 18069-18069/com.zhy.youku E/m_tag: result=2。
二:上面讲的是java调用C的过程,下面将的是C通过java的反射机制调用java类当中的方法的过程,生成头文件的方法和上面是一样的,不同的是C方法中实现步骤的不同。
1:Jni文件夹为:
package zhy.ccalljava; import android.util.Log; /** * Created by kzp on 2017/3/1. */ public class Jni { static { System.loadLibrary("ccalljava"); } /** * 当执行这个代码的时候,让C代码调用 add(int x, int y) */ public native void callbackAdd(); /** * 当执行这个代码的时候,让C代码调用 helloFromJava() */ public native void callbackHelloFromJava(); /** * 当执行这个代码的时候,让C代码调用 printString() */ public native void callbackPrintString(); /** * 当执行这个代码的时候,让C代码调用静态方法 sayHello(String s)() */ public native void callbackSayHello(); public int add(int x, int y) { Log.e("m_tag", "add() x=" + x + " y=" + y); return x + y; } public void helloFromJava() { Log.e("m_tag", "helloFromJava()"); } public void printString(String s) { Log.e("m_tag","C中输入的:" + s); } public static void sayHello(String s){ Log.e("m_tag", "我是java代码中的JNI." + "java中的sayHello(String s)静态方法,我被C调用了:"+ s); } }
2:C的实现步骤为:
// // Created by kzp on 2017/3/1. // //获取java方法签名:在build->intermediates->classes->debug 目录下 javah -s 报名.类名 #include "zhy_ccalljava_Jni.h" #include <stdlib.h> #include <stdio.h> #include <android/log.h> #define LOG_TAG "m_tag" //增加打印 app.gradld中也要配置 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) /** * add(int x, int y) */ JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackAdd(JNIEnv *env, jobject obj){ //得到字节码 jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni"); //得到方法 (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); //参数:env, 类, 方法名, 方法签名 jmethodID methodId = (*env)->GetMethodID(env, jclazz, "add", "(II)I"); //实例化该类 jobject jobject1 = (*env)->AllocObject(env, jclazz); //调用方法 (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); jint value = (*env)->CallIntMethod(env, jobject1, methodId, 1, 1); //成功调用 LOGE("value=%d\n", value); } /** * helloFromJava() */ JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackHelloFromJava (JNIEnv *env, jobject obj){ jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni"); jmethodID methodId = (*env)->GetMethodID(env, jclazz, "helloFromJava", "()V"); jobject jobject1 = (*env)->AllocObject(env, jclazz); (*env)->CallVoidMethod(env, jobject1, methodId); } /** * 让C代码调用printString(String s) */ JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackPrintString (JNIEnv *env, jobject obj){ jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni"); jmethodID methodId = (*env)->GetMethodID(env, jclazz, "printString", "(Ljava/lang/String;)V"); jobject jobject1 = (*env)->AllocObject(env, jclazz); jstring jstring1 = (*env)->NewStringUTF(env, "i am ard"); (*env)->CallVoidMethod(env, jobject1, methodId, jstring1); } /** * 让C代码调用sayHello(String s) * 该方法是静态的 */ JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackSayHello (JNIEnv *env, jobject obj){ jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni"); jmethodID methodId = (*env)->GetStaticMethodID(env, jclazz, "sayHello", "(Ljava/lang/String;)V"); //jobject jobject1 = (*env)->AllocObject(env, jclazz); sayHello方法是静态的 此步骤省掉 jstring jstring1 = (*env)->NewStringUTF(env, "i am static mothod"); (*env)->CallStaticVoidMethod(env, jclazz, methodId, jstring1); }
3:在MainActivity中调用的步骤为:
package zhy.ccalljava; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ Jni jni; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); jni = new Jni(); findViewById(R.id.add).setOnClickListener(this); findViewById(R.id.string_void).setOnClickListener(this); findViewById(R.id.print).setOnClickListener(this); findViewById(R.id.static_hello).setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.add: jni.callbackAdd(); break; case R.id.string_void: jni.callbackHelloFromJava(); break; case R.id.print: jni.callbackPrintString(); break; case R.id.static_hello: jni.callbackSayHello(); break; } } }
补充:获取java方法签名:在build->intermediates->classes->debug 目录下 javah -s 报名.类名
在C方法中打印log信息的方法:1;导库文件
#include <android/log.h> #define LOG_TAG "m_tag" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
2;在build.gradle的ndk配置中加log,如:
apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "zhy.ccalljava" minSdkVersion 17 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" ndk{ moduleName "ccalljava" ldLibs "log" } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' testCompile 'junit:junit:4.12' }
3;打印信息
LOGE("value=%d\n", value);