1. 程式人生 > >ndk-build配置、Android Studio jni的配置以及jni常見問題的解決

ndk-build配置、Android Studio jni的配置以及jni常見問題的解決

           最近專案用到了jni比較頻繁,android studio 配置jni也是必須的。但不知道是不是運氣問題,我在自己電腦使用jni一點問題都沒有,可以說是無障礙。

但是,一使用公司電腦配置就出現了一大片編譯報錯,編譯不通過的問題。

          抱著不怕搞事情的態度,我要將android studio jni配置出現的問題給解決掉。哈哈,不然有問題留著會非常不爽。也可以算幫有問題的小夥伴掃除障礙,

避免掉坑。好啦,現在我們就開始搞事情......

        1. 首先我們建立一個專案,然後建立一個JniUtils.java檔案

       

        2.點選build重構一下程式碼,在app/build/intermediates下生成了一個classes資料夾

        


       

        

        


      3.接下來一步是要編譯我們剛在建立的JniUtils.java,生成c的標頭檔案。我們需要在Terminal板塊中,跳到指的資料夾目錄,做一些常規操作


      


     


    

   cd app/build/intermediates/classes/debug  //跳到指定的資料夾下

   javah -jni com.andy.testjni.JniUtils    //編譯c的標頭檔案

      編譯完成後,我們將會看到生成了一個頭檔案

     


      4.在app目錄下右鍵建立jni資料夾

      

    


   

    接下來將我們剛才生成的標頭檔案複製到我們剛建立的jni資料夾裡面,我們再建立一個JniUtils.c檔案

  

    


   


   5.建立JniUtils.c檔案後,我們需要引用標頭檔案,我們先看一下有檔案的方法名,然後我們c中引用該標頭檔案方法

    

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

#ifndef _Included_com_andy_testjni_JniUtils
#define _Included_com_andy_testjni_JniUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_andy_testjni_JniUtils
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_andy_testjni_JniUtils_getStringFromC
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif
JniUtils.c檔案
//
// Created by andy on 2017/10/28.
//
#include "com_andy_testjni_JniUtils.h"

/*
 * Class:     com_andy_testjni_JniUtils
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_andy_testjni_JniUtils_getStringFromC
  (JNIEnv * env, jclass object){
    return (*env)->NewStringUTF(env,"這裡是來自c的string");

  }

切記方法名字要保持一致,Java_com_andy_testjni_JniUtils_getStringFromC


    6.檔案都有了,接下來我們需要重新編譯一下程式碼,問題開始來了

    

   

     Error:Execution failed for task ':app:compileDebugNdk'.
     > Error: Your project contains C++ files but it is not using a supported native build system.
    Consider using CMake or ndk-build integration with the stable Android Gradle plugin:
    https://developer.android.com/studio/projects/add-native-code.html
    or use the experimental plugin:
    http://tools.android.com/tech-docs/new-build-system/gradle-experimental.

    編譯之後會出現兩個問題,一是你還沒有關聯ndk,二是你沒開啟

     A.配置ndk,你需要關聯你下載好的ndk路徑

    


   B.開啟ndk

  

  

  android.useDeprecatedNdk=true

    注意看,#是註釋


    7.接下來我們繼續配置我們的ndk模組,在build.gradle繼續配置

    

     注意moudleName很重要,名字我們自己定義,這是我們java呼叫c關鍵的一步

    

 ndk{
            moduleName "jniGetFromC"
            abiFilters "armeabi", "armeabi-v7a", "x86"
        }

     我們編譯成so檔案,我們需要將so庫打到APP中,如果不這樣做,就不能呼叫,會報錯。所以,在gradle.build裡做些工作,在app的gradle.build裡的android節點下加入如下程式碼,表示將該so庫打包到APP。

    

sourceSets.main {
        jni.srcDirs = ['libs']
    }

    沒有做此操作,將會出現引用不到路徑

   {NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=F:\workspace\TestJni\app\build\intermediates\ndk\debug\Android.mk APP_PLATFORM=android-25 NDK_OUT=F:\workspace\TestJni\app\build\intermediates\ndk\debug\obj NDK_LIBS_OUT=F:\workspace\TestJni\app\build\intermediates\ndk\debug\lib APP_ABI=all}

    8.接下來我們java檔案需要載入到jni library我們才可以呼叫,所以我們需要在JniUtils.java增載入入庫

    


   

  static {
        System.loadLibrary("jniGetFromC");
    }

 注意moudleName,這是我們在gradle檔案中一樣的,必須保持一致才能找到

     

    9.現在我們已經完成過了大部分工作,我們build一下專案,現在開始用java呼叫c

    

     當專案跑起來的時候,肯定會出現異常問題

   

     只是因為我們還沒有build-ndk,生成so庫,所以jvm找不到library,所以就找不到c中的方法。所以,這時候我們就要去配置ndk-build,手動生成so庫。


  10.配置ndk-build

     點選file--setting我們就可以看到

   


    具體可以參考我的圖進行配置

    Program:C:\Users\AppData\Local\Android\sdk\ndk-bundle\ndk-build.cmd

    Working directory: $ModuleFileDir$\src\main\

    點選ok,我們將配置完成External Tools,接下來我們測試一下是否配置成功,右鍵我們專案中的jni資料夾,然後執行ndk-build

  

   將會肯定又有一個異常問題,慢慢來,我們快成功了,很明顯,我們看錯誤日誌,缺少.mk檔案。

  

   

   11.在jni資料夾建立.mk檔案,讓ndk-build動態生成so庫

   


  


 


  注意注意moduleName哦

  我講解一下.mk檔案語法

1>   LOCAL_PATH := $(call my-dir)
每個Android.mk檔案必須以定義LOCAL_PATH為開始,它用於在開發tree中查詢原始檔,巨集my-dir 則由Build System提供,返回包含Android.mk的目錄路徑。


2>   include $(CLEAR_VARS)
CLEAR_VARS 變數由Build System提供。並指向一個指定的GNU Makefile,由它負責清理很多LOCAL_xxx.


3>   LOCAL_MODULE模組必須定義,以表示Android.mk中的每一個模組。名字必須唯一且不包含空格


4>  LOCAL_SRC_FILES變數必須包含將要打包如模組的C/C++ 原始碼,不必列出標頭檔案,build System 會自動幫我們找出依賴檔案。


application.mk檔案程式碼

APP_ABI := armeabi armeabi-v7a x86

Android.mk檔案程式碼

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
TARGET_PLATFORM := android-23
LOCAL_MODULE := jniGetFromC
LOCAL_SRC_FILES := JniUtils.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)


12.mk檔案建立好後,我們重新ndk-build一下,我們將會成功編譯好so庫,將會自動生成一個obj資料夾

 



   13.走到這一步,操作基本就完成了,因為我們也編譯生成到我們需要的so庫了,但是最後一步切記不能少,我們還需要手動建立一個jniLibs資料夾,將我們手動編譯完成的so庫複製

到jniLibs檔案下,不然還是會報找不到liabrary的異常

   

   重構專案,開啟執行我們將會成功跑起來和呼叫到c的方法

  


    小節:通過解決異常和配置,希望能夠幫助到你。