前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android NDK 初步

Android NDK 初步

作者头像
AnRFDev
发布2021-02-01 15:32:19
9430
发布2021-02-01 15:32:19
举报
文章被收录于专栏:AnRFDevAnRFDev
  • 开发环境: win7 64,Android Studio 2.1

需要工具:NDK,Cygwin

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

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

代码语言:javascript
复制
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,添加下面的指令,使用英文界面。

代码语言:javascript
复制
export LANG='en_US'
export LC_ALL='en_US.GBK'

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

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

代码语言:javascript
复制
Locale=zh_CN
Charset=GBK

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

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

代码语言:javascript
复制
NDK=/cygdrive/G/SDK/ndk-bundle/ndk-build.cmd
export NDK

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

代码语言:javascript
复制
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工程。

代码语言:javascript
复制
Administrator@rust-PC /cygdrive/g/rust_proj/android-ndk-android-mk/hello-jni
$ ndk-build.cmd
# 输出很多信息

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

代码语言:javascript
复制
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报错。需要添加参数

代码语言:javascript
复制
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文件中添加

代码语言:javascript
复制
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后会销毁这些对象。必须注意,在循环中创建的本地对象要在使用后销毁掉。

代码语言:javascript
复制
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里面的方法。如果未经任何处理,会出现无引用错误

代码语言:javascript
复制
error: undefined reference to '......

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

例如

代码语言:javascript
复制
#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文件中添加

代码语言:javascript
复制
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

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

代码语言:javascript
复制
#include <android/log.h>
#define LOG_TAG    "rustApp"

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

此时编译出现了错误:

代码语言:javascript
复制
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的写法

代码语言:javascript
复制
#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

代码语言:javascript
复制
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

代码语言:javascript
复制
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++文件,大致目录如下

代码语言:javascript
复制
main
|-- AndroidManifest.xml
|-- cpp
|   |-- imagetool.cpp   // cpp文件
|   `-- native-lib.cpp
|-- java
|   `-- com
|       `-- example
|           `-- myclib
|               `-- ImageDetect.java // Java文件
`-- res

ImageDetect.java

代码语言:javascript
复制
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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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;
}

编译运行即可。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-06-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用adb查看手机CPU架构信息
  • 使用 SDK Manager 配置安装 NDK
  • 操作示例NDK工程
  • 使用C/C++实现JNI
  • 操作步骤小结
  • NDK遇到的问题与注意事项
    • 文件关联问题
      • NDK本地对象数量溢出问题 Local ref table overflow
        • 调用Java方法时,注意指定返回值
          • C++调用C方法
          • NDK中使用logcat
          • Android Studio 3 为library module添加C++支持
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档