[安卓开发基础]NDK学习笔记 初识jni调用流程
- 1. 初识NDK
NDK(Native Develop Kits ),NDK 可以将C/C++ 编译成可执行的文件.so, JNI (java native interface) Java 本地开发接口,c/c++与Java通信的协议。,C/C++ 运行效率高一点(C/C++ -----(.exe)),Java运行效率相对低一点(Java---(*.class)---JVM---机器)。
Java(跨平台)-------jni---------C/C++(硬件操作)
arm 架构---x86/armeabi
x86架构----libs/x86
2.ndk 环境配置:
- Ndk 自带例子:
- 1.创建一个工程JNIDDemo1
声明native 方法,
static 静态块调用so库。
static {
System.loadLibrary("javaCallC");//javaCallC 的名称
}
package com.example.JNIDDemo1;
/**
* Created by zengjx on 2018/4/11.
*/
public class JNI {
static {
System.loadLibrary("javaCallC");
}
public void helloFromJava(){
System.out.println("helloFromJava");
}
public int javaadd(int x,int y){
System.out.println("javaadd");
return x+y;
}
public void print(String s){
System.out.println(s);
}
//写 native 方法
public native int add(int x,int y);
public native int del(int a ,int b);
public native String getString(String str);
public native int[] getIntArray(int[] array);
public native int getStringLen(String str);
}
-
2.在src目录下 用javah 生成头文件;
- 3.创建 jni目录,将头文件剪切放到 jni目录下;
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_JNIDDemo1_JNI */
#ifndef _Included_com_example_JNIDDemo1_JNI
#define _Included_com_example_JNIDDemo1_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_JNIDDemo1_JNI
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_JNIDDemo1_JNI_add
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_example_JNIDDemo1_JNI
* Method: del
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_JNIDDemo1_JNI_del
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_example_JNIDDemo1_JNI
* Method: getString
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_JNIDDemo1_JNI_getString
(JNIEnv *, jobject, jstring);
/*
* Class: com_example_JNIDDemo1_JNI
* Method: getIntArray
* Signature: ([I)[I
*/
JNIEXPORT jintArray JNICALL Java_com_example_JNIDDemo1_JNI_getIntArray
(JNIEnv *, jobject, jintArray);
/*
* Class: com_example_JNIDDemo1_JNI
* Method: getStringLen
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_example_JNIDDemo1_JNI_getStringLen
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
- 4.根据头文件编写C文件文件名称与JNI 的javaCallC 相同。
static { System.loadLibrary("javaCallC"); }
本地函数命名规则 Java_包名_native所在的类名_native函数名
第二个参数:jobject调用 当前函数的对象
第一个参数 JNIEnv JNINativeInterface (**env)(*env)->
JNINativeInterface : 接口函数指针表
基本数据类型:
引用类型:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "com_example_JNIDDemo1_JNI.h"
#include <android/log.h>
#define TAG "JNI_TAG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
char* _JString2Cstr(JNIEnv* env,jstring jstr);
/*
* Class: com_example_JNITest2_JNI
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL JNICALL Java_com_example_JNIDDemo1_JNI_add
(JNIEnv * env, jobject jobject, jint x, jint y){
int result =0;
result=x+y;
LOGI("LOGI----result%d,%d,%d",x,y,result);
/**************c调用java*****************/
jclass cls;
jmethodID mid;
jmethodID mid_add;
jmethodID mid_string;
cls=(*env)->FindClass(env,"com/example/JNIDDemo1/JNI");
//找到字节码
mid= (*env)->GetMethodID(env,cls,"helloFromJava","()V");
//找到方法
//创建对象(可选 同一个java类中 不要创建对象jobject )
//通过对象调用方法
(*env)->CallVoidMethod(env,jobject,mid);
//第二个 函数
//public int javaadd(int, int);
// descriptor: (II)I
mid_add= (*env)->GetMethodID(env,cls,"javaadd","(II)I");
//通过对象调用方法
int res =(*env)->CallIntMethod(env,jobject,mid_add,123,456);
LOGI("LOGI--c调用 java add--%d",res);
//第3个
// public void print(java.lang.String);
// descriptor: (Ljava/lang/String;)V
mid_string= (*env)->GetMethodID(env,cls,"print","(Ljava/lang/String;)V");
//通过对象调用方法
jstring jstr =(*env)->NewStringUTF(env,"print from c");
(*env)->CallVoidMethod(env,jobject,mid_string,jstr);
return result;
}
/*
* Class: com_example_JNITest2_JNI
* Method: del
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_JNIDDemo1_JNI_del
(JNIEnv *env, jobject jobject, jint a, jint b){
int result =0;
result=a-b;
LOGI("LOGI----result%d",result);
return result;
}
char* _JString2Cstr(JNIEnv* env,jstring jstr){
char* cstr=NULL;
//获取String.class 字节码
LOGI("LOGI----resultcstr%s","_JString2Cstr");
jclass classstring=(*env)->FindClass(env,"java/lang/String");//找到String 类型的字节码
LOGI("LOGI----resultcstr%s","_JString2Cstr 2");
jstring strencode=(*env)->NewStringUTF(env,"GB2312");
LOGI("LOGI----resultcstr%s","_JString2Cstr 3");
//获取 方法 ID 号
//找到 classstring 的 getByte 方法
jmethodID mid =(*env)->GetMethodID(env,classstring,"getBytes","Ljava/lang/String;)[B");
LOGI("LOGI----resultcstr%s","_JString2Cstr 4");
jbyteArray barr=(jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode);
LOGI("LOGI----resultcstr%s","_JString2Cstr 5");
//获取数组的长度
jsize alen =(*env)->GetArrayLength(env,barr);
LOGI("LOGI----resultcstr_JString2Cstr 6 %d",alen);
//获取数组首地址
jbyte* ba=(*env)->GetByteArrayElements(env,barr,JNI_FALSE);
if(alen>0){//是否有内容
cstr=(char*)malloc(alen+1);
memcpy(cstr,ba,alen);
cstr[alen]=0;//"结束符"
}
(*env)->ReleaseByteArrayElements(env,barr,ba,0);
LOGI("LOGI---_JString2Cstr end :%s",cstr);
return cstr;
}
JNIEXPORT jstring JNICALL Java_com_example_JNIDDemo1_JNI_getString
(JNIEnv *env, jobject jobject, jstring jstr){
jsize size;
char acbuf[]="wwwwwwwwwwwwwww";
int strlen=0;
char *cstr;
int i=0;
cstr = (*env)->GetStringUTFChars(env, jstr, NULL);//获得字符串转为 字符串指针
if (cstr == NULL) {//用到分配内存可能会失败
return NULL; /* OutOfMemoryError already thrown */
}
size= (*env)->GetStringUTFLength(env, jstr);
LOGI("Get string from java size:%d\n",size);
// strlen=strlen(cstr);
// LOGI("Get string from java size:%d\n",strlen);
LOGI("Get string from java :%s\n", cstr);
for(i=0;i<size;i++){
LOGI("Get string from java :%c\n", cstr[i]);
*(cstr+i)+=1;
LOGI("Get string from java :%c\n", cstr[i]);
}
// (*env)->ReleaseStringUTFChars(env, jstr, cstr);//释放空间
// return (*env)->NewStringUTF(env,acbuf);
return (*env)->NewStringUTF(env,cstr);
}
- 5.编写Android.mk 文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=javaCallC
LOCAL_SRC_FILES := javaCallC.c
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
#include $(BUILD_STATIC_LIBRARY)
Application.mk 文件代码。(看文件中注释)
APP_ABI := all
#APP_ABI:= armeabi armeabi-v7a x86 mips mips-r2 mips-r2-sf
#如果是普通arm处理器的Android手机,使用APP_ABI := armeabi;
#如果是x86处理器的,使用APP_ABI := x86,等等。
#如果APP_ABI := all,会编译所有指令的so
APP_STL := stlport_static
#NDK中C++标准库、STL的配置
#如未配置可能出现错误: fatal error: iostream: No such file or directory
---------------------
作者:Ruffian-痞子
来源:****
原文:https://blog.****.net/u014702653/article/details/51861013
版权声明:本文为博主原创文章,转载请附上博文链接!
APP_MODULES := javaCallC
APP_ABI := armeabi
---------- cut here ------------------
#当前目录
LOCAL_PATH := $(call my-dir)
#清除上次编译的信息
include $(CLEAR_VARS)
#在这里指定最终生成的文件名字
LOCAL_MODULE := hello-jni
# 要编译的c代码文件名
LOCAL_SRC_FILES := hello-jni.c
#要生成的是一个动态链接库
include $(BUILD_SHARED_LIBRARY)
---------- cut here ------------------
- C代码添加log:
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=Hello
LOCAL_SRC_FILES := Hello.c Hello2.c
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
#include $(BUILD_STATIC_LIBRARY)
加载 liblog.so
liblog.so 在NDK 的路径:
C代码添加:
#include <android/log.h>
#define TAG "JNI_TAG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
Log.h :
Log
优先级
Prio 优先级 tag标记 可以过滤
5.在jni 目录下 执行 ndk-build: 编译生成.so文件。
-
6.调用:
package com.example.JNIDDemo1;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MyActivity extends Activity {
private TextView textView;
Button btn;
/**
* Called when the activity is first created.
*/
JNI jni =new JNI();//创建类
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
inittextView();
// add();
}
public void inittextView(){
textView=(TextView)findViewById(R.id.tv_log);
btn=(Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
add();
}
});
}
public void add(){
int ret=0;
ret= jni.add(12,23);//调用native方法。
Log.i("MyActivity","计算结果:"+jni.add(12,23));
// textView.setText("计算结果:"+jni.add(12,23));
textView.setText("计算结果:"+jni.add(12,23)+"字符串:"+jni.getString("abc"));
}
}
C 调用 java:
-
- Javap 命令 生成 sigenature 签名
Eclipse:在bin/classes 目录下 :
Class 路径:
F:\ideaCode\JNIDDemo1\out\production\JNIDDemo1\com\example\JNIDDemo1
在JNI.class 路径下输入 javap -p -s JNI 输出如下信息:
Android studio :class 文件位置:
F:\AS\AndNDKDemo\app\build\intermediates\classes\debug\com\foundation\zengjx\andndkdemo