您的位置:首页 > 其它

NDK开发基本入门了解

2017-12-14 14:49 369 查看


一、前言


● NDK


Native Development Kit(NDK)是一系列工具的集合。它提供了一系列的工具,帮助开发者快速开发C/C++的动态库,并能自动将so和java一起打包成apk。

● JNI


Java Native Interface(JNI)标准是java平台的一部分,JNI是Java语言提供的Java和C/C++相互沟通的机制,Java可以通过JNI调用C/C++代码,C/C++的代码也可以调用java代码。

● JNI与NDK的关系


NDK可以为我们生成了C/C++的动态链接库,JNI是java和C/C++沟通的接口,两者与android没有半毛钱关系,只因为安卓是java程序语言开发,然后通过JNI又能与C/C++沟通,所以我们可以使用NDK+JNI来实现“Java+C”的开发方式。

● 为什么要NDK开发


NDK开发具有以下优点: 

1. 项目需要调用底层的一些C/C++的一些东西(java无法直接访问到操作系统底层(如系统硬件等)),或者已经在C/C++环境下实现了功能代码(大部分现存的开源库都是用C/C++代码编写的。),直接使用即可。NDK开发常用于驱动开发、无线热点共享、数学运算、实时渲染的游戏、音视频处理、文件压缩、人脸识别、图片处理等。 

2. 为了效率更加高效些。将要求高性能的应用逻辑使用C/C++开发,从而提高应用程序的执行效率。但是C/C++代码虽然是高效的,在java与C/C++相互调用时却增大了开销; 

3. 基于安全性的考虑。防止代码被反编译,为了安全起见,使用C/C++语言来编写重要的部分以增大系统的安全性,最后生成so库(用过第三方库的应该都不陌生)便于给人提供方便。(任何有效的代码混淆对于会smail语法反编译你apk是分分钟的事,即使你加壳也不能幸免高手的攻击) 

4. 便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。


二、安装与配置

配置ndk系统环境变量,并检测 




将NDK_ROOT追加到Path环境变量下-->;%NDK_ROOT%

dos下命令:ndk-build;出现以下信息说明配置成功 



首先我们在Android Studio下新建一个安卓项目。然后打开Project Structure界面,如下: 



在SDK Location目录下,有SDK和NDK的路径,而这里我们暂时还未下载配置过NDK,故我们需要点击Download Android NDK来进行下载(Android Studio还是很强大的,相比Eclipse能省不少事)。这里Android Studio会下载最新版本的NDK进行安装,默认会下载保存在SDK的路径下。我们在上图中还能看到有一段介绍文字,说SDK以及NDK的路径配置会保存在local.properties文件内,安装完成后我们刷新Project,进local.properties文件查看也能看到SDK与NDK的路径。 




 

NDK下载配置完成之后,需要在gradle.properties文件中加上一行:
android.useDeprecatedNdk=true
1

接下来,我们借助强大的Android Studio的插件功能,在External Tools下配置两个非常有用的插件。进入Settings–>Tools–>ExternalTools,点击+号增加。 





javah -jni命令,是根据java文件生成.h头文件的,会自动根据java文件中的类名(包含包名)与方法名生成对应的C/C++里面的方法名。下面是参数配置及其含义: 

1. Program: $JDKPath$\bin\javah.exe 这里配置的是JDK目录下的javah.exe的路径。 

2. Parametes: -classpath . -jni -d $ModuleFileDir$/src/main/jni $FileClass$ 这里$FileClass$指的是要执行操作的类名(即我们操作的文件),$ModuleFileDir$/src/main/jni表示生成的文件保存在这个module目录的src/main/jni目录下。 

3. Working: $ModuleFileDir$\src\main\java module目录下的src\main\java目录(不是很理解)。 

使用方式:选中java文件—>右键—>External Tools—>javah-jni,将生成jni文件夹以及文件夹下的 包名.类名的.h头文件 (名字过长,我们可以自己重命名)。 



ce65
ndk -build命令,是根据C/C++文件生成so文件的。下面是参数配置及其含义: 

