閱讀388 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Android 開發 之 JNI入門 - NDK從入門到精通

NDK項目源碼地址 : 

-- 第一個JNI示例程序下載 : GitHub - https://github.com/han1202012/NDKHelloworld.git 

-- Java傳遞參數給C語言實例程序 : GitHub - https://github.com/han1202012/NDKParameterPassing.git 

--C語言回調Java方法示例程序 : GitHub - https://github.com/han1202012/NDK_Callback.git 

--分析Log框架層JNI源碼所需的Android底層文件 : CSDN - https://download.csdn.net/detail/han1202012/6905507

.

作者 :萬境絕塵 

轉載請注明出處 : https://blog.csdn.net/shulianghan/article/details/18964835

.

開發環境介紹 : 

-- eclipse : adt-bundle-windows-x86-20130917

-- sdk : 版本 2.3.3

-- ndk : android-ndk-r9c-windows-x86.zip

-- cygwin : 所需組件 binutils , gcc , gcc-mingw , gdb , make;

-- javah : jdk6.0自帶工具

-- javap : jdk6.0自帶工具


一. JNI介紹


1. JNI引入


JNI概念 : Java本地接口,Java Native Interface, 它是一個協議, 該協議用來溝通Java代碼和外部的本地C/C++代碼, 通過該協議 Java代碼可以調用外部的本地代碼, 外部的C/C++ 代碼可以調用Java代碼;


C和Java的側重 : 

-- C語言 : C語言中最重要的是 函數 function; 

-- Java語言 : Java中最重要的是 JVM, class類, 以及class中的方法;


C與Java如何交流 : 

-- JNI規範 : C語言與Java語言交流需要一個適配器, 中間件, 即 JNI, JNI提供了一種規範; 

-- C語言中調用Java方法 : 可以讓我們在C代碼中找到Java代碼class中的方法, 並且調用該方法; 

-- Java語言中調用C語言方法 : 同時也可以在Java代碼中, 將一個C語言的方法映射到Java的某個方法上; 

-- JNI橋梁作用 : JNI提供了一個橋梁, 打通了C語言和Java語言之間的障礙;


JNI中的一些概念 : 

-- native : Java語言中修飾本地方法的修飾符, 被該修飾符修飾的方法沒有方法體;

-- Native方法 : 在Java語言中被native關鍵字修飾的方法是Native方法;

-- JNI層 : Java聲明Native方法的部分;

-- JNI函數 : JNIEnv提供的函數, 這些函數在jni.h中進行定義;

-- JNI方法 : Native方法對應的JNI層實現的 C/C++方法, 即在jni目錄中實現的那些C語言代碼;


2. Android中的應用程序框架


正常情況下的Android框架 : 最頂層Android的應用程序代碼, 上層的應用層 和 應用框架層 主要是Java代碼, 中間有一層的Framework框架層代碼是 C/C++代碼, 通過Framework進行係統調用, 調用底層的庫 和linux 內核;



使用JNI時的Android框架 : 繞過Framework提供的調用底層的代碼, 直接調用自己寫的C代碼, 該代碼最終會編譯成為一個庫, 這個庫通過JNI提供的一個Stable的ABI 調用linux kernel;ABI是二進製程序接口 application binary interface.



紐帶 : JNI是連接框架層 (Framework - C/C++) 和應用框架層(Application Framework - Java)的紐帶;


JNI在Android中作用 : JNI可以調用本地代碼庫(即C/C++代碼), 並通過 Dalvik虛擬機 與應用層 和 應用框架層進行交互, Android中JNI代碼主要位於應用層 和 應用框架層;

-- 應用層 : 該層是由JNI開發, 主要使用標準JNI編程模型;

-- 應用框架層 : 使用的是Android中自定義的一套JNI編程模型, 該自定義的JNI編程模型彌補了標準JNI編程模型的不足;


Android中JNI源碼位置 : 在應用框架層中, 主要的JNI代碼位於 framework/base目錄下, 這些模塊被編譯成共享庫之後放在 /system/lib 目錄下;


