配置NDK,調用JNI最終會生成一個so庫,如果so庫生成了。直接在項目中使用so庫即可調用本地方法。注意:api的包名要與so庫定義的包名一致。
1什么是jni
?jni java native interface java本地開發接口,是JAVA和C互相調用的橋梁。
2jni有什么用
?java語言不可以直接操作硬件,但是C可以,通過JNI,JAVA也就能操作硬件。 JAVA操作硬件的API,基本上都是由C和C++去寫的,然后由JNI實現JAVA去調用。
(1)wifi熱點共享
(2)suv的車窗
(3)不重復造輪子?
(4)FFmpeg音視頻解碼 ?vitamio框架
(5)webkit 瀏覽器的內核
(6)opencv 圖像庫?
(7)7Zip
3怎么用jni
JAVA + C + JNI + NDK
學習路線:
C語言學習資源
http://www.dotcpp.com/oj/classc.html
CodeBlocks的安裝
http://www.jianshu.com/p/d5fa463df77d
1)千萬注意下載帶MinGW的版本
2)軟件安裝之后進入settings-compiler-ToolChain Excutable,H點擊AutoDetect
? 否則工程無法運行。
3)快捷鍵配置http://blog.csdn.net/wr132/article/details/48002887
4)格式化代碼:http://blog.csdn.net/qwb492859377/article/details/46933185
第1天:C語言復習
學習C不光是為了JNI,很多算法都是用C來演示的,而且JAVA的底層都是C,學習C很必要。
-
搭建開發環境
dev-c++,體積小,win7和win8需要裝不同的版本,否則編譯會出莫名的錯誤。?
-
基本數據類型、數組的內存地址
基本數據類型內存地址:?&變量名,表示取地址。
數組的內存地址:連續,數組名就是第一個元素的地址,數組就是一種類型指針。
-
指針
■概念:指針就是內存地址,內存地址就是指針。
1 2 3 4 5 6 7 8 9 10 11 12 13 | main() { ????? int ?i?=?4;?? //申請了一塊內存空間??空間的別名叫i??這塊空間里面存的數據是?4? ?????? ???????? ?????? //需求?我想把i的地址給存起來?? ????? int *?p?=??&i;? ????? printf ( "p的地址%#x\n" ,p);? ????? ?????? ?????? system ( "pause" );???????????? //執行一個windos系統的外部命令??讓dos窗體暫停退出? ????? ?} |
■*號的三種含義
1 2 3 4 5 6 | 1?*代表乘法?? 2?如果*號放到一種數據類型的后面?那么就代表這種數據類型的指針?比如? int *?? float *? ?? 當然,類型還可以是更為復雜的數據類型,如結構體,枚舉,聯合體。 ?? 類型當然也可以是指針本身,這樣就形成了多級指針。?? 3?*號?如果放到指針變量的前面???代表取出該變量里面的地址的對應的數據? ?? *指針變量?==?指針變量所指向的變量 |
■指針的類型和長度
1 2 3 | 不管什么類型的指針都是4個字節(32位操作系統),對于64位操作系統是8個字節。 C語言為了方便指針運算,?定義各種基本類型的指針,?每種類型的指針運算時所偏移量的值是根據類型的長度決定的 +1?移動的是一個單位 |
■數組與指針
?
1 | 數組名就是指針,代表數組的首地址。可以采用地址來遍歷數組 |
■字符串與指針
?
1 2 3 4 | char ?*str?=? "www.dotcpp.com" ?; ? ?? 字符指針可以采用字符串常量來初始化 ? 字符串指針指向字符串常量的首地址,*str打印出來是w,并不是整個字符串。 |
?
■多級指針
1 | 在一種數據類型的后面?有幾個*?就代表幾級指針 |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | int ?main() { ???? int ?**p;??? ???? int ?i,j;??? //p[4][8]? ???? //開始分配4行8列的二維數據??? ???? p?=? new ?int ?*[4]; ???? for (i=0;i<4;i++){ ???????? p[i]= new ?int ?[8];??? ???? } ???? for (i=0;?i<4;?i++){ ???????? for (j=0;?j<8;?j++){ ???????????? p[i][j]?=?j*i; ???????? } ???? }??? ???? //打印數據??? ???? for (i=0;?i<4;?i++){ ???????? for (j=0;?j<8;?j++)????? ???????? {??? ???????????? if (j==0)?cout<<endl;??? ???????????? cout<<p[i][j]<< "\t" ;??? ???????? } ???? }??? ???? //開始釋放申請的堆??? ???? for (i=0;?i<4;?i++){ ???????? delete ?[]?p[i];??? ???? } ???? delete ?[]?p;??? ???? return ?0; } 上面的例子中 int ?*p?:p表示指向 int 型的指針 int ?**p:?p表示指向 int ?*(指針)的指針,即指向指針的指針。 |
■函數指針
1 2 3 4 5 6 7 8 9 | ????? 和函數頭一樣,其它不變,只要將函數名改為(*函數別名)即可。如: ? 定義一個函數 ????? int ?add( int ?x?, int ?y) ????? { ???????? return ?x?+?y; ????? }????? ????? 定義上面這種函數的指針 ????? int ?(*func)?( int ?x?, int ?y)?=?add; ????? 函數指針的作用---> |
■指針的常見錯誤
?
1 2 | (1)?定義的數據類型?要和你定義的指針類型要相對應? (2)指針變量未經賦值??不可以直接使用????野指針 |
■深入理解指針
? ?&i:表示取變量i的內存地址
? ?int* p = &i:表示將變量i的內存地址用int類型指針p存儲起來
? ?*p:表示指針p所指向的內存地址對應的變量(注意最好不要理解成變量的值),也就是*p=*
? ? ?(&i)=i,就把*與&理解成可以相互抵消的意思(雖然這么理解不太準確)
? ?--------------------------------------------------------------------------------------
? ?所以對于下面的"修改i的值,會不會影響*p"和"修改*p的值,會不會影響i",答案是兩個都會,因
? ?為*p就指向了i,它們是同一塊內存地址。
? ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | main() { ?????? int ?i?=?8; ?????? ??????? int *?p?=?&i;?? //p?是用來?存地址? ?????? ??????? //(1)我修改i的值?會不會影響p的值???不會? ?????? /** ?????? printf("p的值修改前為%#x\n",p); ??????? i?=?100; ?????? printf("p的值修改后為%#x\n",p);??**/ ?????? ??????? //(2)我修改p的值??會不會影響i???????不會? ?????? /** ??????? printf("i的值修改前為%d\n",i); ??????? int?j?=?100;? ??????? p?=?&j;? ??????? printf("i的值修改后為%d\n",i);??**/ ??????? ??????? //(3)我修改i的值?會不會影響?*p??????會? ??????? /** ????????? printf("*p的值修改前為%d\n",*p); ????????? i?=?200;? ????????? printf("*p的值修改后為%d\n",*p);?**/ ????????? ???????? ??????? //(4)我修改*p?會不會影響i???????????會? ???????? printf ( "i的值修改前為%d\n" ,i); ???????? *p?=?100; ????????? ????????? printf ( "i的值修改后為%d\n" ,i); ?????? system ( "pause" );???????????? //執行一個windos系統的外部命令??讓dos窗體暫停退出? } |
■動態內存和靜態內存
?靜態內存是系統是程序編譯執行后系統自動分配,由系統自動釋放, 靜態內存是棧分配的.
? 動態內存是開發者手動分配的, 是堆分配的. ?你要手動釋放
? malloc:動態申請一塊內存空間 需要我們手動釋放 free();
? realloc:為已有的動態內存再去申請額外的動態內存
1 2 3 4 5 6 7 8 9 10 11 12 13 | /*示例*/ ???? printf ( "親,請輸入學生的數量\n" ); ???? int ?stuNumber; ???? scanf ( "%d" ,&stuNumber); ???? ????? //動態申請內存空間 ???? int *?stuArrs?=? malloc (stuNumber* sizeof ( int )); ???? //輸入每個學生的學號 ???? int ?i?; ???? for (i?=?0;?i?<?stuNumber;i++){ ???????? printf ( "請輸入第%d個學生的學號\n" ,i); ???????? scanf ( "%d" ,stuArrs?+?i); ???? } |
■結構體的兩種取值方式
?
1 2 | ? 結構體變量名.元素名稱 ? 結構體指針->元素名稱,避免了寫多個*,造成混淆。 |
■自定義數據類型
?
1 2 3 | ? 就是給數據類型定義別名,這樣對于比較長的數據類型就可以簡寫了。 ? 數據類型可以是任意的,包括指針或多級指針。 ? 如: typedef ?const ?struct ?JNINativeInterface*?JNIEnv; |
4.預處理
?■宏定義
? 》實現函數功能,比函數效率更高。
?■文件包含
??
?
C的模板代碼
1 2 3 4 5 6 | #include?<stdio.h>??????//?類似java的導包???引入標準的輸入輸出? #include?<stdlib.h>?????//引入常用函數庫? main(){????? ????? printf ( "Hello?world?!\n" );?? //相當于java的?System.out.println();?\n代表換行? ????? system ( "pause" );???????????? //執行一個windos系統的外部命令??讓dos窗體暫停退出? } |
C語言的基本數據類型
?java的八大基本類型所占字節個數
? (1)byte ? ? 1
? (2)short ? ?2
? (3)int ? ? ?4?
? (4)float ? ? 4
? (5)double ? ?8
? (6)long ? ? 8
? (7)char ? ? 2
? (8)boolean ? 1 ? ??
??
C語言的基本數據類型 ?用sizeof函數來判斷數據類型長度
?char 的數據類型長度為:1 ? ? 和java不一樣
? int 的數據類型長度為:4
? float 的數據類型長度為:4
? double 的數據類型長度為:8
? long 的數據類型長度為:4 ? ? ? ? 和java不一樣
? short 的數據類型長度為:2
? void ? 不確定是什么類型 就代表任意類型???
? signed ?unsigned 他不是數據類型 是數據類型的修飾符?
? ? ? signed 有符號 ?可以表示負數 ? -128~127
? ? ? unsigned 無符號 沒有負數 ? ? ? 0~255?
? ?
? C語言中沒有boolean C語言中用0表示假 非0表示真
??在32位操作系統下,任何類型的指針變量都占四個字節!
C語言的輸入輸出占位符??
????%d ?- ?int
????%ld - long int
????%c ?- char
????%f - ?float ? ? 默認保留小數點后6位 ? ?可以通過.幾 ?
????%u – 無符號數
????%hd – 短整型
????%lf – double
????%x – 十六進制輸出 int 或者long int 或者short int
????%o - ?八進制輸出
????%s – 字符串
C語言中如何定義字符串???
? ? ?String ? C語言中用字符數組來定義字符串?????
? ? ?C語言中不檢查角標越界 ??
? ? ?定義數組的時候要有一個結束的標志 ?\0?
??
1 2 3 | ??? char ?arr[]?=?{ 'h' , 'e' , 'l' , '\0' , 'l' , 'o' };??? ???? ???? printf ( "您輸入的數為:%s" ,arr);??? //輸出結果為hel,說明'\0'就表示結束了。 |
學習C++
?1.基礎篇章,與C的不同之處。
? 1)引用和指針的區別
? 2)輸入輸出
? 3)動態內存
? 4)命名空間(對應java的包名)
? 5)模板(相當于java的泛型)
? 6)類(多繼承,繼承類型)
? 7)預處理(預定義宏__LINE__ ?__FILE__)?
?http://www.runoob.com/cplusplus/cpp-templates.html?(接口實現,沒有看明白。)
? 8)信號處理(java中的接口回調)
? 9)多線程(找不到pthread.h這個文件)
??
第2天:JNI的實現
####################################################
AS2.2使用CMake方式進行JNI/NDK開發
http://www.jianshu.com/p/cb3064450688
1.javac 命令將native方法所在的類編譯成.class文件
2.javah 生成本地方法的頭文件
3.新建.cpp文件,include本地方法的頭文件,實現各個方法.
?》由于.cpp是jni方法,所以在最前面要加上include <jni.h>,<jni.h>其實就是用原生的C實現了java與
?C交互一套原則即JNI規范。
?》.cpp本質就是c++文件,只是實現了java native方法,按照一定的命名規則,使native方法可以
?關聯到.cpp里的方法。
?
?注意.cpp文件的除了include代碼,其它代碼都要寫在
?extern "C" {
?}模塊里.
4.配置CMakeList.txt文件
?往往在調用C的時候,除了工程自己生成的native-lib.cpp文件之外,要自己新建.cpp文件,并且有可能要調用到.c文件.這時候所有的.c和.cpp文件需要在CMakeList里配置好,否則就會報這個錯誤:
?
?undefined reference to `jlEncodeKeyDev2()' clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)
?
?比如我的工程CPP目錄如下:
?
?正確配置多個cpp的話如下:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | cmake_minimum_required(VERSION?3.4.1) #?Creates?and?names?a?library,?sets?it?as?either?STATIC #?or?SHARED,?and?provides?the?relative?paths?to?its?source?code. #?You?can?define?multiple?libraries,?and?CMake?builds?it?for?you. #?Gradle?automatically?packages?shared?libraries?with?your?APK. aux_source_directory(src/main/cpp/?SRC_LIST)?????????? //不會往下遞歸,所以要配置多個. aux_source_directory(src/main/cpp/ private ?SRC_LIST2) aux_source_directory(src/main/cpp/ public ?SRC_LIST3) add_library(?#?Sets?the?name?of?the?library. ????????????? native-lib???#導入時so庫的名稱,也是native方法被調用處 system .loadlibary ????????????? #?Sets?the?library?as?a?shared?library. ????????????? SHARED ????????????? #?Provides?a?relative?path?to?your?source?file(s). ????????????? #?Associated?headers?in?the?same?location?as?their?source ????????????? #?file?are?automatically?included. ????????????? ${SRC_LIST} ????????????? ${SRC_LIST2} ????????????? ${SRC_LIST3} ????????????? ) |
####################################################
5.實現.cpp文件里jni方法
?首先,需要弄清楚jni的幾個要點:
?1)jni數據類型:
??http://www.cnblogs.com/guanmanman/archive/2017/05/05/6811264.html
? 弄清jni有哪些類型,對應c和java分別是什么類型.
? 通過查看<jni.h>文件,可以發現最終jni的某種數據類型就對應C中的某個類型.
? 注意:一般jni的類型是無法直接被C原生代碼調用,需要通過env->相關方法轉換一下.
? ?
?2)jni的方法api:
? 所有jni方法的第一個參數:JNIEnv * env
? 通過env->方法名()即可調用jni方法.
??數組操作函數
? >>jni數組變換成c可用的數組指針?GetByteArrayElements
? ?
1 | jbyte?*olddata1?=?env->GetByteArrayElements(jbyteArray1,?0); |
? ?c語言方法參數往往會是一個指針,如果直接將jbyteArray類型轉換成指針,會發生錯誤.所以需要
jni的api將jni類型過渡成原生C可用的類型.
? >> jni數組轉數組指針
? ? jni在調用C源碼的時候,很多時候參數都是指針,如果直接傳jbyteArray就會報錯。需要通過
jni的api將數組轉換成指針
1 | jbyte?*olddata1?=?env->GetByteArrayElements(jbyteArray1,?0); |
??
?>>?獲取數組長度
??
1 | jsize?oldsize1?=?env->GetArrayLength(jbyteArray1) |
?這里的jsize即是jint類型。
?>> jni數組截取?
??SetByteArrayRegion(新數組,tart_index,end_index,原始數組指針)?
1 2 3 4 5 | //新數組,用于接收截取后的數組 jbyteArray?olddata5Resut?=?env->NewByteArray(len5); jbyte?*by5?=?(jbyte*)olddata5; env->SetByteArrayRegion(olddata5Resut,?0,?len5,?by5); |
?3)?
??
eclipse中的CDT插件怎么安裝
http://zhidao.baidu.com/link?url=NAsq91axWRclHe2OsTW5nEeVzQG5n6ZVf28h-JP8Tiw75d3YVR18XVunRtv6xsJIAx1-FZrEDNA5an3OIzL6lK
出現的問題:
1.之前寫過的一個Android項目上有JNI,后來ADT更新了,出現下面的問題,并且Android沒有NDK選項。
[原]cygwin下載、安裝教程和解決CDT出現“program 'make' is not found in path”bug
但是上面的這個博客里有一個地方說得不對,就是url連接不一定要使用中國的鏡像,中國的鏡像有可能
不好使,在列表中隨便選一個就行了。
Eclipse CDT plugin problems(StackOverFlow的解答)
eclipse+cygwin+cdt搭建c/c++開發環境
http://www.cnblogs.com/skyofbitbit/p/3705994.html
MinGW 是什么?
http://blog.csdn.net/jpcfei/article/details/6428613
MinGW如何卸載:將文件夾直接刪除即可。
最后終于找到答案了:
http://stackoverflow.com/questions/11579135/program-make-not-found-in-path
You may try altering toolchain in case if for some reason you can't use gcc. Open?Properties?for your project (by right clicking on your project name in the?Project Explorer), then?C/C++ Build?>?Tool Chain Editor. You can change the current builder there from?GNU Make Builder?to?CDT Internal Builder?or whatever compatible you have.
同時我也將C:\MinGW64\bin下的xx-make32.exe直接改成了make.exe
還修改了3個環境變量
????C_INCLUDE_PATH=C:\MinGW\include
? ? ?CPLUS_INCLUDE_PATH=C:\MinGW\include
????LIBRARY_PATH=C:\MinGW\lib
總之,最新的ADT的CDT的配置可能會出現問題,我也不知道具體是哪里出現了問題,反正按照上面的來做,就解決了問題。
?
-
NDK
本地開發包 谷歌提供 在谷歌官方文檔Develop-Tools-Download-NDK Download目錄下有下載鏈接
交叉編譯:在一種操作系統上編譯出來另外一個操作系統上可以執行二進制文件
??
? 在Eclipse里關聯NDK
? 在環境變量Path里配置ndk-build所在路徑,以便在編譯.c文件時使用這個指令。
2. JNI開發HelloWorld的復雜寫法
??
??(1)定義本地方法??native
? ? ? public native String getStringFromC();
??(2)在工程目錄下創建一個jni目錄
? ? ?(2-1)在jni目錄下新建.c文件
? ? ? ?方法名的定義 Java_包名_類名_方法名?
? ? ? ?必須接收2個參數 JNIEnv* env jobject obj
???
? ? (2-2) 在jni目錄下新建Android.mk文件
?????
? ? (2-3) 在jni目錄下新建Application.mk文件
? ? ? ? 文件內容為APP_ABI :=all。如果沒有Application.mk文件,只有Android.mk的話,編譯時只能生成arm
? ? 處理器對應的庫文件,項目只能運行在arm架構的模擬器上,加了Application.mk文件,就能生成所有類型的處理
? ? 器對應的文件。
??(3)編譯.c文件
? ? ?在cmd命令行進入項目帶盤符路徑,輸入ndk-build命令(確保Path里已經配置好了),ndk會自動尋找jni目錄
? ? ?下的文件進行編譯,生成os庫文件。刷新libs文件夾,查看有沒有成功生成os文件。
??(4)引入庫文件
? ? ? 在本地聲明的類的靜態代碼塊里
? ? ? System.loadlibary("名稱"); ? ?名稱就是Android.mk文件中LOCAL_MODULE那一項對應的名稱
??(5)運行項目
? ? ? 如果在jni下只新建了Android.mk文件,只能在arm架構的模擬器上運行,在其它的模擬器上運行會報錯。
? ? ? 如果想要在其它類型的模擬器上運行,需要Application.mk文件,且要重新編譯。
????如果忘記了.mk文件的寫法,可以參考ndk的文檔:
????
3. JNI開發HelloWorld的簡單寫法?
? (1)先給eclipse配置一些ndk環境 ?window->preference->Android->ndk-找到你自己ndk所在目錄
? (2)定義本地方法
? (3)點擊工程右鍵 添加本地支持 Android Tools-add native support,需要給庫文件取個名字。
? (4)自動生成jni目錄 自帶Android.mk文件 和一個.cpp文件 ?需要你把cpp改成.c文件 Android.mk文件里面的LOCAL_SRC_FILES也需要改一下
? (5)使用javah ?生成jni樣式頭文件
? ? ??
? (6)在C代碼中實現本地方法?
? (7)一定要記得給他一錘子 (編譯)
? (8)在聲明本地方法的類中 在靜態代碼塊中加載庫文件
Java調C
?
C調Java
#########Android Studio配置NDK##########
?http://blog.csdn.net/aplixy/article/details/51429305?(****)
?http://www.cnblogs.com/Andrew-XinFei/p/5608001.html
?http://blog.csdn.net/a_zhon/article/details/53097512
-
經常出現的一個錯誤?http://blog.csdn.net/commshare/article/details/53186025?
?2.android studio與eclipse不同,不需要手動編寫mk文件,只要在gradle里將ndk及其module配置好
?。
3.android studio .c和.h文字報紅,如果項目能run起來的話,不用管,這不是bug.
#########app應用卸載監聽##########
-
?http://www.apkbus.com/thread-250628-1-1.html
2.http://blog.csdn.net/delmoremiao/article/details/52032832?(實現思路)
? ? ? 本文轉自屠夫章哥 ?51CTO博客,原文鏈接:http://blog.51cto.com/4259297/1689141,如需轉載請自行聯系原作者