您的位置:首页 > 其它

跟我详读ndk(第一篇)

2016-05-14 21:46 351 查看

     要聊这个ndk呢?我先说一下为啥我要学ndk的历史。各位童鞋们也明白现在面试的时候面试官除了应用层会问,有时候也会问问你们对底层开发的了解程度,实际上所有面试官就是在装逼

,不过当然也有他们的道理

。ndk涉及的东西比较底层,c/c++,由于一切语言底层都是c/c++,如果你对c语言熟悉程度能自称精通,那么你肯定就很刁了,就像练武一样,如果你马步扎实了,练咏春,太极,功夫那就易如反掌了,对吧!

       为何我要讲底层c/c++呢?因为我们接下来讲的就是要靠c/c++来一步步往上学通的,假如你们对c/c++不熟悉那么可以先买一本书琢磨个几个星期学好它再来看看jni.好吧,废话少说,我们来看看如何学好jni了。

       第一步,我们先要配置好环境。到官网下载ndk,如果你用的是eclipse(当然要学ndk我也建议大家先使用eclipse来学比较快,因为android studio有很多坑,建议使用eclipse),菜单Window->Preferences->Android->NDK->配置你的ndk路径(F:\android\android-ndk-r10是我的路径)

       第二步,我们导入ndk下面的第一个项目(我的路径是F:\android\android-ndk-r10\samples\hello-jni),导入项目后我来帮你们分析使用jni的重要目录

jni:存放c/c++和Android.mk和Application.mk的目录

libs:存放第三方jar包,当然存放的也有我们jni生成的so文件,表示cpu的类型

obj:obj这个目录我们不要动,这个是系统自动生成的

我们今天先学习第一个小例子,原生代码和java交互。我们使用刚才导入的hello-jni来讲。首先打开Activity

1.调用so文件,System.loadLibrary(name),调用过程,实际上name后面会加libname.so

static {
System.loadLibrary("hello-jni");
}

2.创建native函数,没有实现的函数

public native String stringFromJNI();
3.调用

TextView tv = new TextView(this);
tv.setText(stringFromJNI());
setContentView(tv);
接下来在jni目录下创建Android.mk和Application.mk文件,先讲讲Android.mk文件
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello/hello-jni.c

include $(BUILD_SHARED_LIBRARY)

Android.mk文件的变量名需要大写,如LOCAL_PATH、LOCAL_MODULE、LOCAL_SRC_FILES等

LOCAL_PATH变量定位源文件,Android构建系统提供了一个名为my-dir的宏功能。通过将该变量设置为my-dir宏功能的返回值,可以将其放在当前目录下。

LOCAL_MODULE用于给构建过程中生成的文件命名,所以构建系统给该文件添加了适当的前缀和后缀。例如hello-jni生成的so文件名叫libhello-jni.so。

LOCAL_SRC_FILES表示文件的路径,如果hello-jni.c放在hello文件下面那么就是

LOCAL_SRC_FILES := hello/hello-jni.c


然后我们来讲讲Application.mk文件,文件就这么一行APP_ABI := all,也可以是APP_ABI := 指定cpu目录(如APP_ABI := armeabi)

最后在jni目录下打开hello-jni.c文件

#include <jni.h>
jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a/NEON (hard-float)"
#else
#define ABI "armeabi-v7a/NEON"
#endif
#else
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a (hard-float)"
#else
#define ABI "armeabi-v7a"
#endif
#endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__x86_64__)
#define ABI "x86_64"
#elif defined(__mips64)  /* mips64el-* toolchain defines __mips__ too */
#define ABI "mips64"
#elif defined(__mips__)
#define ABI "mips"
#elif defined(__aarch64__)
#define ABI "arm64-v8a"
#else
#define ABI "unknown"
#endif
return (*env)->NewStringUTF(env, "Hello from JNI !  Compiled with ABI " ABI ".");
}
这个文件大多都是使用ndk-build.cmd来生成的,稍等我讲讲生成so文件

对于c文件我就不多说了,看起来像不像你们学习的c语言,首先使用#include导入h文件,就像我们java的import导包一样,导入之后我们可以使用h文件下的属性,函数。
Java_com_example_hellojni_HelloJni_stringFromJNI

这个是表示着函数的所在位置由Java开头,当然Java首字母要大写,然后包名+类名+函数名,有2个参数是很重要的,说到这个我们先从7步创建虚拟机开始

