freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Android开发笔记 ——(4)NDK开发(eclipse)
2021-12-22 12:43:01
所属地 广东省

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层操作以及整体编译流程:

  1. -定义被native修饰的方法

  2. -根据java代码生成.h头文件(javah -jni 类的包名路径)

  3. -编写C/C++代码,导入.h头文件,实现我们.h头文件中方法

  4. -编写(配置)两个mk文件:application.mk/android.mk

  5. -通过ndk-build生成so文件;

  6. -java代码中加载so文件(system.loadlibrary)

  7. -补充:获取so库的名称

    1. -libs文件下去头去尾(头:lib;尾:.so);

    2. -在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++方法的方式之一
流程:

  1. -java中定义native的方法

  2. -创建C++代码代码,导入头文件;

    1. -编写(配置)两个mk文件:application.mk/android.mk

  3. -JNInativeMethod:绑定java方法和C/C++的方法

  4. -registerNatives(4个参数):注册java层相应的类以及方法

  5. -使用jni.h中JNI_onload进行判断:注册是否成功(JIN_onload:系统调用,相当于java中的psvm:public static void main)

  6. -ndk-build生成so,

  7. -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 静态注册和动态注册比较

静态注册:

  1. 编写不方便,jni方法名必须遵守规则且名字很长;

  2. 过程较多,效率低

  3. 不安全,

动态注册:

  1. 流程清晰可控

  2. 效率更高,安全

  3. JIN_onload

4.2 NDK开发总结

关于头文件:

  1. 静态注册:
    创建了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头文件

  2. 动态注册
    创建了3个文件:.c、.mk、.mk
    .c:#include <jni .h>
    各种代码的实现:实现java层被native修饰的方法
    mk:配置文件;

# Android开发
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录