ndk

ndk开发

一直没机会接触ndk开发,抽了个时间研究了下,在网上查了很多别人写的博客,也不知道为什么总是会出问题,其实很多东西也都是这样,有时候看到一种技术原来别人已经总结写的很好了,自己何必去写呢,别人写的总归是别人的,不如自己去过一遍来的深刻,

开发环境配置

下载 NDK 和构建工具

+ NDK 这套工具集允许您为 Android 使用 C 和 C++ 代码,并提供众多平台库,让您可以管理原生 Activity 和访问物理设备组件,例如传感器和触摸输入。 . + CMake一款外部构建工具,可与 Gradle 搭配使用来构建原生库。如果您只计划使用 ndk-build,则不需要此组件。 +LLDB 一种调试程序,Android Studio 使用它来调试原生代码. + Tools > Android > SDK Manager ![这里写图片描述](https://img-blog.csdn.net/20170109184319124?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGRsX2NzZG4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) ##创建支持 C/C++ 的新项目 >>1.在向导的 Configure your new project 部分,选中 Include C++ Support 复选框
  static {
        System.loadLibrary("JniUtilsTest");
    }
    public native String getCLanguageString();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        NdkJniUtils juti=new NdkJniUtils();
        tv.setText(juti.getCLanguageString()+"\n"+stringFromJNI());
    }
- 上述代码应该都能看得懂,
- 1.建一个类提供一个native方法;
- 2cmd命令进入E:\viewtest\MyApplication\app\build\intermediates\classes\debug 执行java -jni 全类名命令会生成一个.h的头文件
- 我们在src目录下 main目录下创建一个jni的文件夹;把刚才的头文件剪切过来
- 然后我们在jni目录下创建一个命名规范的.c文件内容如下

这里写代码片 #include “com_ldl_jnitest_NdkJniUtils.h” JNIEXPORT jstring JNICALL Java_com_ldl_jnitest_NdkJniUtils_getCLanguageString(JNIEnv * env, jobject obj){ return (*env)->NewStringUTF(env,”北京你好”); } ##创建 CMake 构建脚本 如果您的原生源文件还没有 CMake 构建脚本,则您需要自行创建一个并包含适当的 CMake 命令。CMake 构建脚本是一个纯文本文件,您必须将其命名为 CMakeLists.txt。 - 1.从 IDE 的左侧打开 Project 窗格并从下拉菜单中选择 Project 视图。 - 2.右键点击您的模块的根目录并选择 New > File。 注:您可以在所需的任意位置创建构建脚本。不过,在配置构建脚本时,原生源文件和库的路径将与构建脚本的位置相关。 - 3.输入“CMakeLists.txt”作为文件名并点击 OK。


cmake_minimum_required(VERSION 3.4.1)
# Sets the minimum version of CMake required to build the native
library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.cmake_minimum_required(VERSION 3.4.1)
           # Creates and names a library, sets it as either STATIC
            # or SHARED, and provides the relative paths to its source code.
            # You can define multiple libraries, and CMake builds it for you.
            # Gradle automatically packages shared libraries with your APK.
            #设置so库的名称,调用时候以这个为准

            add_library( # Sets the name of the library.
                         JniUtilsTest
         #设置这个库是否是个分享的库
         # Sets the library as a shared library.
         SHARED

         # Provides a relative path to your source file(s).
         # Associated headers in the same location as their source
         # file are automatically included.
         #映射so库的本地路径
         src/main/cpp/native-lib.cpp
         src/main/jni/JniUtilsTest.c
         )

        # Searches for a specified prebuilt library and stores the path as a
        # variable. Because system libraries are included in the search path by
        # default, you only need to specify the name of the public NDK library
        # you want to add. CMake verifies that the library exists before
        # completing its build.
        #将 find_library() 命令添加到您的 CMake 构建脚本中以定位 NDK 库,并将其路径存储为一个变量。您可以使用此变量在构建脚本的其他部分引用 NDK 库。以下示例可以定位 Android 特定的日志支持库并将其路径存储在 log-lib 中:

        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 )

        # Specifies libraries CMake should link to your target library. You
        # can link multiple libraries, such as libraries you define in the
        # build script, prebuilt third-party libraries, or system libraries.
        #为了确保您的原生库可以在 log 库中调用函数,您需要使用 CMake 构建脚本中的 #target_link_libraries()
        target_link_libraries( # Specifies the target library.
                                 JniUtilsTest

                               # Links the target library to the log library
                               # included in the NDK.
                               ${log-lib} )

其它细节

              NDK 还以源代码的形式包含一些库,您在构建和关联到您的原生库时需要使用这些代码。您可以使用 CMake 构建脚本中的 add_library() 命令,将源代码编译到原生库中。要提供本地 NDK 库的路径,您可以使用 ANDROID_NDK 路径变量,Android Studio 会自动为您定义此变量。 

以下命令可以指示 CMake 构建 android_native_app_glue.c,后者会将 NativeActivity 生命周期事件和触摸输入置于静态库中并将静态库关联到 native-lib: 
    add_library( app-glue
                 STATIC
                 ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

    # You need to link static libraries against your shared native library.
    target_link_libraries( native-lib app-glue ${log-lib} )


    添加其他预构建库 

    添加预构建库与为 CMake 指定要构建的另一个原生库类似。不过,由于库已经预先构建,您需要使用 IMPORTED 标志告知 CMake 您只希望将库导入到项目中: 
    add_library( imported-lib
                 SHARED
                 IMPORTED )



 然后,您需要使用 set_target_properties() 命令指定库的路径,如下所示。 

某些库为特定的 CPU 架构(或应用二进制接口 (ABI))提供了单独的软件包,并将其组织到单独的目录中。此方法既有助于库充分利用特定的 CPU 架构,又能让您仅使用所需的库版本。要向 CMake 构建脚本中添加库的多个 ABI 版本,而不必为库的每个版本编写多个命令,您可以使用 ANDROID_ABI 路径变量。此变量使用 NDK 支持的一组默认 ABI,或者您手动配置 Gradle 而让其使用的一组经过筛选的 ABI。例如: 
add_library(...)
set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )


为了确保 CMake 可以在编译时定位您的标头文件,您需要使用 include_directories() 命令,并包含标头文件的路径: 
include_directories( imported-lib/include/ )


注:如果您希望封装一个并不是构建时依赖项的预构建库(例如在添加属于 imported-lib 依赖项的预构建库时),则不需要执行以下说明来关联库。 

要将预构建库关联到您自己的原生库,请将其添加到 CMake 构建脚本的 target_link_libraries() 命令中: 
target_link_libraries( native-lib imported-lib app-glue ${log-lib} )

将 Gradle 关联到您的原生库

   要将 Gradle 关联到您的原生库,您需要提供一个指向 CMake 或 ndk-build 脚本文件的路径。在您构建应用时,Gradle 会以依赖项的形式运行 CMake 或 ndk-build,并将共享的库封装到您的 APK 中。Gradle 还使用构建脚本来了解要将哪些文件添加到您的 Android Studio 项目中,以便您可以从 Project 窗口访问这些文件。如果您的原生源文件没有构建脚本,则需要先创建 CMake 构建脚本,然后再继续。 

    将 Gradle 关联到原生项目后,Android Studio 会更新 Project 窗格以在 cpp 组中显示您的源文件和原生库,在 External Build Files 组中显示您的外部构建脚本。 

    注:更改 Gradle 配置时,请确保通过点击工具栏中的 Sync Project  应用更改。此外,如果在将 CMake 或 ndk-build 脚本文件关联到 Gradle 后再对其进行更改,您应当从菜单栏中选择 Build > Refresh Linked C++ Projects,将 Android Studio 与您的更改同步

使用 Android Studio UI

您可以使用 Android Studio UI 将 Gradle 关联到外部 CMake 或 ndk-build 项目: 
1.从 IDE 左侧打开 Project 窗格并选择 Android 视图。 
2.右键点击您想要关联到原生库的模块(例如 app 模块),并从菜单中选择 Link C++ Project with Gradle。您应看到一个如图 4 所示的对话框。 
3.从下拉菜单中,选择 CMake 或 ndk-build。 a.如果您选择 CMake,请使用 Project Path 旁的字段为您的外部 CMake 项目指定 CMakeLists.txt 脚本文件。 
b.如果您选择 ndk-build,请使用 Project Path 旁的字段为您的外部 ndk-build 项目指定 Android.mk 脚本文件。如果 Application.mk 文件与您的 Android.mk 文件位于相同目录下,Android Studio 也会包含此文件。 

手动配置 Gradle

要手动配置 Gradle 以关联到您的原生库,您需要将 externalNativeBuild {} 块添加到模块级 build.gradle 文件中,并使用 cmake {} 或 ndkBuild {} 对其进行配置: 
android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Encapsulates your external native build configurations.
  externalNativeBuild {

// Encapsulates your CMake build configurations.
cmake {

  // Provides a relative path to your CMake build script.
  path "CMakeLists.txt"
}

}
}

指定 ABI

   默认情况下,Gradle 会针对 NDK 支持的 ABI 将您的原生库构建到单独的 .so 文件中,并将其全部封装到您的 APK 中。如果您希望 Gradle 仅构建和封装原生库的特定 ABI 配置,您可以在模块级 build.gradle 文件中使用 ndk.abiFilters 标志指定这些配置,如下所示: 
    android {
      ...
      defaultConfig {
        ...
        externalNativeBuild {
          cmake {...}
          // or ndkBuild {...}
        }

        ndk {
          // Specifies the ABI configurations of your native
          // libraries Gradle should build and package with your APK.
          abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                       'arm64-v8a'
        }
      }
      buildTypes {...}
      externalNativeBuild {...}
    }

在大多数情况下,您只需要在 ndk {} 块中指定 abiFilters(如上所示),因为它会指示 Gradle 构建和封装原生库的这些版本。不过,如果您希望控制 Gradle 应当构建的配置,并独立于您希望其封装到 APK 中的配置,请在 defaultConfig.externalNativeBuild.cmake {} 块(或 defaultConfig.externalNativeBuild.ndkBuild {} 块中)配置另一个 abiFilters 标志。Gradle 会构建这些 ABI 配置,不过仅会封装您在 defaultConfig.ndk{} 块中指定的配置。

为了进一步降低 APK 的大小,请考虑配置 ABI APK 拆分,而不是创建一个包含原生库所有版本的大型 APK,Gradle 会为您想要支持的每个 ABI 创建单独的 APK,并且仅封装每个 ABI 需要的文件。如果您配置 ABI 拆分,但没有像上面的代码示例一样指定 abiFilters 标志,Gradle 会构建原生库的所有受支持 ABI 版本,不过仅会封装您在 ABI 拆分配置中指定的版本。为了避免构建您不想要的原生库版本,请为 abiFilters 标志和 ABI 拆分配置提供相同的 ABI 列表。

指定可选配置

您可以在模块级 build.gradle 文件的 defaultConfig {} 块中配置另一个 externalNativeBuild {} 块,为 CMake 或 ndk-build 指定可选参数和标志。与 defaultConfig {} 块中的其他属性类似,您也可以在构建配置中为每个产品风味重写这些属性。 

例如,如果您的 CMake 或 ndk-build 项目定义多个原生库,您可以使用 targets 属性仅为给定产品风味构建和封装这些库中的一部分。以下代码示例说明了您可以配置的部分属性: 
android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake or ndk-build script.
    externalNativeBuild {

      // For ndk-build, instead use ndkBuild {}
      cmake {

        // Passes optional arguments to CMake.
        arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

        // Sets optional flags for the C compiler.
        cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2"

        // Sets a flag to enable format macro constants for the C++ compiler.
        cppFlags "-D__STDC_FORMAT_MACROS"
      }
    }
  }

  buildTypes {...}

  productFlavors {
    ...
    demo {
      ...
      externalNativeBuild {
        cmake {
          ...
          // Specifies which native libraries to build and package for this
          // product flavor. If you don't configure this property, Gradle
          // builds and packages all shared object libraries that you define
          // in your CMake or ndk-build project.
          targets "native-lib-demo"
        }
      }
    }

    paid {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets "native-lib-paid"
        }
      }
    }
  }

  // Use this block to link Gradle to your CMake or ndk-build script.
  externalNativeBuild {
    cmake {...}
    // or ndkBuild {...}
  }
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值