首先说明下相应的概念:
NDK是一系列工具的集合。它提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。它集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。它可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。
简言之:NDK是一种工具。借助他可以实现一些更适合Android平台版本的C/C++开发
起始:NDK是Google公司推出的帮助Android开发者通过C/C++本地语言编写应用的开发包
JNI是java语言提供的Java和C/C++相互沟通的机制,Java可以通过JNI调用本地的C/C++代码,本地的C/C++的代码也可以调用java代码。JNI 是本地编程接口,Java和C/C++互相通过的接口。Java通过C/C++使用本地的代码的一个关键性原因在于C/C++代码的高效性。
简言之:JNI是一些头文件,里面定义了一些方法以及使用标准
起始:JNI是Java调用Native机制,是Java语言自己的特性全称为Java Native Interface
总之:NDK是一种工具需要借助JNI的标准。所以他们扯在了一起,这里有篇不错的文章:NDK与JNI的关系
JNI的一些数据类型:
在Android最新的NDK与JNI开发中,以前的.mk文件被替换为了CMakeList.txt
创建一个NDK工程并实现属性引用
首先你得去下一堆东西,补充相应的环境支持 也就是Android Studio 中要安装 Android SDK->SDK Tools中安装 CMake, LLDB, NDK. 很大,时间炒鸡久,特别是还被墙(2-3M的速度大概用了30分钟?不太记得清)
另外检查一个插件是否勾上
安装完新建工程时勾选Include C++ support,然后在工程的Project 工具栏选择,Project选项 ,你会发现多了几个文件,在main中和java对齐的cpp目录里包括了一个.cpp的实现,src目录同级多了一个CMakeList.txt
实际上AS已经生成了一些示例,有没有似曾相识?
毋庸置疑,运行后肯定就是显示Hello from C++了,写这里的代码你不喜欢用->的话可以用.(点)然后按Table键IDE很舒服的帮你转换方法调用,仿写一个方法
使用IDE的提示去自动在CPP里创建方法,然后会发现,这个套路原来是一样的
这样我们就知道自己怎么去创建了,那个extern “C”是代表支持混合编程的意思,下面我们改下
extern "C" JNIEXPORT jstring JNICALL Java_com_lckiss_ndkgradle_MainActivity_updateNameFromC(JNIEnv *env, jobject instance) { // Jobject代表的是谁调用的这个方法,比如这里就是MainActivity的类对象 首先混去jclass //C++的命名习惯性加_ jclass _jclass=env->GetObjectClass(instance); /** * 获取属性ID jfieldID GetFieldID(jclass clazz, const char* name, con st char* sig) * 第二个参数表示我们想要获取的属性名 比如我们新加的name, * 第三个参数看下面的图,其代表的是JNI想要访问Java中的属性,变量等需要的一个签名 * String不属于基本数据类型 所以需要用简写加上类的完整路径就是L java/lang/String ; */ jfieldID _jfieldID = env->GetFieldID(_jclass,"name","Ljava/lang/String;"); /** * jobject GetObjectField(jobject obj, jfieldID fieldID) * object向下强转为jstring 和java一样 */ jstring res= (jstring) env->GetObjectField(instance, _jfieldID); //输出下 printf("%#x\n",res); //转换成Java的string去调用 char * str= (char *) env->GetStringUTFChars(res, NULL); //新生成一个char数组去拼接,方便演示 char text[20]="success"; char* finalRes= strcat(str,text); //最后将结果返回 return env->NewStringUTF(finalRes); }
然后MainActivity中:
public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } //新定义一个属性 public String name="Test "; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(updateNameFromC()); } /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public native String stringFromJNI(); public native String updateNameFromC(); }
运行下就会显示test success。
test success
其实简单的用法就是这样,并没什么难度,NDK已经简化了很多很多东西,附上签名描述图
JNI常用方法函数
JIN方法数组的引用(静态方法等等诸如此类)
继续在Activity中添加方法
public String getName(){ return "this is name form act"; } //我们来通过这个方法来返回上面的 "this is name form act" public native String getMethod();
然后按老套路继续写Cpp,顺便改Activity中的那个textview.setText的来源
extern "C" JNIEXPORT jstring JNICALL Java_com_lckiss_ndkgradle_MainActivity_getMethod(JNIEnv *env, jobject instance) { //先拿到class jclass _jclass = env->GetObjectClass(instance); //拿方法 jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) jmethodID _jmethodID = env->GetMethodID(_jclass, "getName", "()Ljava/lang/String;"); //jni调用java的方法 静态方法等诸如此类 env->CallStaticObjectMethod() jstring _jstring = (jstring) env->CallObjectMethod(instance, _jmethodID); //转换成Java的string去调用 和上面一样 char *str = (char *) env->GetStringUTFChars(_jstring, NULL); //新生成一个char数组去拼接,方便演示 char text[20] = "success"; char *finalRes = strcat(str, text); //最后将结果返回 return env->NewStringUTF(finalRes); }
最后肯定是返回:
this is name form act success
附上方法的签名图
小彩蛋:
so库文件哪里找?
还有SDK目录下NDK的一些东西
接下来是数组的引用
Activity中加入以下内容
private int[] source={1,4,0,7,33,11}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(getMethod()); //利用JNI来进行数组排序 getArray(source); for (int i = 0; i <source.length ; i++) { Log.d("source", "onCreate: "+source[i]); } } public native void getArray(int[] arrays);
Cpp中追加头文件以及内容
#include <stdlib.h>
/** * 首先将__lhs强制声明为指向整数的指针(int*),再读取指针对应的整数(最外面的*) * @param __lhs * @param __rhs * @return */int compare(const void* __lhs, const void* __rhs){ return (*(int*)__lhs-*(int*)__rhs); } extern "C" JNIEXPORT void JNICALL Java_com_lckiss_ndkgradle_MainActivity_getArray(JNIEnv *env, jobject instance, jintArray arrays_) { //已经自动拿到了数组 jint *arrays = env->GetIntArrayElements(arrays_, NULL); //获取数组长度 int _len= env->GetArrayLength(arrays_); //void qsort(void* __base, size_t __nmemb, size_t __size, int (*__comparator)(const void* __lhs, const void* __rhs)); //第三个参数是需要实现的一个方法 qsort(arrays,_len, sizeof(int),compare); env->ReleaseIntArrayElements(arrays_, arrays, 0); }
然后运行,你会发现排序成功,
基本上就是这样子玩了,套路都是一样一样的。秒秒钟就成了老司机
第一部分完结,请看第二部分:NDK与JNI基础-JIN的引用、缓存策略、异常
本站由以下主机服务商提供服务支持:
0条评论