您的位置:首页 > 产品设计 > UI/UE

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

2016-12-29 19:07 656 查看
关于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 := (callmy−dir)include(CLEAR_VARS)

LOCAL_MODULE := nativeTest

LOCAL_SRC_FILES := nativeTest.cpp

include (BUILDSHAREDLIBRARY)LOCALMODULE指的是要生成的.so文件的名字LOCALSRCFILES指的是c或者c++的源代码.之后再创建一个Application.mk文件APPPROJECTPATH:=(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对你有所提高.

不足之处请指教!直接在评论区提出你的问题即可,我们共同研讨
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  sdk jni android studio