Android NDK开发,使用ndk-build编译

06-01 1017阅读

setContentView(R.layout.activity_main);

}

//创建的本地方法,具体功能在C或者C++中实现

public native String fromJNIString();

}

四、手动创建本地方法 fromJNIString() 对应的. h 头文件

1,在电脑的 cmd 或者 AndroidStudio 的 Terminal 中输入 javah -d D:\Demo\NDKDemo\app\src\main\jni -classpath D:\Demo\NDKDemo\app\src\main\java com.ang.ndkdemo.MainActivity

javah -d D:\Demo\NDKDemo\app\src\main\jni -classpath D:\Demo\NDKDemo\app\src\main\java

com.ang.ndkdemo.MainActivity

  • a, -d  D:\Demo\NDKDemo\app\src\main\jni      创建 jni 文件夹并指定. h 输出目录
  • b, D:\Demo\NDKDemo\app\src\main\jni           要创建的. h 头文件输出的绝对路径
  • c, D:\Demo\NDKDemo\app\src\main\java  com.ang.ndkdemo.MainActivity    包含本地方法 (fromJNIString()) 的类路径;注意不要写成了 D:\Demo\NDKDemo\app\src\main\java\com\ang\ndkdemo\MainActivity(把包名中的点 “.” 写成了斜杠 “****”,这样写是不对的) **;**com.ang.ndkdemo.MainActivity(注意是包名 + 类名);
  • 参数说明

    -classpath :类搜索路径,这里表示从当前的 D:\Demo\NDKDemo\app\src\main\java 目录下查找

    -d :将生成的头文件放到当前的 jni 目录下

    -o : 指定生成的头文件名称,默认以类全路径名生成(包名 + 类名. h)

    注意:-d 和 - o 只能使用其中一个参数。

    注意: -d D:\Demo\NDKDemo\app\src\main\jni 和 -classpath D:\Demo\NDKDemo\app\src\main\java  位置可以互换;一下写法和等价于上面的写法;

    javah -classpath D:\Demo\NDKDemo\app\src\main\java -d D:\Demo\NDKDemo\app\src\main\jni com.ang.ndkdemo.MainActivity

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    补充:可以通过 - o 指定生成的头文件名称,如果不指定,默认以类全路径名生成(包名 + 类名. h)

    javah -classpath E:\Demo\JNIDemo\app\src\main\java -o E:\Demo\JNIDemo\app\src\main\java\jni\JNITest.h com.ang.MainActivity

    2,执行以上命令之后:就在项目的 main 文件夹下创建了 jni 文件夹,并且在 jni 文件夹下自动创建了. h 头文件;头文件名也是自动生成的,命名规则是 com_ang_ndkdemo_MainActivity.h(包名 + 类名. h)

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    3,自动生成的 com_ang_ndkdemo_MainActivity.h 头文件代码

    /* DO NOT EDIT THIS FILE - it is machine generated /

    #include

    / Header for class com_ang_ndkdemo_MainActivity */

    #ifndef _Included_com_ang_ndkdemo_MainActivity

    #define _Included_com_ang_ndkdemo_MainActivity

    #ifdef __cplusplus

    extern “C” {

    #endif

    /*

    • Class: com_ang_ndkdemo_MainActivity
    • Method: fromJNIString
    • Signature: ()Ljava/lang/String;

      */

      JNIEXPORT jstring JNICALL Java_com_ang_ndkdemo_MainActivity_fromJNIString

      (JNIEnv *, jobject);

      /JNIEnv 是定义任意native函数的第一个参数(包括调用JNI的RegisterNatives函数注册的函数),指向JVM函数表的指针,函数表中的每一个入口指向一个JNI函数,每个函数用于访问JVM中特定的数据结构。*/

      #ifdef __cplusplus

      }

      #endif

      #endif

      4,生成. h 头文件时候,如果出现 “找不到类文件” 的错误请参考  blog.csdn.net/ezconn/arti… 这篇文章

      注意:

      a. 包名或类名或方法名中含下划线 _ 要用 _1 连接;

      b. 重载的本地方法命名要用双下划线 __ 连接;

      c. 参数签名的斜杠 “/” 改为下划线 “_” 连接,分号 “;” 改为 “_2” 连接,左方括号 “[” 改为 “_3” 连接;

      另外,对于 Java 的 native 方法,static 和非 static 方法的区别在于第二个参数,static 的为 jclass,非 static 的 为 jobject;JNI 函数中是没有修饰符的。

      五、在 jni 目录下创建 c 或者 c++ 文件;

      文件名可以随意写,但需要注意文件类型;Hello.c 文件(.c 后缀的文件为 C)代表内容是 C 代码;Hello.cpp(.cpp 后缀的文件为 C++)文件代表内容是 C++ 代码;

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      C++ 代码(注意 C 和 C++ 代码是有区别),以下分别给出 C 和 C++ 两种实现方式:

      • a,Hello.c 文件。在 C 中没有引用,传递的 env 是个两级指针,用(*env)-> 调用方法且方法中要传入 env.

        #include

        #include “com_ang_ndkdemo_MainActivity.h”

        JNIEXPORT jstring JNICALL

        Java_com_ang_ndkdemo_MainActivity_fromJNIString(JNIEnv* env, jobject obj) {

        return (*env)->NewStringUTF(env,“I am From Native C”);

        }

        • b, Hello.cpp 文件。C++ 中 env 为一级指针,用 env-> 调用方法,无需传入 env;C++ 语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而 C 语言则不会,因此会造成链接时找不到对应函数的情况,此时 C 函数就需要用 extern “C” 进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名;exter  “C”{jni 代码}。

          #include

          #include

          JNIEXPORT jstring JNICALL

          Java_com_ang_ndkdemo_MainActivity_fromJNIString(JNIEnv *env, jobject obj)

          {

          return env->NewStringUTF(“I am From Native C”);

          }

          Java 的 native 方法是如何链接 C/C++ 中的函数的呢?可以通过静态和动态的方式注册 JNI。 以上是通过静态注册的方式。

          静态注册:根据函数名建立 Java 本地方法和 JNI 函数的一一对应关系。

          动态注册:直接告诉 Java native 方法其在 JNI 中对应函数的指针。

          六、配置 build.gradle(Model:App)

          也可以不配置 ndk{}, 这里只是指定编译出哪几种对应的 abi 架构的. so 库,如果不配置,会根据 ndk-build 默认输出对应的 abi 架构的. so 库;最好配置,不然不能编译出自己想要的对应 ABI 架构的. so,如果自己的项目中已经引用其他的. so 库还要做适配;

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

          defaultConfig {

          applicationId “com.ang.demo”

          minSdkVersion 19

          targetSdkVersion 28

          versionCode 1

          versionName “1.0”

          testInstrumentationRunner “android.support.test.runner.AndroidJUnitRunner”

          //ndk编译生成.so文件

          ndk{

          moduleName “Java2c” //生成的so名字,Android.mk文件中已经指定了,这里可以不写

          abiFilters “armeabi”, “armeabi-v7a”, “x86” //输出指定三种abi体系结构下的so库。

          }

          }

          七、编写 Android.mk 文件

          Android.mk 文件一般包含如下信息就够了,差不多可算得上一个模板;根据自己的. so 库名和 C 或者 C++ 文件名修改一下就可以用了;

          LOCAL_PATH:= $(call my-dir)#不用修改

          include $(CLEAR_VARS)#不用修改

          LOCAL_MODULE:= hello #动态库名称

          LOCAL_SRC_FILES:= hello.c #C文件,里面就是我们写的C代码

          include $(BUILD_SHARED_LIBRARY)#生成.so动态库

          #include $(BUILD_STATIC_LIBRARY) 编译出.a的静态库

          还有一种方式,就是让 androidstudio 自动生成;如下是我获取自动生成的 Android.mk 文件的方式:

          a, 紧接着步骤六之后,点击 Androidstudio 菜单栏 Build ->ReBuildProject

          报错:

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

          b, 在 app ——> build ——>intermediater——>ndk(自动创建) 目录下自动创建了一个 Android.mk 文件

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

          Android.mk 文件如下:

          LOCAL_PATH := $(call my-dir)

          include $(CLEAR_VARS)

          LOCAL_MODULE := Java2c

          LOCAL_LDFLAGS := -Wl,–build-id

          LOCAL_SRC_FILES :=

          D:\Demo\NDKDemo\app\src\main\jni\Hello.cpp \

          LOCAL_C_INCLUDES += D:\Demo\NDKDemo\app\src\debug\jni

          LOCAL_C_INCLUDES += D:\Demo\NDKDemo\app\src\main\jni

          include $(BUILD_SHARED_LIBRARY)

          八、修改默认编译工具

          鼠标右键项目,点击 Link C++ Project with Gradle 修改 Androidstudio 默认编译工具, 在 BuildSystem 栏选择 ndk—build, 在 ProjectPath 选项栏,找到刚才创建的 Android.mk 文件(其实就是 Android.mk 文件路径),点击 OK 之后就在 build.gradle(Model:App)的 android{} 中自动生成了 externalNativeBuild { ndkBuild { path ‘src/main/jni/Android.mk’} }

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

          //增加之后如下信息之后,右键项目的时候Link C++ Project with Gradle选项不再显示;

          externalNativeBuild {

          ndkBuild {

          path ‘src/main/jni/Android.mk’

          }

          }

          注意:有的时候需要再次显示 Link C++ Project with Gradle 选项,删掉 externalNativeBuild {ndkBuild {   path ‘src/main/jni/Android.mk’} } 点击 sync now 同步一下;再次右键项目就可以出现了;

          **相关知识:**要将 Gradle 关联到原生库,需要提供一个指向 CMake 或 ndk-build 脚本文件的路径。在构建应用时,Gradle 会以依赖项的形式运 CMake 或 ndk-build,并将共享的. so 库打包到 APK 中。externalNativeBuild 就是配置的脚本路径;

          九、最后在 MainActivity 中加载我们生成的动态库:

          注意:加载生成的动态库指定的文件名(System.loadLibrary(“Java2c”);)和生成. so 时指定的名字(buil.gradle 中的 ndk{moduleName “Java2c” }),还有 Android.mk 中 LOCAL_MODULE := Java2c 三者是否一致;

          例如: 下图加载生成的动态库指定的文件名为:Java2JNI 和上面生成. so 时指定的名字为:Java2c 还有 Android.mk 中 LOCAL_MODULE := Java2c 三者不一致,就会出现 UnsatisfiedLinkError 异常;

          public class MainActivity extends AppCompatActivity {

          @Override

          protected void onCreate(Bundle savedInstanceState) {

          super.onCreate(savedInstanceState);

          setContentView(R.layout.activity_main);

          Toast.makeText(this,new Java2C().fromJNIString(),Toast.LENGTH_LONG).show();

          }

          //加载.so库 Java2c为库名

          static {

          System.loadLibrary(“Java2c”);

          }

          public native String fromJNIString();

          }

          关于 UnsatisfiedLinkError 异常的原因还有很多,这里针对 NDK 开发总结几种可能的原因:https://blog.csdn.net/ezconn/article/details/82531893

          **总结:**以上不用编译成. so 库放到指定的路径下;如果需要. so 库(给其他项目使用,例如使用百度地图服务,就要使用其提供的. so 库)如下手动编译并使用. so 库;

          手动编译. so 文件

          从步骤八开始的第二种方式,不指定 AndroidStudio 编译工具(Cmake 或者 ndk-build),直接手动生成. so 库

          a, cmd 或者 Android studio 的 Terminal 中进入 jni 的上一级目录

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

          b, 输入 ndk-build 命令,在 jni 同级的目录中生成了一个 libs 文件夹,里面生成了各个 cup 架构对应的. so 文件,

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

          c, 应用. so 动态库   

          译工具(Cmake 或者 ndk-build),直接手动生成. so 库

          a, cmd 或者 Android studio 的 Terminal 中进入 jni 的上一级目录

          [外链图片转存中…(img-smcj6v50-1738829158448)]

          b, 输入 ndk-build 命令,在 jni 同级的目录中生成了一个 libs 文件夹,里面生成了各个 cup 架构对应的. so 文件,

          [外链图片转存中…(img-KounFW6m-1738829158448)]

          c, 应用. so 动态库

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码