0、前言
ndk的开发,可以简单理解为c代码和java代码互调;
1、常见逻辑就是java通过loadLibrary方法和声明native方法加载c函数库和对应函数;
2、c代码编写对应函数,如果是静态注册的方法,那么根据java生成的.h头文件来编写c代码;
3、如果是动态注册的方法,那么通过JNInativeMethod/registerNatives/JIN_onload加载c函数。
1、jni
全称:java native interface
作用:用于java代码和C++、c代码的交互(代码混编);
分类使用:Jni静态注册、jni动态注册
2、静态注册
作用:绑定java方法和C/C++方法的方式之一;
java层操作和c层操作以及整体编译流程:
-定义被native修饰的方法
-根据java代码生成.h头文件(javah -jni 类的包名路径)
-编写C/C++代码,导入.h头文件,实现我们.h头文件中方法
-编写(配置)两个mk文件:application.mk/android.mk
-通过ndk-build生成so文件;
-java代码中加载so文件(system.loadlibrary)
-补充:获取so库的名称
-libs文件下去头去尾(头:lib;尾:.so);
-在android.mk直接复制模块名称;
java层:
public class MainActivity extends Activity {
//1.调用c层的一个字符串;
public String ZD = "我是ZD";
//2.java层调用C层,从c调用java层的普通和静态字段
public static String ZD1 = "我是静态ZD";
private static Context context;
//2.java层调用C层,从c调用java层的普通和静态方法
public void mothod(){
Toast.makeText(this,"我是普通方法" , 1).show();
}
public static void staticmothod(){
Toast.makeText(context,"我是静态方法" , 1).show();
}
static{
System.loadLibrary("JNIstudy");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//context=this;
Toast.makeText(this,Getstring() , 1).show();
Toast.makeText(this,GetstrZD(), 1).show();
Toast.makeText(this,GetstrstaticZD() , 1).show();
javatoc();
}
public native String GetstrZD();
public native String GetstrstaticZD();
public native String Getstring();
public native void javatoc();
public String getstring1(){
return null;};
}
根据java生成的.h头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_jinstudy_MainActivity */
#ifndef _Included_com_example_jinstudy_MainActivity
#define _Included_com_example_jinstudy_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_jinstudy_MainActivity
* Method: GetstrZD
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jinstudy_MainActivity_GetstrZD
(JNIEnv *, jobject);
/*
* Class: com_example_jinstudy_MainActivity
* Method: GetstrstaticZD
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jinstudy_MainActivity_GetstrstaticZD
(JNIEnv *, jobject);
/*
* Class: com_example_jinstudy_MainActivity
* Method: Getstring
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jinstudy_MainActivity_Getstring
(JNIEnv *, jobject);
/*
* Class: com_example_jinstudy_MainActivity
* Method: javatoc
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_jinstudy_MainActivity_javatoc
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
c代码:
#include <JNIstudy.h>
JNIEXPORT jstring JNICALL Java_com_example_jinstudy_MainActivity_Getstring
(JNIEnv *env, jobject obj){
//类型转换 NewStringUTF(env,字符串)
jstring str= (*env)->NewStringUTF(env, "hello NDK");
return str;
}
//调用java层普通字段
JNIEXPORT jstring JNICALL Java_com_example_jinstudy_MainActivity_GetstrZD
(JNIEnv *env, jobject obj){
//1.获取类
//FindClass:第一个env,第二个:字段所在类的路径(点换成斜杠)
jclass _jclass = (*env)->FindClass(env,"com/example/jinstudy/MainActivity");
//2.获取字段ID
//GetFieldID:第一个env,第二个FindClass的返回值,第三个java层字段的名称,第四个java层字段的签名
jfieldID _jfieldID = (*env)->GetFieldID(env, _jclass, "ZD", "Ljava/lang/String;");
//3.获取字段
//GetObjectField:第一个env,第二个obj,第三个GetFieldID的返回值
jobject str= (*env)->GetObjectField(env, obj, _jfieldID);
return str;
}
//调用java层静态字段
JNIEXPORT jstring JNICALL Java_com_example_jinstudy_MainActivity_GetstrstaticZD
(JNIEnv *env, jobject obj){
//1.获取类:FindClass()
jclass _jclass = (*env)->FindClass(env,"com/example/jinstudy/MainActivity");
//2.获取静态字段ID
jfieldID _jfieldID= (*env)->GetStaticFieldID(env, _jclass, "ZD1","Ljava/lang/String;");
//3.获取静态字段
//GetStaticObjectField:第一个env,第二个是类,既FindClass的返回值,第三个参数GetStaticFieldID的返回值
jobject str= (*env)->GetStaticObjectField(env, _jclass, _jfieldID);
return str;
}
JNIEXPORT void JNICALL Java_com_example_jinstudy_MainActivity_javatoc
(JNIEnv *env, jobject obj){
jclass _jclass = (*env)->FindClass(env,"com/example/jinstudy/MainActivity");
//1,调用普通方法
//GetMethodID:第一个env,第二个FindClass返回值,第三个java层方法的名称,第四个java层方法的签名(参数+返回值)
jmethodID _jmethodID = (*env)->GetMethodID(env, _jclass, "mothod", "()V");
//CallVoidMethod:第一个env,第二个obj,第三个GetMethodID的返回值
(*env)->CallVoidMethod(env, obj, _jmethodID);
//2,调用静态方法
jmethodID _jmethodID1 = (*env)->GetStaticMethodID(env, _jclass, "staticmothod", "()V");
(*env)->CallStaticVoidMethod(env, _jclass, _jmethodID1);
}
3、动态注册
作用:.绑定java方法和C/C++方法的方式之一
流程:
-java中定义native的方法
-创建C++代码代码,导入头文件;
-编写(配置)两个mk文件:application.mk/android.mk
-JNInativeMethod:绑定java方法和C/C++的方法
-registerNatives(4个参数):注册java层相应的类以及方法
-使用jni.h中JNI_onload进行判断:注册是否成功(JIN_onload:系统调用,相当于java中的psvm:public static void main)
-ndk-build生成so,
-java代码中加载so文件(system.loadlibrary)
java层代码,跟静态注册其实是一样的逻辑:
public class MainActivity extends Activity {
private EditText first;
private EditText second;
private Button add;
private Button sub;
private Button mul;
private Button div;
//编辑框的值
private float one;
private float two;
static{
System.loadLibrary("jisuanqi");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
//运算
yunsuan();
}
private void yunsuan() {
// TODO Auto-generated method stub
OnClickListener cl= new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.add:
//获取编辑框的值
one=Float.parseFloat(first.getText().toString());
two=Float.parseFloat(second.getText().toString());
Toast.makeText(MainActivity.this, add(one, two)+"", 1).show();
break;
case R.id.sub:
//获取编辑框的值
one=Float.parseFloat(first.getText().toString());
two=Float.parseFloat(second.getText().toString());
Toast.makeText(MainActivity.this, sub(one, two)+"", 1).show();
break;
case R.id.mul:
//获取编辑框的值
one=Float.parseFloat(first.getText().toString());
two=Float.parseFloat(second.getText().toString());
Toast.makeText(MainActivity.this, mul(one, two)+"", 1).show();
break;
case R.id.div:
//获取编辑框的值
one=Float.parseFloat(first.getText().toString());
two=Float.parseFloat(second.getText().toString());
Toast.makeText(MainActivity.this, div(one, two)+"", 1).show();
break;
default:
break;
}
}
};
add.setOnClickListener(cl);
sub.setOnClickListener(cl);
mul.setOnClickListener(cl);
div.setOnClickListener(cl);
}
private void init() {
// TODO Auto-generated method stub
//编辑框的绑定
first=(EditText) findViewById(R.id.editText1);
second=(EditText) findViewById(R.id.editText2);
//按钮的绑定
add=(Button) findViewById(R.id.add);
sub=(Button) findViewById(R.id.sub);
mul=(Button) findViewById(R.id.mul);
div=(Button) findViewById(R.id.div);
}
public native float add(float one,float two);
public native float sub(float one,float two);
public native float mul(float one,float two);
public native float div(float one,float two);
}
c代码:
#include <jni.h>
//实现java层被native修饰的方法
jfloat addc(JNIEnv* env,jobject obj ,jfloat a,jfloat b){
return a+b;
}
jfloat subc(JNIEnv* env,jobject obj ,jfloat a,jfloat b){
return a-b;
}
jfloat mulc(JNIEnv* env,jobject obj ,jfloat a,jfloat b){
return a*b;
}
jfloat divc(JNIEnv* env,jobject obj ,jfloat a,jfloat b){
return a/b;
}
//绑定java方法和C方法的关系
JNINativeMethod nativeMethon[]={
{"add","(FF)F", (void*)addc},
{"sub","(FF)F", (void*)subc},
{"mul","(FF)F", (void*)mulc},
{"div","(FF)F", (void*)divc}
};
//注册相应的类以及方法;
jint registerNatives(JNIEnv* env){
// jclass (*FindClass)(JNIEnv*, const char*);
jclass _jclass = (*env)->FindClass(env, "com/example/jisuanqi1/MainActivity");
//RegisterNatives,注册方法;
//第一个、第二个:env,clazz;第三个:定义的数组名;第四个:数组元素的个数
// jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);
if((*env)->RegisterNatives(env, _jclass, nativeMethon,sizeof(nativeMethon)/sizeof(nativeMethon[0]))!=JNI_OK)
{
return JNI_ERR;
}
return JNI_OK; //#define JNI_OK (0) /* no error */
}
//动态注册
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
JNIEnv* env;
if( (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4)!=JNI_OK){
return JNI_ERR;
}
if(registerNatives(env)!=JNI_OK){
return JNI_ERR;
}
return JNI_VERSION_1_4; //必须返回这个值
}
4、总结:
4.1 静态注册和动态注册比较
静态注册:
编写不方便,jni方法名必须遵守规则且名字很长;
过程较多,效率低
不安全,
动态注册:
流程清晰可控
效率更高,安全
JIN_onload
4.2 NDK开发总结
关于头文件:
静态注册:
创建了4个文件:.c、.h、.mk、.mk
JNIstudy.c:#inude < .h>
各种代码的实现:实现java层被native修饰的方法
JNIstudy.h:#include <jni.h>、#include <number.h>、#include <String.h>
mk:配置文件;
捋一捋这句话:在.c文件里面导入包含jni.h头文件的 JNIstudy.h头文件动态注册
创建了3个文件:.c、.mk、.mk
.c:#include <jni .h>
各种代码的实现:实现java层被native修饰的方法
mk:配置文件;