NDK與JNI區別 : 

-- NDK: NDK是Google開發的一套開發和編譯工具集, 主要用於Android的JNI開發;

-- JNI : JNI是一套編程接口, 用來實現Java代碼與本地的C/C++代碼進行交互;


JNI編程步驟

-- 聲明native方法 : 在Java代碼中聲明 native method()方法;

-- 實現JNI的C/C++方法 : 在JNI層實現Java中聲明的native方法, 這裏使用javah工具生成帶方法簽名的頭文件, 該JNI層的C/C++代碼將被編譯成動態庫;

-- 加載動態庫 : 在Java代碼中的靜態代碼塊中加載JNI編譯後的動態共享庫;

.


3. JNI作用


JNI作用 : 

-- 擴展: JNI擴展了JVM能力, 驅動開發, 例如開發一個wifi驅動, 可以將手機設置為無限路由;

-- 高效 : 本地代碼效率高, 遊戲渲染, 音頻視頻處理等方麵使用JNI調用本地代碼, C語言可以靈活操作內存;

-- 複用 : 在文件壓縮算法 7zip開源代碼庫, 機器視覺 openCV開放算法庫 等方麵可以複用C平台上的代碼, 不必在開發一套完整的Java體係, 避免重複發明輪子;

-- 特殊 : 產品的核心技術一般也采用JNI開發, 不易破解;


Java語言執行流程 : 

-- 編譯字節碼 : Java編譯器編譯 .java源文件, 獲得.class 字節碼文件;

-- 裝載類庫 : 使用類裝載器裝載平台上的Java類庫, 並進行字節碼驗證;

-- Java虛擬機 : 將字節碼加入到JVM中, Java解釋器 和 即時編譯器 同時處理字節碼文件, 將處理後的結果放入運行時係統;

-- 調用JVM所在平台類庫 : JVM處理字節碼後, 轉換成相應平台的操作, 調用本平台底層類庫進行相關處理;



Java一次編譯到處執行 : JVM在不同的操作係統都有實現, Java可以一次編譯到處運行, 字節碼文件一旦編譯好了, 可以放在任何平台的虛擬機上運行;

.


二. NDK詳解


1. 交叉編譯庫文件


C代碼執行 : C代碼被編譯成庫文件之後, 才能執行, 庫文件分為動態庫 和靜態庫 兩種;

-- 動態庫 : unix環境下.so 後綴的是動態庫, windows環境下.dll 後綴的是動態庫; 動態庫可以依賴靜態庫加載一些可執行的C代碼;

-- 靜態庫 :.a 後綴是靜態庫的擴展名;


庫文件來源 : C代碼 進行 編譯 鏈接操作之後, 才會生成庫文件, 不同類型的CPU 操作係統 生成的庫文件是不一樣;

-- CPU分類 : arm結構, 嵌入式設備處理器; x86結構, pc 服務器處理器; 不同的CPU指令集不同;

-- 交叉編譯 :windows x86編譯出來的庫文件可以在arm平台運行的代碼;

-- 交叉編譯工具鏈 : Google提供的 NDK 就是交叉編譯工具鏈, 可以在linux環境下編譯出在arn平台下執行的二進製庫文件;


NDK作用 : 是Google提供了交叉編譯工具鏈, 能夠在linux平台編譯出在arm平台下執行的二進製庫文件;


NDK版本介紹 : android-ndk-windows 是在windows係統中的cygwin使用的, android-ndk-linux 是在linux下使用的;


2. 部署NDK開發環境


(1) 下載Cygwin安裝器


下載地址 : https://cygwin.com/setup-x86.exe , 這是下載器, 可以使用該下載器在線安裝, 也可以將cygwin下載到本地之後, 在進行安裝;


安裝器使用 : Cygwin的下載, 在線安裝, 卸載 等操作都有由該安裝器進行;

-- 本地文件安裝 : 選擇安裝文件所在的目錄, 然後選擇所要安裝的安裝包;

-- 在線安裝 : 選擇在線安裝即可, 然後選擇需要的安裝包;

-- 卸載 : windows上使用其它軟件例如360, 控製麵板中是無法卸載Cygwin的, 隻能通過安裝器來卸載;


(2) 安裝Cygin


雙擊安裝器 setup-x86.exe 下一步 : 



選擇安裝方式 : 

-- 在線安裝 : 直接下載, 然後安裝;

-- 下載安裝文件 : 將安裝文件下載下來, 可以隨時安裝, 注意安裝文件也需要安裝器來進行安裝;

-- 從本地文件安裝 : 即使用下載的安裝文件進行安裝;



選擇Cygwin安裝位置 : 



選擇下載好安裝文件位置 : 之前我下了一個完全版的Cygwin, 包括了所有的Cygwin組件, 全部加起來有5.23G, 下載速度很快, 使用網易的鏡像, 基本可以全速下載;



選擇需要安裝Cygwin組件 : 這裏我們隻需要以下組件 : binutils , gcc , gcc-mingw , gdb , make , 不用下全部的組件;



之後點擊下一步等待完成安裝即可;

.

安裝完之後, 打開bash命令窗口, 可以設置下顯示的字體, 使用 make -version 查看是否安裝成功 : 



(3) Cygwin目錄介紹


以下是Cygwin安裝目錄的情況 : 該安裝目錄就是所模擬的linux 的根目錄;


對應的linux目錄 : 這兩個目錄進行對比發現, 兩個目錄是一樣的, Cygwin的安裝目錄就是 linux根目錄;



cygdrive目錄 : 該目錄是Cygwin模擬出來的windows目錄結構, 進入該目錄後, 會發現windows的盤符目錄, 通過該目錄可以訪問windows中的文件;




(4) 下載NDK工具 


從Google的Android開發者官網上下載該工具, 注意NDK工具分類 : 下載地址 -https://developer.android.com/tools/sdk/ndk/index.html -;

-- windows版本NDK:android-ndk-r9c-windows-x86.zip (32位),android-ndk-r9c-windows-x86_64.zip (64位) 該版本是用在windows上的Cygwin下, 不能直接在windows上直接運行;

-- linux版本NDK :android-ndk-r9c-linux-x86.tar.bz2(32位) , android-ndk-r9c-linux-x86_64.tar.bz2 (64位) , 該版本直接在linux下執行即可;


在這裏下載windows版本的NDK, 運行在Cygwin上;



(4) NDK環境介紹


NDK工具的文件結構 : 



ndk-build腳本 : NDK build 腳本是 gun-make 的簡單封裝, gun-make 是編譯C語言代碼的工具, 該腳本執行的前提是linux環境下必須安裝 make 程序;


NDK安裝在Cygwin中 : 將NDK壓縮文件拷貝到Cygwin的根目錄中, 解壓 : android-ndk-r9c 目錄就是NDK目錄;

執行以下NDK目錄下的 ndk-build 命令 : ./ndk-build ;

執行結果 :

[plain] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <span >Android NDK: Could not find application project directory !  
  2. Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.  
  3. /android-ndk-r9c/build/core/build-local.mk:148: *** Android NDK: Aborting    。 停止。</span>  



三. 開發第一個NDK程序


1. 開發NDK程序流程


a. 創建Android工程

首選創建一個Android工程, 在這個工程中進行JNI開發;


b. 聲明native方法 : 

注意方法名使用 native 修飾, 沒有方法體 和 參數, eg : public native String helloFromJNI();


c. 創建C文件 : 

在工程根目錄下創建 jni 目錄, 然後創建一個c語言源文件, 在文件中引入 include <jni.h> , C語言方法聲明格式 jstring Java_shuliang.han.ndkhelloworld_MainActivity_helloFromJNI(JNIEnv *env) , jstring 是 Java語言中的String類型, 方法名格式為 : Java_完整包名類名_方法名();

-- JNIEnv參數 : 代表的是Java環境, 通過這個環境可以調用Java裏麵的方法;

-- jobject參數 : 調用C語言方法的對象, thiz對象表示當前的對象, 即調用JNI方法所在的類;


d. 編寫Android.mk文件 : 

如何寫 查看文檔, NDK根目錄下有一個 documentation.html 文檔, 點擊該html文件就可以查看文檔, 查看 Android.mk File 文檔, 下麵是該文檔給出的 Android.mk示例 : 


[plain] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4.   
  5. LOCAL_MODULE    := hello-jni  
  6. LOCAL_SRC_FILES := hello-jni.c  
  7.   
  8. include $(BUILD_SHARED_LIBRARY)  


-- LOCAL_PATH : 代表mk文件所在的目錄;

-- include $(CLEAR_VARS) : 編譯工具函數, 通過該函數可以進行一些初始化操作;

-- LOCAL_MODULE : 編譯後的 .so 後綴文件叫什麼名字;

-- LOCAL_SRC_FILES: 指定編譯的源文件名稱;

-- include $(BUILD_SHARED_LIBRARY) : 告訴編譯器需要生成動態庫;


e. NDK編譯生成動態庫 : 

進入 cygdrive 找到windows目錄下對應的文件, 編譯完成之後, 會自動生成so文件並放在libs目錄下, 之後就可以在Java中調用C語言方法了;


f. Java中加載動態庫 : 

在Java類中的靜態代碼塊中使用System.LoadLibrary()方法加載編譯好的 .so 動態庫;



NDK平台版本 : NDK腳本隨著 android-sdk 版本不同, 執行的腳本也是不同的, 不同平台會引用不同的頭文件, 編譯的時候一定注意 sdk 與 ndk 版本要一致;

so文件在內存中位置 : apk文件安裝到手機上之後, .so動態庫文件存在在 data/安裝目錄/libs 目錄下;


2. 開發實例


按照上麵的步驟進行開發


(1) 創建Android工程


Android工程版本 : 創建一個Android工程,minSdk 為 7 即 android-2.1, 編譯使用的sdk為 10 即 android-2.3.3 ;
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <uses-sdk  
  2.     android:minSdkVersion="7"  
  3.     android:targetSdkVersion="10" />  


NDK編譯原則 : 編譯NDK動態庫是按照最小版本進行編譯, 選擇編譯的平台的時候, 會選擇 NDK 7 平台進行編譯;

      

(2) 聲明native方法


聲明native方法, 注意該方法沒有方法體 和 參數, 如下 :

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /* 
  2.  * 聲明一個native方法 
  3.  * 這個方法在Java中是沒有實現的, 沒有方法體 
  4.  * 該方法需要使用C語言編寫 
  5.  */  
  6. public native String helloFromJNI();  

.

作者 : 萬境絕塵 

轉載請注明出處 : https://blog.csdn.net/shulianghan/article/details/18964835

.

(3) 創建C文件


引入頭文件: 首先要包含頭文件 jni.h, 該頭文件位置定義在 android-ndk-r9c\platforms\android-5\arch-arm\usr\include目錄下的 jni.h, 下麵是該頭文件中定義的一些方法, 包括本項目中使用的 NewString 方法;
  1. jstring     (*NewString)(JNIEnv*, const jchar*, jsize);  
  2. jsize       (*GetStringLength)(JNIEnv*, jstring);  
  3. const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);  
  4. void        (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);  
  5. jstring     (*NewStringUTF)(JNIEnv*, const char*);  
  6. jsize       (*GetStringUTFLength)(JNIEnv*, jstring);  



調用Java類型 : C中調用Java中的String類型為 jstring;

C語言方法名規則 : Java_完整包名類名_方法名(JNIEnv *env, jobject thiz), 注意完整的類名包名中包名的點要用 _ 代替;

參數介紹 : C語言方法中有兩個重要的參數, JNIEnv *env, jobject thiz ;
-- JNIEnv參數 : 該參數代表Java環境, 通過這個環境可以調用Java中的方法;
-- jobject參數 : 該參數代表調用jni方法的類, 在這裏就是MainActivity;

