1. 程式人生 > >Android Studio ndk-Jni開發 示例

Android Studio ndk-Jni開發 示例

Java Native Interface (JNI)標準是java平臺的一部分,它允許Java程式碼和其他語言寫的程式碼進行互動。JNI 是本地程式設計介面,它使得在 Java 虛擬機器 (VM) 內部執行的 Java 程式碼能夠與用其它程式語言(如 C、C++ 和組合語言)編寫的應用程式和庫進行互動操作。
由於Android的應用層的類都是以Java寫的,這些Java類編譯為Dex型式的Bytecode之後,必須靠Dalvik虛擬機器(VM: Virtual Machine)來執行。VM在Android平臺裡,扮演很重要的角色。
(多的咱不說了,介紹什麼的 到處都有可以去搜一下)
咱們先建一個NdkJniDemo的工程


新建工程.png


新建JniUtils類實現native方法

public class JniUtils {
    public static native String getStringFormC();
}

build.png


然後clean project 再rebuild project 生成class檔案,
這時候開啟如下圖的資料夾看是否生成了classes資料夾,沒有生成請重新來過。


資料夾.png


再開啟Terminal輸入指令
cd app/build/intermediates/classes/debug
然後再輸入指令
javah -jni com.wobiancao.ndkjnidemo.ndk.JniUtils


注意 這裡javah -jni後面跟的是JniUtils類的全路徑,如果javah報不存在之類的,是你的java環境沒有配置好。


Paste_Image.png


這時候開啟classes/debug下面的檔案發現多了一個檔案
com_wobiancao_ndkjnidemo_ndk_JniUtils.h
然後在src/main下新建資料夾jni,把生成的.h檔案複製或者剪下到jni資料夾下面去,新建一個c類隨便取一個名字,新增程式碼如下

//// Created by XY on 16/1/4.//
#include "com_wobiancao_ndkjnidemo_ndk_JniUtils.h"
/* 
* Class:     Java_com_wobiancao_ndkjnidemo_ndk_JniUtils 
* Method:    getStringFormC 
* Signature: ()Ljava/lang/String; 
*/
JNIEXPORT jstring JNICALL Java_com_wobiancao_ndkjnidemo_ndk_JniUtils_getStringFormC (JNIEnv *env, jobject obj){ return (*env)->NewStringUTF(env,"這裡是來自c的string");

這裡有一點需要注意,目前windows上android studio 下ndk編譯有一個bug,就是如果你的原始檔(.C和.cpp檔案,不算.h)只有一個,那麼最後也會有編譯error,目前google還沒有修復,暫時的解決辦法是,新建一個空的.c檔案放在那裡,就可以編譯通過了。。。。有點奇葩!!

這裡發現標頭檔案#include <jni.h>報紅色,是因為咱們還沒有配置ndk環境,開啟file->project structure
選擇你所下載的ndk環境路徑,如果沒有ndk這裡有個地址大家可以去下載 一個安卓工具集合的網站:http://androiddevtools.cn/


Paste_Image.png


設定好了之後,發現標頭檔案還是紅色的,然後再build一下工程,就會有提示

Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.

按著提示做就行了 在gradle.properties檔案末尾新增android.useDeprecatedNdk=true就ok啦
然後在app檔案下得build.gradle ->defaultConfig括號內新增如下程式碼

ndk {    
   moduleName "NdkJniDemo"          //生成的so名字 
   abiFilters "armeabi", "armeabi-v7a", "x86" //輸出指定三種abi體系結構下的so庫,目前可有可無。}

到了這一步重新build專案,發現已經沒有變紅了。接下來就是運用了,在JniUtils類裡面新增如下程式碼

static {    
   System.loadLibrary("NdkJniDemo");//之前在build.gradle裡面設定的so名字,必須一致
}

然後簡單呼叫就行了,MainActivity程式碼如下

public class MainActivity extends AppCompatActivity { 
   TextView textView;    
@Override    protected void onCreate(Bundle savedInstanceState) { 
       super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.ndk_text);
        textView.setText(JniUtils.getStringFormC());
    }
}

執行結果如圖


執行圖.png


咱們開啟app->intermediates-ndk-debug發現生成了三個資料夾,並且對應了之前我們在build.gradle配置的abiFilters
大功告成。
新建libs資料夾把這三個資料夾放進去


Paste_Image.png


然後刪除咱們的jni檔案試試,是否工程還能運用?

/*********************************************************************/

補充:

在android studio中引入so的方法

1.把so檔案拷貝到工程的libs資料夾中去(如放到src\main\jniLibs中可省略第3步)

2.呼叫so的java程式碼中加入如下一段:

static {    
   System.loadLibrary("NdkJniDemo");//NdkJniDemo 是so的名字
}

3.把.SO檔案打包到APK的lib資料夾中,新版Gradle已經自動實現了.SO檔案的打包.只需要修改模組的build.gradle檔案,在配置的android節點下加入下面的內容就ok:

sourceSets {

        main {

            jniLibs.srcDirs = ['libs']

        }

    }

其他地方無需修改,整個專案的配置檔案如下:

apply plugin: 'android'

android {

    compileSdkVersion 19

    buildToolsVersion "19.0.0"

    defaultConfig {

        minSdkVersion 16

        targetSdkVersion 19

        versionCode 1

        versionName "1.0"

    }

    buildTypes {

        release {

            runProguard false

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'

        }

    }

    sourceSets {

        main {

            jniLibs.srcDirs = ['libs']

        }

    }

}

dependencies {

    compile fileTree(dir: 'libs', include: ['*.jar'])

}