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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章