調用jni.h中的NewStringUTF方法 : 該方法的作用是在C語言中創建一個Java語言中的String類型對象, jni.h中是這樣定義的 jstring (*NewStringUTF)(JNIEnv*, const char*), JNIEnv 結構體中包含了 NewStringUTF 函數指針, 通過 JNIEnv 就可以調用這個方法;

C語言文件源碼 : 
  1. #include <jni.h>  
  2.   
  3. /* 
  4.  * 方法名稱規定 : Java_完整包名類名_方法名() 
  5.  * JNIEnv 指針 
  6.  * 
  7.  * 參數介紹 : 
  8.  * env : 代表Java環境, 通過這個環境可以調用Java中的方法 
  9.  * thiz : 代表調用JNI方法的對象, 即MainActivity對象 
  10.  */  
  11. jstring Java_shuliang_han_ndkhelloworld_MainActivity_helloFromJNI(JNIEnv *env, jobject thiz)  
  12. {  
  13.     /* 
  14.      * 調用 android-ndk-r9c\platforms\android-8\arch-arm\usr\include 中jni.h中的方法 
  15.      * jni.h 中定義的方法  jstring (*NewStringUTF)(JNIEnv*, const char*);  
  16.      */  
  17.     return (*env)->NewStringUTF(env, "hello world jni");  
  18. }  


(4) 編寫Android.mk文件



查詢NDK文檔 : NDK的文檔在NDK工具根目錄下, 點擊 documentation.html 文件, 就可以在瀏覽器中打開NDK文檔;

上麵的開發流程中詳細的介紹了Android.mk 五個參數的詳細用處, 這裏直接給出源碼 : 
[plain] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4.   
  5. LOCAL_MODULE    := hello  
  6. LOCAL_SRC_FILES := hello.c  
  7.   
  8. include $(BUILD_SHARED_LIBRARY)  



(5) 編譯NDK動態庫



進入Cygwin相應目錄 : 從Cygwin中的cygdrive 中進入windows的工程jni目錄 ;


編譯hello.c文件 : 注意Android.mk文件 與 hello.c 文件在同一目錄中;


編譯完成後的情況 : 編譯完之後 會成成一個obj文件, 在obj文件中會生成 libhello.so, 係統會自動將該 so後綴文件放在libs目錄下;



(6) Java中加載動態庫


靜態代碼塊中加載 : Java中在靜態代碼塊中加載庫文件, 調用 System.loadLibrary("hello") 方法,注意 libs中的庫文件名稱為 libhello.so,我們加載的時候 將 lib 去掉, 隻取hello 作為動態庫名稱, 這是規定的;
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. //靜態代碼塊加載C語言庫文件  
  2. static{  
  3.     System.loadLibrary("hello");  
  4. }  


(7) 其它源碼


MainActivity源碼 : 
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package shuliang.han.ndkhelloworld;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.view.View;  
  6. import android.widget.Toast;  
  7.   
  8. public class MainActivity extends Activity {  
  9.   
  10.     //靜態代碼塊加載C語言庫文件  
  11.     static{  
  12.         System.loadLibrary("hello");  
  13.     }  
  14.       
  15.     /* 
  16.      * 聲明一個native方法 
  17.      * 這個方法在Java中是沒有實現的, 沒有方法體 
  18.      * 該方法需要使用C語言編寫 
  19.      */  
  20.     public native String helloFromJNI();  
  21.       
  22.     @Override  
  23.     protected void onCreate(Bundle savedInstanceState) {  
  24.         super.onCreate(savedInstanceState);  
  25.         setContentView(R.layout.activity_main);  
  26.         System.out.println(helloFromJNI());  
  27.     }  
  28.   
  29.     public void onClick(View view) {  
  30.         //點擊按鈕顯示從jni調用得到的字符串信息  
  31.         Toast.makeText(getApplicationContext(), helloFromJNI(), 1).show();  
  32.     }  
  33.       
  34. }  