1. Program: F:\apk\sdk\ndk-bundle\ndk-build.cmd 这里配置的是ndk下的ndk-build.cmd的路径(根据实际情况填写)。 

2. Working: $ModuleFileDir$\src\main\ 

使用方式:选中C/C++文件—>右键—>ExternalTools—>ndk-build,将在main文件夹下生成libs文件夹以及多个so文件,我们可以移动至jniLibs目录下去。


三、简单实例

接下来我们创建一个访问本地C/C++方法的java类。

public class JniKit {
/**
* 如果你的native方法报错,没关系,配置完成自然会编译通过
*
* @param num
* @return
*/
public static native int calculate(int num);

//System.loadLibrary("库文件名称不带前缀,后缀名");
static {
//Android.mk中LOCAL_MODULE的名字
System.loadLibrary("demo");
}
}


使用生成的模板会自动生成一个.h文件和一个jni的包

然后在你的jni包里面建立一个.c文件





写c的测试代码。 

include中指向你创建的.h头文件 

Java_com_lh_jni_JniKit_calculate;Java_包名类名方法名(参数)
#include <com_lh_jni_JniKit.h>

JNIEXPORT jint JNICALL Java_com_lh_jni_JniKit_calculate(JNIEnv *env, jclass cls, jint num) {
return num * num;
}


至此,.h文件和c文件均已完成,接下来还需要在这个jni目录下增加两个文件,Android.mk和Application.mk。 

Android.mk,注意LOCAL_MODULE的值与之前的名字相对应,LOCAL_SRC_FILES的值写c文件的名字,这两个值成对设置,可设置多组。(:=是赋值的意思,$是引用某变量的值。)

里面的符号正确的应该是:=,代码中已更正,图片里面的更换麻烦就没改了。很奇怪,我当初写的时候编译运行好像是没出错是正常的…(Tips.20170519)

LOCAL_PATH := $(call my-dir)     // 设置当前的编译目录(Android.mk所在的目录)

include $(CLEAR_VARS)            // 清除LOCAL_XX变量(LOCAL_PATH除外)
LOCAL_MODULE := JNI_ANDROID_TEST  // 指定当前编译模块的名称
LOCAL_SRC_FILES := jnitest.c    // 编译模块需要的源文件
include $(BUILD_SHARED_LIBRARY) // 指定编译出的库类型,BUILD_SHARED_LIBRARY:动态库;BUILD_STATIC_LIBRARY:静态库, BUIL


Application.mk,APP_ABI有四种类型(默认armeabi),armeabi、armeabi-v7a、x86、mips,设置时以空格隔开,all表示所有。该文件中有个可选配置的APP_MODULES,类似于上面Android.mk文件中的LOCAL_MODULE,以空格隔开,且会覆盖掉Android.mk文件中的LOCAL_MODULE设置(比如Android.mk文件中的写了两个jni库的配置,LOCAL_MODULE := JNI1、LOCAL_MODULE := JNI2,而Application.mk中设置的APP_MODULES
:= JNI1,则只能生成JNI1的so文件,要生成JNI2的so文件的时候会报错,除非写成APP_MODULES := JNI1 JNI2,这里我们直接省略默认使用Android.mk中的)。
APP_ABI := all

接下来我们需要对C++文件执行ndk-build操作,生成相应的so文件。 

如图,在main/libs目录下生成了多个so文件,名字为lib+我们指定的库名

activity,测试本地c代码的调用(这里要注意的是,移到其他项目使用,必须把含有native方法的java文件放在与生成so文件的同包名下)

public class MainActivity extends AppCompatActivity {

@InjectView(R.id.test_jni_text)
TextView testJniText;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
testJniText.setText("4*4=" + JniKit.calculate(4));
}
}

到这里,整个ndk环境的搭建、测试已经全部完成了。如果你的步骤代码都没毛病,依然告诉你so文件找不到别着急


在build.gradle中配置

externalNativeBuild {
ndkBuild {
path file("src\\main\\jni\\Android.mk")
}
}

这个是放在build.gradle中的Android下面的,希望可以帮到你。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: