专栏首页AnRFDevAndroid NDK 初步

Android NDK 初步

  • 开发环境: win7 64,Android Studio 2.1

需要工具:NDK,Cygwin

使用adb查看手机CPU架构信息

将手机通过USB连接到电脑,adb shell进入手机根目录,执行cat /proc/cpuinfo

shell@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,添加下面的指令,使用英文界面。

export LANG='en_US'
export LC_ALL='en_US.GBK'

配置text选项,在option里的text可设置。

可以在G:\soft\Cygwin\home\Administrator\.minttyrc中看到。

Locale=zh_CN
Charset=GBK

设置完字体后可以避免中文乱码。

配置 G:\soft\Cygwin\home\Administrator\.bash_profile

NDK=/cygdrive/G/SDK/ndk-bundle/ndk-build.cmd
export NDK

在Cygwin中查找NDK位置,可以看到在SDK目录里面

Administrator@rust-PC /cygdrive/g/soft/Cygwin/home/Administrator
$ echo $NDK
/cygdrive/G/SDK/ndk-bundle/ndk-build.cmd

操作示例NDK工程

JDK10已经不提供javah这个工具了,我们可以使用as支持c++的功能;详情见下文

生成一次试试。从github上获取android-ndk-android-mk,进入hello-jni工程。

Administrator@rust-PC /cygdrive/g/rust_proj/android-ndk-android-mk/hello-jni
$ ndk-build.cmd
# 输出很多信息

编译成功后,自动生成一个libs目录,编译生成的.so文件放在里面。

Administrator@rust-PC /cygdrive/g/rust_proj/NDKTest/app/src/main
$ ndk-build.cmd
[armeabi] Install        : librust_ndk.so => libs/armeabi/librust_ndk.so
# 进入java目录,编译.h文件
Administrator@rust-PC /cygdrive/g/rust_proj/NDKTest/app/src/main/java
$ javah com.rustfisher.ndktest.HelloJNI
# 会生成一个.h文件

将它复制到jni文件夹下;这个就是JNI层的代码。

Ubuntu下javah报错。需要添加参数

javah -cp /home/rust/Android/Sdk/platforms/android-25/android.jar:. com.example.LibUtil

使用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文件中添加

sourceSets.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后会销毁这些对象。必须注意,在循环中创建的本地对象要在使用后销毁掉。

env->DeleteLocalRef(local_ref);// local_ref 是本地创建的对象

调用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里面的方法。如果未经任何处理,会出现无引用错误

error: undefined reference to '......

因此在C++文件中涉及到C方法,需要声明。

例如

#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文件中添加

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

代码中添加头文件,即可调用logcat的方法

#include <android/log.h>
#define LOG_TAG    "rustApp"

__android_log_write(ANDROID_LOG_VERBOSE, LOG_TAG, "My Log");

此时编译出现了错误:

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: 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的写法

#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

cmake_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

android {
    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++文件,大致目录如下

main
|-- AndroidManifest.xml
|-- cpp
|   |-- imagetool.cpp   // cpp文件
|   `-- native-lib.cpp
|-- java
|   `-- com
|       `-- example
|           `-- myclib
|               `-- ImageDetect.java // Java文件
`-- res

ImageDetect.java

public 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.cpp

#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.cpp

#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;
}

编译运行即可。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 构建React Native官方Examples

    尊重版权,未经授权不得转载 本文出自:贾鹏辉的技术博客(http://www.devio.org) 告诉大家一个好消息,为大家精心准备的React N...

    CrazyCodeBoy
  • linux 环境 编译 ijkplayer so 库实践

    公司的一个项目新项目涉及到音频播放的内容,音频格式从常见的 mp3 到冷门的无损音乐 ape 都有,琢磨了好久,最后选中了 B 站的开源库 ijkplayer ...

    业志陈
  • 音视频学习路线(二)

    本文主要讲一下笔者计划在音视频方向的学习路线计划,主要以Android开发为例,让我们一起进步。

    PengJie
  • Android中JNI与NDK

    JNI的定义:Java Native Interface 也就是Java本地的接口。它的作用就是使Java与本地的其他语言(C C++)交互。

    全栈程序员站长
  • Android之NDK开发初体验

    记得前年开始自己在项目中使用第三方so库的时候就接触NDK编程开发了,只不过哪个时候自己是输出了"Hello Wrold~!"。如今一年多的时间过去了,回头拾起...

    静默加载
  • Android Studio 2.2 Native 开发新特性

    在本文所述新特性之外,参考资料中还提供了利用向导工具创建具有 Native 支持的 Android 新项目的方法。由于这个新特性比较明显,本文未做详述。

    QQ音乐技术团队
  • Android开发笔记(六十九)JNI实战

    NDK全称为Native Development Kit,意即原生的开发工具,NDK允许开发者在APP中通过C/C++代码执行部分程序。它是Android提...

    用户4464237
  • 一定能成功的Android NDK环境配置教程

    Carson.Ho
  • Android的JNI【实战教程】2⃣️--AS下NDK环境配置及第一个工程

    通过上一篇相信大家已经对java和c/c++之间的桥梁JNI有了初步认识,那么接下来就让我们写个小demo来实现。 http://blog.csdn.net...

    先知先觉
  • 良心解析 | 搭建NDK环境历程及问题记录 暨 Android Studio 2.3.3 to 3.3 填坑之路(Update坑 + AVD坑 + NDK坑)

    (前言部分有点日记性质,对整个历程做了一个概况,如果你时间紧迫,可以跳过这部分直接看正文部分,正文部分分点分部分给出问题的描述和对应的解决方法)

    凌川江雪
  • Android:JNI 与 NDK到底是什么?(含实例教学)

    本文根据版本的不同介绍了两种在Android Studio中实现 NDK的方法:Android Studio2.2 以下 & 2.2以上

    Carson.Ho
  • JNI和NDK的区别

    android常用的开发方式是java封装的库,而这些库的底层实现是由C/C++实现,如媒体,图形库等

    阳光岛主
  • NanoMsg框架|Android Studio编译NanoMsg源码

    前面的章节已经把NanoMsg的简介,及C#相关的NNanoMsg使用Demo已经介绍完成了,今天这篇开始我们就要写关于Android怎么使用NanoMsg的文...

    Vaccae
  • Android JNI(一)——NDK与JNI基础

    Android 平台从一开就已经支持了C/C++了。我们知道Android的SDK主要是基于Java的,所以导致了在用Android SDK进行开发的工程师们都...

    隔壁老李头
  • jni和ndk详解

    NDK(Native Development Kit)“原生”也就是二进制 android常用的开发方式是java封装的库,而这些库的底层实现是由C/C++实现...

    xiangzhihong
  • Android Studio开发so库实践

    用户1130025
  • 老生常谈-FFmpeg 的编译问题轻松搞定

    前几天发了一篇 FFmpeg 调用 Android MediaCodec 进行硬解码 的文章,这里面的技术点不算太难,也还是调用 FFmpeg 的常用接口操作,...

    音视频开发进阶
  • 我的非线性视频编辑器MiaoVideoCut(1) --- 视频基础知识及环境搭建

    所谓视频编码方式就是指通过特定的压缩技术,将某个视频格式的文件转换成另一种视频格式文件的方式。视频流传输中最为重要的编解码标准有国际电联的H.261、H.26...

    瑶瑶
  • 【Android 音视频开发打怪升级:FFmpeg音视频编解码篇】一、FFmpeg so库编译

    网上其实已经有很多的关于FFmpeg so库编译的分享,但是大部分都是直接把配置文件的内容贴出来。我想大部分取搜索 「如何编译FFmpeg so库」的人,对交叉...

    开发的猫

扫码关注云+社区

领取腾讯云代金券