XML布局文件 : 
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="https://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:paddingBottom="@dimen/activity_vertical_margin"  
  6.     android:paddingLeft="@dimen/activity_horizontal_margin"  
  7.     android:paddingRight="@dimen/activity_horizontal_margin"  
  8.     android:paddingTop="@dimen/activity_vertical_margin"  
  9.     tools:context=".MainActivity" >  
  10.   
  11.     <Button  
  12.         android:id="@+id/bt"  
  13.         android:layout_width="wrap_content"  
  14.         android:layout_height="wrap_content"  
  15.         android:onClick="onClick"  
  16.         android:text="顯示JNI返回的字符串" />  
  17.   
  18. </RelativeLayout>  


(8) 將源碼上傳到GitHub中



在上一篇博客 https://blog.csdn.net/shulianghan/article/details/18812279 中對GitHub用法進行了詳解;

在GitHub上創建工程 : 

項目地址 
-- HTTP: https://github.com/han1202012/NDKHelloworld.git 
-- SSH : git@github.com:han1202012/NDKHelloworld.git

生成的命令 : 
[plain] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. touch README.md  
  2. git init  
  3. git add README.md  
  4. git commit -m "first commit"  
  5. git remote add origin git@github.com:han1202012/NDKHelloworld.git  
  6. git push -u origin master  

打開 Git Bash 命令行窗口 : 
-- 從GitHub上克隆項目到本地 : git clone git@github.com:han1202012/NDKHelloworld.git , 注意克隆的時候直接在倉庫根目錄即可, 不用再創建項目根目錄 ;


-- 添加文件 : git add ./* , 將目錄中所有文件添加;

-- 查看狀態 : git status ;

-- 提交緩存 : git commit -m '提交';

-- 提交到遠程GitHub倉庫 : git push -u origin master ;


GitHub項目 : 



3. 項目講解


(1) Android.mk文件講解


Android.mk文件內容 : 

[plain] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4.   
  5. LOCAL_MODULE    := hello  
  6. LOCAL_SRC_FILES := hello.c  
  7.   
  8. include $(BUILD_SHARED_LIBRARY)  

獲取當前文件內容 : $(call my-dir) 是編譯器中的宏方法, 調用該宏方法, 就會返回前的目錄路徑
賦值符號 : " := " 是賦值符號, 第一句話 是 返回當前文件所在的當前目錄, 並將這個目錄路徑賦值給 LOCAL_PATH;
初始化編譯模塊參數 : $(CLEAR_VARS) 作用是將編譯模塊的參數初始化, LOCAL_MODULE LOCAL_SRC_FILES 也是這樣的參數;
指定編譯模塊 : LOCAL_MODULE    := hello , 指定編譯後的 so 文件名稱, 編譯好之後係統會在該名稱前麵加上 "lib", 後綴加上 ".so";
指定編譯源文件 : LOCAL_SRC_FILES := hello.c 告訴編譯係統源文件, 如果有多個文件那麼就依次寫在後麵即可; 
編譯成靜態庫 : include $(BUILD_SHARED_LIBRARY), 作用是高速係統, 編譯的結果編譯成 .so 後綴的靜態庫;

靜態庫引入 : NDK的platform中有很多 ".a" 結尾的動態庫, 我們編譯動態庫的時候, 可以將一些靜態庫引入進來;


(2) 自動生成方法簽名



使用javah工具 : 在C中實現Java調用的jni方法, 方法的簽名很複雜, 需要將完整的包名類名方法名都要使用 "_" 連接起來, 很麻煩, jdk提供的生成簽名方法的工具;

遺留問題 : 目前查到的方法是 在bin目錄下 執行 javah -jni 包名類名 命令, 但是執行不成功, 暫時沒找到解決方案;
-- Android中會自動生成 .class文件嗎, 沒發現啊, PY人!


解決問題 : 在jni目錄下存在classes目錄, 但是這個目錄在eclipse中不顯示, 這裏我們要注意;


在Cygwin中使用 javah 命令即可 : 

生成的頭文件 : shuliang_han_ndkparameterpassing_DataProvider.h;
  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class shuliang_han_ndkparameterpassing_DataProvider */  
  4.   
  5. #ifndef _Included_shuliang_han_ndkparameterpassing_DataProvider  
  6. #define _Included_shuliang_han_ndkparameterpassing_DataProvider  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /* 
  11.  * Class:     shuliang_han_ndkparameterpassing_DataProvider 
  12.  * Method:    add 
  13.  * Signature: (II)I 
  14.  */  
  15. JNIEXPORT jint JNICALL Java_shuliang_han_ndkparameterpassing_DataProvider_add  
  16.   (JNIEnv *, jobject, jint, jint);  
  17.   
  18. /* 
  19.  * Class:     shuliang_han_ndkparameterpassing_DataProvider 
  20.  * Method:    sayHelloInc 
  21.  * Signature: (Ljava/lang/String;)Ljava/lang/String; 
  22.  */  
  23. JNIEXPORT jstring JNICALL Java_shuliang_han_ndkparameterpassing_DataProvider_sayHelloInc  
  24.   (JNIEnv *, jobject, jstring);  
  25.   
  26. /* 
  27.  * Class:     shuliang_han_ndkparameterpassing_DataProvider 
  28.  * Method:    intMethod 
  29.  * Signature: ([I)[I 
  30.  */  
  31. JNIEXPORT jintArray JNICALL Java_shuliang_han_ndkparameterpassing_DataProvider_intMethod  
  32.   (JNIEnv *, jobject, jintArray);  
  33.   
  34. #ifdef __cplusplus  
  35. }  
  36. #endif  
  37. #endif  