1.生成java虚拟机选项

2.生成java虚拟机

res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);

3.查找并加载类

cls = (*env)->FindClass(env, "InvocationApiTest");

4.获取main()方法的ID

mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");

5.生成字符串对象,用作main()方法的参数

6.调用main()方法

(*env)->CallStaticVoidMethod(env, cls, mid, args);

7.销毁Java虚拟机

(*vm)->DestroyJavaVM(vm);

      从创建虚拟机开始我们可以看到将生成JNIEnv*,如果要与java虚拟机对接,那么我们需要这个JNIEnv*来指向一个函数表格,这个函数表格有很多函数,我们可以打开jni.h文件然后搜索JNIEnv这个对象看看它究竟是何方神圣,它包含了很多函数表,GetMethodID获取函数的ID,GetFieldID获取属性字段的ID等等,第二个参数是jobject,它代表了从java虚拟机带入的对象,如HelloJniActivity,如果调用的函数是静态的,那么第二个参数则是jclass。还有一个返回值,返回值我们要看java和jni的数据类型表了

Java 类型本地类型描述
booleanjbooleanC/C++8位整型
bytejbyteC/C++带符号的8位整型
charjcharC/C++无符号的16位整型
shortjshortC/C++带符号的16位整型
intjintC/C++带符号的32位整型
longjlongC/C++带符号的64位整型e
floatjfloatC/C++32位浮点型
doublejdoubleC/C++64位浮点型
Objectjobject任何Java对象,或者没有对应java类型的对象
ClassjclassClass对象
Stringjstring字符串对象
Object[]jobjectArray任何对象的数组
boolean[]jbooleanArray布尔型数组
byte[]jbyteArray比特型数组
char[]jcharArray字符型数组
short[]jshortArray短整型数组
int[]jintArray整型数组
long[]jlongArray长整型数组
float[]jfloatArray浮点型数组
double[]jdoubleArray双浮点型数组
※     JNI类型映射
java文件和c文件的对比:
public native String stringFromJNI();

jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz )

好了,分析jni我们到此结束。我们现在来看看生成so文件的工具怎么配置环境
1)点击Project->Properties->Builders->New,新建立一个Builder。在弹出的对话框上面点击Program,点击OK;

2) 在弹出的对话框【Edit Configuration】中,配置选项卡【Main】:

               Location中需要填入nkd-build.cmd的路径(NDK安装目录下)。

               WorkingDiretcoty中需要填入HelloJni的工程根目录。

3)选中配置选项卡【Refresh】:

              勾选“Refresh resources upon completion”,

              选中“The entire workspace”,

              勾选“Recuresively include sub-folders”。
4)选中配置选项卡【Build Options】:

             勾选“After a “Clean””,

             勾选“During manual builds”,

             勾选“During auto builds”,

             勾选“Specify working set of relevant resources”。

             点击“Specify Resources…”勾选TestNDK工程的“jni“目录,Finish!保存设置,点击OK。

生成so文件

     由于我们勾选了“During auto builds”,所以在工程有所改变的时候,so文件便会自动编译,正确生成以后就能在工程目录下发现多了两个文件夹,文件夹libs\armeabi目录下生成了一个叫libhello-jni.so的文件。至此,使用NDK生成so文件的工作就完成了。

好了,so生成装逼结束,我们接下来配置一个h文件。
1)点击Project->Properties->C/C++ Build->Tool Chain Editor在Used tools右边点击Select Tools按钮将Android GCC Compiler替换成GCC C Compiler

2)找到C/C++ General选项卡找到Paths and Symbols导入
1.F:\android\android-ndk-r10\platforms\android-L\arch-arm\usr\include(含c语言的h文件,如stdio.h,stdlib.h,string.h)
2.F:\android\android-ndk-r10\sources\cxx-stl\stlport\stlport
3.F:\android\android-ndk-r10\platforms\android-L\arch-arm\usr\include\linux
4.F:\android\android-ndk-r10\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\lib\gcc\arm-linux-androideabi\4.9\include
5.F:\android\android-ndk-r10\sources\cxx-stl\stlport\stlport\stl\config
自己可以去看看文件夹下面有什么文件可以猜一猜,这里我就不多说了。

好了,第一个例子和环境已配置完毕。

如果有什么问题可以加群android搜一下①群 424568648

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: