写点什么

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

用户头像
Android架构
关注
发布于: 1 小时前

#define _Included_com_ang_ndkdemo_MainActivity#ifdef __cplusplusextern "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 <jni.h>#include "com_ang_ndkdemo_MainActivity.h"JNIEXPORT jstring JNICALLJava_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 <com_ang_ndkdemo_MainActivity.h>#include <stdio.h>


JNIEXPORT jstring JNICALLJava_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 19targetSdkVersion 28versionCode 1versionName "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 := (CLEAR_VARS)


LOCAL_MODULE := Java2cLOCAL_LDFLAGS := -Wl,--build-idLOCAL_SRC_FILES :=


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


LOCAL_C_INCLUDES += D:\Demo\NDKDemo\app\src\debug\jniLOCAL_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'}?}


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


点击 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 {


@Overrideprotected 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 文件

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android NDK开发,使用ndk-build编译,androidkotlin教程