首页 > 代码库 > JNI_2

JNI_2

JNI开发的流程

①交叉编译的概念
Java 调用 C/C++
C/C++ 本地语言 特点 平台相关性强
windows上编写 能在linux上运行的 本地代码
交叉编译 在一个平台上编译出另一个平台可以执行的本地代码
平台 CPU平台         X86(Intel AMD) ARM(高通骁龙 华为 海思 麒麟 三星 MTK)99%android设备
                    mips 嵌入式设备上
    操作系统平台    windows linux max OS  unix  类unix
不同平台支持的native code不同
交叉编译的原理就是在一个平台上模拟另外一个平台的特点进行编译

在windows上编写能够在android上运行的本地代码
NDK native develop kit  

NDK目录的结构

技术分享

 


技术分享

 NDk Helloworld


NDK开发相关文档&案例下载
https://developer.android.google.cn/ndk/index.html
 
JNI开发通用流程 NDK-BUILD(studio eclipse都可以使用的方法)
①在java代码中声明native 方法  native方法没有实现

  1. //用native关键字声明本地方法 本地方法不用实现
  2. public native String helloFromC();

 

②在模块根目录下(如果是eclipse就是在项目的根目录下)创建jni文件夹,在文件夹中创建.c的源代码

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<jni.h>
  4. //
  5. // Created by fullcircle on 2017/2/18.
  6. //
  7. //java程序还是应用的入口 在C中只是实现一些函数 等待着java去调用
  8. //native函数命名规则 Java_包名_类名(包含native方法的java类)_native方法名
  9. //JNIEnv* env
  10. //JNIEnv 是结构体 JNINativeInterface的一级指针
  11. //env是JNIEnv的一级指针 所以env就是结构体JNINativeInterface的二级指针
  12. //JNINativeInterface 这个结构体中声明了大量的函数指针 这些函数指针在Jni开发中做用非常重要
  13. //jobject thiz 哪个java对象通过jni调用到这个方法 这个jobject 就是这个java对象
  14. //对于当前的案例来说 这个jobject就是MainActivity
  15. //jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
  16. // jobject thiz )
  17. //JNIEnv* env,jobject thiz 这两个参数是必须传入的
  18. jstring Java_com_itheima_jni97_MainActivity_helloFromC(JNIEnv* env,jobject thiz){
  19. char* cstr = "hello from c!!!!";
  20. //NewStringUTF 作用把c的字符串(char* 类型)转换成java的字符串
  21. //jstring (*NewStringUTF)(JNIEnv*, const char*);
  22. //return (**env).NewStringUTF(env, cstr);
  23. return (*env)->NewStringUTF(env, cstr);
  24. }

③ 在jni目录下创建一个Android.mk文件

技术分享

技术分享

 

 


  1. #LOCAL_PATH 获取当前路径
  2. LOCAL_PATH := $(call my-dir)
  3. # 清除上次编译获取的编译环境变量 会保存LOCAL_PATH
  4. include $(CLEAR_VARS)
  5. #指定编译生成的模块叫什么名字
  6. LOCAL_MODULE := hello-jni
  7. #指定要编译的.c源文件叫什么名字
  8. LOCAL_SRC_FILES := hello.c
  9. #生成动态链接库 .so文件
  10. include $(BUILD_SHARED_LIBRARY)
Android.mk文件作用 向编译系统描述 我要编译的文件在什么位置 叫什么名字 要生成的文件叫什么名字 是什么类型

④在项目的根目录下调用ndk-build编译项目

如果是studio环境 还需要指定 NDK_PROJECT_PATH=app

编译成功后会在libs目录下创建一系列文件夹 包含了生成的.so文件

⑤在运行之前 需要通过System.loadLibrary加载.so文件 注意加载.so文件 文件名字不包含lib前缀和.so后缀
⑥如果是studio环境 需要在模块的.gradle文件中加上如下内容
sourceSets.main.jniLibs.srcDirs = [‘libs‘] 放到android{}中
 
JNI开发常见错误
①Caused by: java.lang.UnsatisfiedLinkError: Native method not found 本地方法没有找到
出现原因 c函数名字 不符合 Native函数的命名规则
解决办法 用javah生成头文件
javah使用方法 到模块的src/main/java 目录下运行javah
格式 javah 包含native方法的java类的全类名

原因2忘记使用System.loadlibrary加载.so文件
解决办法:使用静态代码块在Activity加载的时候就把.so文件加载进来
  1. static {
  2. System.loadLibrary("hello-jni");
  3. }
②java.lang.UnsatisfiedLinkError: Couldn‘t load libhello-jni from loader ....findLibrary returned null
出现原因 在System.loadlibrary加载.so文件 文件名字写错
原因2 当前的.so不支持设备的cpu平台 解决方法 在jni目录下创建Application.mk 指定当前应用的.so支持哪些cpu平台
  1. APP_ABI := armeabi x86
 
如果使用NDK-build做jni开发 Android.mk Appliction.mk 尽量ctrl+c ctrl+V

studio中NDK-Build简便流程
①在java代码中声明native方法
②在main目录下创建jni目录 并且创建一个Android.mk
③右键单击模块 选择如下内容
技术分享
 
技术分享
 实际上就是在build.gradle中加入了如下内容
  1. android{
  2. ......
  3. externalNativeBuild {
  4. ndkBuild {
  5. path ‘src/main/jni/Android.mk‘
  6. }
  7. }
  8. }
④编写C的代码 这个时候C的代码就有提示了
⑤ System.loadLibrary("")加载对应的.so文件
⑥ 运行模块 不用单独编译
 
CMake方式进行NDK开发
技术分享

 

技术分享

 

技术分享
 技术分享
 项目创建出来之后 会在模块的根目录下创建一个CMakeLists.txt 构建脚本
 
项目创建之后手动加入CMake支持
技术分享
 技术分享技术分享

 

技术分享

 

 点击ok之后就会添加cmake的支持
实际上就是在gradle文件下添加如下内容
技术分享

 

技术分享
 
向logcat输出日志:
①Android.mk文件增加以下内容 LOCAL_LDLIBS += -llog
如果是CMake方式 studio帮助创建的CMakeLists.txt文件中已经加入了导入liblog的内容不需要特殊配置
 
②C代码中增加以下内容 #include <android/log.h> #define LOG_TAG "System.out" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) C代码中使用logcat, 例: LOGI("info\n"); LOGD("debug\n");

JNI_2