Android NDK 初步
本文最后更新于:2023年4月15日 下午
- 开发环境: win7 64,Android Studio 2.1
需要工具:NDK,Cygwin
使用adb查看手机CPU架构信息
将手机通过USB连接到电脑,adb shell进入手机根目录,执行cat /proc/cpuinfo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19shell@hnCHE-H:/ $ cat /proc/cpuinfo
cat /proc/cpuinfo
Processor : AArch64 Processor rev 3 (aarch64)
processor : 0
processor : 1
processor : 2
processor : 3
processor : 4
processor : 5
processor : 6
processor : 7
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer : 0x41
CPU architecture: AArch64
CPU variant : 0x0
CPU part : 0xd03
CPU revision : 3
Hardware : hi6210sft
可以看到手机处理器的信息
使用 SDK Manager 配置安装 NDK
添加系统环境变量 G:\SDK\ndk-bundle;G:\SDK\platform-tools
下载并安装Cygwin:https://cygwin.com/install.html
Cygwin 安装NDK需要的工具包(如果第一次安装时没有选择工具包,可以再次启动安装):
make, gcc, gdb, mingw64-x86_64-gcc, binutils
配置G:\soft\Cygwin\home\Administrator\.bashrc
,添加下面的指令,使用英文界面。1
2export LANG='en_US'
export LC_ALL='en_US.GBK'
配置text选项,在option里的text可设置。
可以在G:\soft\Cygwin\home\Administrator\.minttyrc
中看到。
1 |
|
设置完字体后可以避免中文乱码。
配置 G:\soft\Cygwin\home\Administrator\.bash_profile
1 |
|
在Cygwin中查找NDK位置,可以看到在SDK目录里面
1 |
|
操作示例NDK工程
JDK10已经不提供javah
这个工具了,我们可以使用as支持c++的功能;详情见下文
生成一次试试。从github上获取android-ndk-android-mk,进入hello-jni工程。
1 |
|
编译成功后,自动生成一个libs目录,编译生成的.so文件放在里面。
1 |
|
将它复制到jni文件夹下;这个就是JNI层的代码。
Ubuntu下javah报错。需要添加参数
1 |
|
使用C/C++实现JNI
遇到错误: Error:Execution failed for task ‘:app:compileDebugNdk’.
Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set “android.useDeprecatedNdk=true” in gradle.properties to continue using the current NDK integration.
解决办法:在app\build.gradle
文件中添加1
2
3
4sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] //disable automatic ndk-build call
}
文件有3种:接口文件.h
; 实现文件.c
,注意与前面的.h
文件同名; .h
与.c
生成的库文件.so
操作步骤小结
From Java to C/C++
Step 1 定义Java接口文件,里面定义好native方法。
Step 2 javah生成.h接口文件 。
Step 3 复制.h文件的文件名,编写C/C++文件。注意要实现.h中的接口。
NDK遇到的问题与注意事项
文件关联问题
写cpp源文件的时候,忘记include头文件。产生java.lang.UnsatisfiedLinkError: No implementation found for
之类的错误
stackoverflow上有关于Android NDK C++ JNI (no implementation found for native…)
的问题。
NDK本地对象数量溢出问题 Local ref table overflow
NDK本地只允许持有512个本地对象,return后会销毁这些对象。必须注意,在循环中创建的本地对象要在使用后销毁掉。
1 |
|
调用Java方法时,注意指定返回值
env->CallBooleanMethod(resArrayList,arrayList_add, javaObject);
ArrayList的add方法返回Boolean
参考:https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html
C++调用C方法
C++文件中,需要调用C里面的方法。如果未经任何处理,会出现无引用错误1
error: undefined reference to '......
因此在C++文件中涉及到C方法,需要声明。
例如1
2
3
4
5
6
7
8#ifdef __cplusplus
extern "C" {
#include "c_file_header.h"
#ifdef __cplusplus
}
#endif
#endif
// ___ 结束声明
javah生成的JNI头文件中也有extern,可作为参考
NDK中使用logcat
配置:Cygwin, NDK 14.1…
可以在NDK中使用logcat,方便调试
需要在mk文件中添加1
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
代码中添加头文件,即可调用logcat的方法1
2
3
4#include <android/log.h>
#define LOG_TAG "rustApp"
__android_log_write(ANDROID_LOG_VERBOSE, LOG_TAG, "My Log");
此时编译出现了错误:1
2
3
4G:/SDK/ndk-bundle/build//../toolchains/x86_64-4.9/prebuilt/windows-x86_64/lib/gcc/x86_64-linux-android/4.9.x/../../../../x86_64-linux-android/bin\ld: warning: skipping incompatible G:/SDK/ndk-bundle/build//../platforms/android-21/arch-x86_64/usr/lib/libc.a while searching for c
G:/SDK/ndk-bundle/build//../toolchains/x86_64-4.9/prebuilt/windows-x86_64/lib/gcc/x86_64-linux-android/4.9.x/../../../../x86_64-linux-android/bin\ld: error: treating warnings as errors
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [G:/openSourceProject/NDKAlgo/app/src/main/obj/local/x86_64/libNDKMan.so] Error 1
出现了error: treating warnings as errors
处理方法,在mk文件中添加LOCAL_DISABLE_FATAL_LINKER_WARNINGS=true
再次编译即可
我们可以使用宏定义简化打log的写法1
2
3
4
5
6
7
8
9#define LOG_TAG "rustApp"
#define LOGV(...) __android_log_write(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , LOG_TAG, __VA_ARGS__)
// 调用
LOGV("This is my log");
Android Studio 3 为library module添加C++支持
as在新建project的时候可以选择支持C++,可以新建一个支持C++的项目来参考。
可以不用自己javah来生成头文件。
在工程中新建android library,将CMakeLists.txt
添加到模块中。这里模块名是native-lib
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp
src/main/cpp/imagetool.cpp)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
修改模块的build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// 添加
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// 添加
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
新建Java文件和C++文件,大致目录如下1
2
3
4
5
6
7
8
9
10
11main
|-- AndroidManifest.xml
|-- cpp
| |-- imagetool.cpp // cpp文件
| `-- native-lib.cpp
|-- java
| `-- com
| `-- example
| `-- myclib
| `-- ImageDetect.java // Java文件
`-- res
ImageDetect.java1
2
3
4
5
6
7
8
9
10
11
12public class ImageDetect {
static {
System.loadLibrary("native-lib");
}
public static native void inputPath(String path);
public static native String stringFromJNI();
public static native int getOne();
}
native-lib.cpp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include <jni.h>
#include <string>
extern "C" {
JNIEXPORT jstring
JNICALL
Java_com_example_myclib_ImageDetect_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "In myclib Hello from C++";
return env->NewStringUTF(hello.c_str());
}
JNIEXPORT jint
JNICALL
Java_com_example_myclib_ImageDetect_getOne(
JNIEnv *env,
jobject /* this */) {
return 9257;
}
}
imagetool.cpp1
2
3
4
5
6
7
8
9
10
11
12
13#include <jni.h>
#include <string>
extern "C"
JNIEXPORT void
JNICALL
Java_com_example_myclib_ImageDetect_inputPath(
JNIEnv *env,
jobject /* this */jobj, jstring inputPath) {
// just test
return;
}
编译运行即可。