JNI相关配置javah,ndk-build和指令集以及.mk文件详细讲解(亲测可用)

关于JNI,相比大家都不陌生,主要是最近工作用到了很多JNI相关的内容,踩了很多坑,记录下为了以后的学习者少走弯路.
一.先写一下Android studio NDK的下载和配置方式.
1.Ctrl+alt+S打开Settings面板
2.搜索界面输入SDK,之后选择SDK Tools
3.选择NDK工具点击OK下载即可.
这里写图片描述
点击NDK选中即可下载,建议把CMake一同下载

下载完需要配置NDK的路径
按住Ctrl+Shift+Alt+S打开Project Structure,选择SDK Location,选择下载的NDK-bundle的位置,如下图所示
这里写图片描述

二.javah和ndk-build的作用以及Android studio配置方式和linus指令集生成
1.javah命令的作用:生成java调用c++文件所需的.h头文件,头文件要符合JNI的规范 JAVA_包名类名方法名,具体实例如下
这里写图片描述

2.Android External Tools工具中配置javah命令和ndk-build命令

javah命令的配置方式及相关参数
打开Settings界面,选择ExternalTools工具,点击+号,如下图所示
这里写图片描述

Program: JDKPath \bin\javah.exe
Parameters: -classpath . -jni -o ModuleFileDir /src/main/jni/Prompt FileClass
Working Directory: ModuleFileDir \src\main\Java

使用方式,在生成之前先选择build—make project编译下代码生成对应的.class字节码文件,选中要生成.h头文件的类,右键External Tools—javah 会弹出一个需要输入生成的文件的名字
这里写图片描述

这里写图片描述

生成之后的.h文件在jni目录下
这里写图片描述

之后就会在和java同级的目录下生成jni的文件夹,里面存放着.h头文件.

Android studio ndk-build指令生成.so文件的配置方式

这里写图片描述

Program配置的是ndk-build.cmd的安装目录
Working Directory指的是工作空间的目录
三.JNI中.mk文件的格式和作用.
在jni的目录下新建一个Android.mk文件,内容如下
LOCAL_PATH := (callmydir)include (CLEAR_VARS)

LOCAL_MODULE := nativeTest

LOCAL_SRC_FILES := nativeTest.cpp

include (BUILDSHAREDLIBRARY)LOCALMODULE.soLOCALSRCFILEScc++.Application.mkAPPPROJECTPATH:= (call my-dir)
APP_MODULES := nativeTest
APP_ABI:= all

APP_ABI指的是生成的.so文件支持的CPU架构,指定All就是指生成所有的类型的CPU架构
生成后的libs文件如图所示
这里写图片描述

然后再写一下Android studio中调用.so,so文件的存放方式.可以调用成功的有两种配置方式

第一种就是在main目录下和java平级创建jniLibs文件夹,创建不同的CPU架构的文件夹,如下图所示:
这里写图片描述

第二种方式是把.so放入和src平级的libs目录下,但是要在gradle文件夹下配置一些命令,如图所示
这里写图片描述

gradule文件如下图所示

这里写图片描述

注意代码是添加在Android节点下面
代码如下:
sourceSets.main{
jni.srcDirs = []
jniLibs.srcDir “src/main/libs”
}
或者
sourceSets.main{
jniLIbs.srcDirs = [‘libs’]
}
这个代码配置的作用就是更改jniLibs目录指向libs.
主要是因为Android studio和之前的eclipse的目录结构的不同造成的.
javah指令集的调用方式,简单来说就是进入到java目录,找到确切的类执行javah命令具体如下

-d ../jni testjni.jnidemo.AndroidJNI(包名.类名)

下面讲解一个jni调用的完整demo,附带源码

1.先创建一个带有native方法的类 HelloWorld.class

package com.geocompass.sdk.map.jni;

/**
 * Created by liuxu on 2016/12/28.
 */

public class HelloWorld {
    public  static native String stringFromJNI1();
}

然后用刚才配置的javah命令工具或者javah linux指令集生成.h头文件
build–make project 之后再运行javah命令,生成的HelloWorld.h如下

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_geocompass_sdk_map_jni_HelloWorld */

#ifndef _Included_com_geocompass_sdk_map_jni_HelloWorld
#define _Included_com_geocompass_sdk_map_jni_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_geocompass_sdk_map_jni_HelloWorld
 * Method:    stringFromJNI1
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_geocompass_sdk_map_jni_HelloWorld_stringFromJNI1
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

注意生成的.h文件 方法名命名方式java–包名–类名–方法名的形式
本地方法的实现HelloWorld.cpp代码如下:

//
// Created by liuxu on 2016/12/28.
//
#include "HelloWorld.h"
#include <string.h>

extern "C"
JNIEXPORT jstring
JNICALL Java_com_geocompass_sdk_map_jni_HelloWorld_stringFromJNI1(
        JNIEnv *env,
        jobject obj) {
//    std::string hello = "Hello from C++";
    return env->NewStringUTF("Hello from C++");
}

Android.mk代码

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE     := test
LOCAL_SRC_FILES  := HelloWorld.cpp


include $(BUILD_SHARED_LIBRARY)

Application.mk代码

APP_ABI:= all

注意.cpp文件要把.h包含进去,然后还要遵循JNI的规范,注意传入的数据类型,native方法是没有参数的,但是JNI的实现方法带有两个参数,第一个是个JNI引用的指针,第二个要注意是jclass还是jobject,之前我执行ndk.builid的时候一直报错,就是因为.h文件和.cpp文件的数据类型不一致造成的.
然后进入项目的jni文件夹下,如图所示
这里写图片描述

之后执行ndk-build指令,前提是你配置了ndk的环境变量,配置方式我就不多做阐述了.执行之后的效果如下
这里写图片描述
之后刷新下,会发现多了两个目录,libs和obj
然后把libs目录下的所有文件夹复制到jniLIbs文件夹下就可以了
这里写图片描述
java代码的调用代码如下
在activity中 通过静态代码块加载.so文件,之后就可以把c++的字符串通过jni调用显示到一个textview上面了
static {
System.loadLibrary(“test”);
}
tv1.setText(HelloWorld.stringFromJNI1());
Android相关部分比较简单就不过多阐述了.
希望我的demo对你有所提高.
不足之处请指教!直接在评论区提出你的问题即可,我们共同研讨

发布了28 篇原创文章 · 获赞 29 · 访问量 7万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章