.


(3) NDK開發中亂碼問題


解決亂碼思路 : C語言編譯的時候用的是 ISO-8859-1 碼表進行編碼, 如果我們使用C語言jni開發, 需要進行轉碼操作;
-- 將ISO-8859-1轉為UTF-8字符: String string = new String(str.getBytes("iso8859-1"), "UTF-8");


示例 : 

添加中文jni調用 : 將jni中的hello.c 中返回的字符串修改為中文, 重新編譯 .so 靜態庫文件;
-- 修改後的hello.c文件如下 : 隻改變了返回的字符串, 添加了中文;
  1. #include <jni.h>  
  2.   
  3. /* 
  4.  * 方法名稱規定 : Java_完整包名類名_方法名() 
  5.  * JNIEnv 指針 
  6.  * 
  7.  * 參數介紹 : 
  8.  * env : 代表Java環境, 通過這個環境可以調用Java中的方法 
  9.  * thiz : 代表調用JNI方法的對象, 即MainActivity對象 
  10.  */  
  11. jstring Java_shuliang_han_ndkhelloworld_MainActivity_helloFromJNI(JNIEnv *env, jobject thiz)  
  12. {  
  13.     /* 
  14.      * 調用 android-ndk-r9c\platforms\android-8\arch-arm\usr\include 中jni.h中的方法 
  15.      * jni.h 中定義的方法  jstring (*NewStringUTF)(JNIEnv*, const char*); 
  16.      */  
  17.     return (*env)->NewStringUTF(env, "hello world jni 中文");  
  18. }  

使用NDK重新編譯hello.c文件 : 修改了C源碼之後, 重新將該c文件編譯成so文件;
-- 編譯過程: 打開cygwin, 進入cygdrive/ 下對應windows中源碼項目中的jni目錄, 執行 /android-ndk-r9c/ndk-build 命令;



運行Android代碼報錯 : 因為jni中c文件有中文, 中文不能被識別;
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. 01-31 14:36:04.803: W/dalvikvm(389): JNI WARNING: illegal continuation byte 0xd0  
  2. 01-31 14:最後更新:2017-04-03 12:55:22

      上一篇:go Eclipse打包工具 Fatjar clipse插件安裝方法 如何使用fat打包運行
      下一篇:go Java ???????????? ??????????????? ??? ?????????Adapter?????? (???)-??????-????????????-?????????