Android Studio1.4.x JNI开发基础 - 简单实例
接上一篇,搭建好基于Android Studio的環境之后,編寫native代碼相對來說也比較簡單了。在Android上編寫Native代碼和在Linux編寫C/C++代碼還是有區別,Native代碼一般需要與JVM交互數據,需要遵循一定的規范,本文來介紹一下基本的JNI代碼寫法。
我們還是從實例出發,配置好Android Studio工程之后,我們需要創建jni目錄和在jni目下創建c/c++文件和相應的頭文件,創建方式見下圖。
在實例工程中我們創建了NdkSample.cpp 和 NdkSample.h,源碼見下面:
#include "NdkSample.h"JNIEXPORT jstring JNICALL Java_com_zyp_ndktest_MainActivity_sayHello(JNIEnv *env, jclass cls, jstring j_str) {const char *c_str = nullptr;char buff[128] = {0};jboolean isCopy; c_str = env->GetStringUTFChars(j_str, &isCopy);printf("isCopy:%d\n",isCopy);if(c_str == NULL){return NULL;}printf("C_str: %s \n", c_str);sprintf(buff, "hello %s", c_str);env->ReleaseStringUTFChars(j_str, c_str);return env->NewStringUTF(buff); } #ifndef NDKTEST_NDKSAMPLE_H #define NDKTEST_NDKSAMPLE_H#include "jni.h" #include <stdio.h> #include <string.h>extern "C" { JNIEXPORT jstring JNICALLJava_com_zyp_ndktest_MainActivity_sayHello(JNIEnv *env, jclass type,jstring filename); }#endif //NDKTEST_NDKSAMPLE_H現在來簡單介紹一下,首先是NdkSample.h文件,剛剛創建的時候只有相應的預處理命令,我們在頭文件預處理命令之間加上 jni.h ,stdio.h ,string.h 后兩個非必要。將我們要在java層調用的接口聲明出來,放在extern "c"{} 中(告訴編譯器按照C標準進行編譯)。第一次接觸jni的同學看到那么復雜的函數命名和奇怪的JNIEXPORT ,JNICALL,JNIEnv之類的估計有點不習慣,本文就不詳細介紹它們的意思,其實你跟蹤源碼它們就是幾個宏(JNIEnv是一個結構體保存當前環境的上下文),其它的jstring,jclass之類的很好理解就是在Native環境中對JVM中java對應結構的一種表示方式。
函數命令方式是包名加activity名加函數名,表如我們在java層中的包名是java.com.zyp.ndktest,在MainActivity中調用sayHello函數,則jni層函數命名就要寫成上面的方式。
接下來看NdkSample.cpp文件中函數的定義。ni層的函數還需要多兩個參數,一個是JNIEnv * ,一個是jclass。我們在java層調用的時候就只用傳遞前兩個參數之外的參數。例子中我們想從java層傳遞一個String類型的參數到jni層,jni層從JVM中取數據的時候取到的卻是jstring類型,在jni層我們不能直接使用需要轉換。這里我們通過env->GetStringUTFChars(j_str, &isCopy)函數來完成,將j_str所在的地址轉換并賦值給const char *類型的指針,之后我們就可以通過該指針訪問那塊內存了。注意這里是const char * 表示該指針指向的內存區域的內容是不可以改變的,java中的String 也是自帶final屬性的。GetStringUTFChars()實際上是將JVM內部的Unicode轉化成為了C/C++認識的UTF-8的格式的字符串,注意這個函數內部發生了內存分配,相當于是拷貝了一份Unicode然后進行轉化,所以后面需要ReleaseStringUTFChars()來釋放內存。
最后該函數返回一個新的構建好的jstring類型給java層,為了將C/C++層的UTF-8字符串轉換為JVM中的Unicode字符串,需要調用另外一個函數NewStringUTF()來完成轉換。
我們注意到JVM中的內容jni中不能直接操作需要進行轉換,jni中的內同也要進行轉換,因此也有大量的相關jni接口存在,后面文章中會挑選一些來講解。
此外,函數中JNIEnv *env這個參數需要說一下,在C和C++中使用方式是不一樣的,不要搞混了。在C中,看到JNIEnv 我們實質是取得了JNINativeInterface* (JNIEnv指針的指針),我們得使用**env獲取結構體,從而才能使用結構體里面的方法。在C++中,看到JNIEnv我們實質是取得了JNIEnv*(JNIEnv結構體的指針),我們可以直接使用env->使用結構體里面的方法。注意我們調用GetStringUTFChars()的方式,但是注意和Java_com_zyp_ndktest_MainActivity_sayHello()進行區別。
現在來看看我們在java層中如何調用jni層的接口,看下面代碼:
package com.zyp.ndktest;import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);String ret = sayHello("zhuzhu");Log.i("JNI_INFO", ret);}static {System.loadLibrary("NdkSample");}public native static String sayHello(String str); }我們首先要通過System.loadLibrary()加載jni代碼編譯后生成的.so,但是這個庫的名字怎么來的呢,注意回過頭去看上一篇中gradle ndk{}中的內容,我們是在那里進行的命名的;然后還要聲明native static 類型的該函數。然后直接調用就好了。運行結果見下圖。
希望通過這篇文章能夠讓大家入門JNI開發^_^。
?
總結
以上是生活随笔為你收集整理的Android Studio1.4.x JNI开发基础 - 简单实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql常用函数参考
- 下一篇: Bootstrap 模态对话框只加载一次