Android-NDK-之Hello-World

今天来聊聊 Android Studio 之创建 NDK 之Hello World


以下是ndk相关的简单介绍

什么是ndk

ndk全称 Native Development Kit 原生开发套件
众所周知,Android程序运行在Dalvik虚拟机中,NDK允许用户使用类似C / C++之类的原生代码语言执行部分程序。
NDK包括了:
    1.从C / C++生成原生代码库所需要的工具和build files。
    2.将一致的原生库嵌入可以在Android设备上部署的应用程序包文件(application packages files ,即.apk文件中)。
    3.支持所有未来Android平台的一系列原生系统头文件和库。

使用ndk开发有什么好处(以下是我自己总结的)

1.在某些算法比较复杂耗时的情况下,使用c c++可以有效提高性能。如fresco
2.可以加密,相对于apk反编译来说so文件一定程度上可以加大被破解的难度。如 BAT等公司就使用了非常多的so文件混合开发

ndk的开发方式

1.使用插件 experimental

优缺点:可以一键快捷生成cpp等文件,缺点需要使用experimental插件的各种配置。

2.就是本文所要讲的使用Android.mk Application.mk 来生成so文件库

优缺点:不需要experimental插件的各种配置,缺点可能是编译调试有点麻烦。

这是华丽的分割线,前面口水话了说這么多,现在我们正式接入正题


使用方式2创建Hello World

第一步配置开发环境

ndk下载配置路径
1)基本的开发环境:Android Studio、SDK、JDK

2)ndk下载配置如下图


配置ndk快捷命令
需要配置如下3个命令方便开发中使用

    1.javah                 用于生成头文件
    2.ndk-build             用于生成so
    3.ndk-build clean       用于清空生成的so

配置成功如下图

其中javah 配置如下图所示

其他配置就不贴图了,参考以下自行配置

javah 用于生成头文件
Program:$JDKPath$/bin/javah
Parameters: -encoding UTF-8 -d ../jni -jni $FileClass$       注:-encoding UTF-8指定编码,你需要根据你工程的编码自行修改。
Working directory: $SourcepathEntry$\..\java

ndk-build 用于构建so包
Program: 你配置的NDK目录\build\ndk-build.cmd                 注意:windows用ndk-build.cmd,MAC/Linux用ndk-build
Parameters:                                                  什么都不用填
Working directory:$ModuleFileDir$\src\main

ndk-build clean清除so包
Program: 你配置的NDK目录\build\ndk-build.cmd                 注意:windows用ndk-build.cmd,MAC/Linux用ndk-build
Parameters: clean
Working directory:$ModuleFileDir$\src\main

第二步创建项目

创建一个eandroid项目完成之后进行如下操作:

1.创建一个native方法 可如下创建:

1
2
3
4
5
6
7
public class HelloNDKUtil {
static {
System.loadLibrary("NdkTest");
}
// 这里java传递一个String参数到native层然后再返回一个String
public native String getHelloNDK(String str);
}

(注:NdkTest是定义的NDKlibrary名称,后打包操作mk文件配置需要保持一致)

2.配置build文件

1
2
3
4
5
6
7
8
9
10
11
android{
defaultConfig {
ndk {
moduleName "NdkTest"//定义NDKlibrary的名称
//abiFilters("armeabi", "armeabi-v7a"..)
//ldLibs = ["log"] 添加log库,看自己需求
//cFlags
//stl(ie:gnustl_shared,stlport_static..)
}
}
}

3.在gradle.properties文件中添加如下代码

1
android.useDeprecatedNdk = true

4.通过上面配置的javah命令生成头文件 ,如下图操作(注:图片如果看不全可选择在新标签页打开查看)

5.通过c/c++实现native方法 例如:

1
2
3
4
JNIEXPORT jstring JNICALL Java_top_goluck_ndk_HelloNDKUtil_getHelloNDK
(JNIEnv *env, jobject obj,jstring str) {
return str;//这里是将java层的参数直接返回到java层
}

(注意:Java_top_goluck_ndk_HelloNDKUtil_getHelloNDK 方法名参数,一定要和生成的头文件里面的方法保持一致)

6.java调用native方法

1
2
HelloNDKUtil helloNDKUtil=new HelloNDKUtil();
String result = helloNDKUtil.getHelloNDK("这是java传递的参数到native方法然后再返回到java的文本");

到这里就可以编译运行了,把 result 可以显示到 TextView 看到的效果就是这样

第三步打包生成so文件及在正式项目中使用该so库

1.创建Android.mk文件 代码如下

1
2
3
4
5
6
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := NdkTest
LOCAL_SRC_FILES := NdkTest.cpp
include $(BUILD_SHARED_LIBRARY)}

(注: 以上[NdkTest]名称需同NDKlibrary的名称一致
cpp文件全部位于android项目下的jni文件夹下,结构如下时

jni
 |---1.cpp
 |---2.cpp
 |---Android.mk
 |---Application.mk
 |---ndk_test.cpp
 |---src
 |    |---core
 |    |    |---core1.cpp
 |    |    |---core2.cpp
 |    |---src1.cpp
 |    |---src2.cpp

按照通常的写法,需在android.mk中,应该写入

LOCAL_SRC_FILES := ndk_test.cpp \
            1.cpp \
            2.cpp \
            src/src1.cpp \
            src/src2.cpp \
            src/core/core1.cpp \
            src/core/core2.cpp)

2.创建Application.mk文件 代码如下

1
2
3
APP_MODULES := NdkTest

APP_ABI := all

(注: 以上[NdkTest]名称需同NDKlibrary的名称一致)

3.通过上面配置的ndk build命令生成so库

(注意:会生成libs 和 obj两个目录,前一个没有日志调试信息,后者有)

4.在正式项目中使用该so库

方式1:

(1).复制需要的so文件到main下的libs

(2).build.gradle加入如下代码

1
2
3
4
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] //disable automatic ndk-build call
}

方式2:

直接复制需要的so文件到main下的jniLibs文件下