-
在Eclipse環境下進行JNI環境配置非常的複雜,需要記憶的東西很多,相比較,IDE變爲AS之後整個過程都變的更簡單,AS2.2版本發佈後讓我覺得很有用的更新內容之一就是NDK的支持更加便捷,之前的NDK使用需要在Android.mk等文件中進行諸多配置,在java代碼中寫明瞭native方法之後要生成cpp文件示例依賴於在終端中編寫的代碼(這一塊我沒有做過只是瞭解可能說明有錯誤的地方,等我看了這塊回來改正)
現在在AS2.2中進行JNI開發更容易,它將原先的jni文件夾取消,取而代之的是cpp文件夾,並且進行編譯的方式也改成了cmake。
首先簡要介紹一下一個新的工程,怎麼支持AS2.2的新特性
第一步:在AS中下載cmake等工具
tools->android->sdkManager裏面勾選CMake、LLDB、NDK這三項,在安裝完成之後AS會自動的配好NDK的路徑,如果想手動設置可以在項目上app右鍵選擇open module setting裏面的sdk設定NDK路徑
第二步:新建工程,並勾選對C++的支持選項
下面全部next一路到底,在最後一步可以選擇C++規範,這裏我選擇默認不改變
然後就可以finish了,創建好項目之後的項目結構是這樣的:
(Android結構)
(project結構)
說明一下,cpp文件夾是由工程創建的時候勾選的支持C++來完成的,cmakeList.txt也是,如果是自己的老項目,需要手動的建,我認爲比較好的方法就是新建一個支持C++的新項目,然後對照着在老項目的project視圖裏建立cpp和cmakelist.txt,還可以直接把cmakelist裏的內容粘貼過來。
第三步:編寫(更改)cmakeList的內容
爲了要會寫或者說會改,首先看一下這個文件裏有哪些配置的內容
-
# Sets the minimum version of CMake required to build the native
-
# library. You should either keep the default value or only pass a
-
# value of 3.4.0 or lower.
-
-
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.
-
-
add_library( # Sets the name of the library.
-
native-lib
-
-
# 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/main/cpp/native-lib.cpp )
-
-
# Searches for a specified prebuilt library and stores the path as a
-
# variable. Because system libraries are included in the search path by
-
# default, you only need to specify the name of the public NDK library
-
# you want to add. CMake verifies that the library exists before
-
# completing its build.
-
-
find_library( # Sets the name of the path variable.
-
log-lib
-
-
# Specifies the name of the NDK library that
-
# you want CMake to locate.
-
log )
-
-
# Specifies libraries CMake should link to your target library. You
-
# can link multiple libraries, such as libraries you define in the
-
# build script, prebuilt third-party libraries, or system libraries.
-
-
target_link_libraries( # Specifies the target library.
-
native-lib
-
-
# Links the target library to the log library
-
# included in the NDK.
-
${log-lib} )
原始的代碼是這樣的,把註釋拿掉,改成自己的說法
-
cmake_minimum_required(VERSION 3.4.1)#說明要求的cmake最低版本
-
add_library( #這裏三行從下往上看分別是源文件、生成庫類型、生成庫名稱
-
native-lib
-
SHARED
-
src/main/cpp/native-lib.cpp )
-
find_library( #這裏是找到兩個庫
-
log-lib
-
log )
-
target_link_libraries( #這裏鏈接了我們上面生成的庫
-
native-lib
-
${log-lib} )
第四步:module裏的gradle編寫(改寫)
-
apply plugin: 'com.android.application'
-
-
android {
-
compileSdkVersion 25
-
buildToolsVersion "25.0.1"
-
defaultConfig {
-
applicationId "cn.ndk.jni1"
-
minSdkVersion 22
-
targetSdkVersion 25
-
versionCode 1
-
versionName "1.0"
-
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-
externalNativeBuild {
-
cmake {
-
cppFlags ""
-
}
-
}
-
}
-
buildTypes {
-
release {
-
minifyEnabled false
-
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
-
}
-
}
-
externalNativeBuild {
-
cmake {
-
path "CMakeLists.txt"
-
}
-
}
-
}
-
-
dependencies {
-
compile fileTree(dir: 'libs', include: ['*.jar'])
-
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
-
exclude group: 'com.android.support', module: 'support-annotations'
-
})
-
compile 'com.android.support:appcompat-v7:25.0.1'
-
testCompile 'junit:junit:4.12'
-
}
其中要注意到的地方我用紅框框下來了(或者截圖了下面一個忘記畫框了尷尬)
最後看一下整個調用過程,基本和沒有改變編譯方式之前是一樣的,首先是在java代碼裏面寫一個靜態塊,用來加載lib庫
-
-
static {
-
System.loadLibrary("native-lib");
-
}
然後是申明一下要調用這個庫裏的底層函數的名字同名的方法
-
-
-
-
-
public native String stringFromJNI();
最後是在java代碼裏調用這個方法就可以了,具體的函數實現交給cpp文件裏面去寫
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
-
-
TextView tv = (TextView) findViewById(R.id.sample_text);
-
tv.setText(stringFromJNI());
-
}
然後具體的這個函數會在cpp裏面實現
-
#include <jni.h>
-
#include <string>
-
-
extern "C"
-
jstring
-
Java_cn_ndk_jni1_MainActivity_stringFromJNI(
-
JNIEnv *env,
-
jobject ) {
-
std::string hello = "Hello from C++";
-
return env->NewStringUTF(hello.c_str());
-
}
這裏可以看到方法的後半段和java的方法名字是一樣的,這個函數的名字和基本內容是可以自動生成的,這一塊留到下面講,集中說一下解決的問題和可以使用的技巧。
解決的問題和學到的技巧:
1.想到的第一個問題就是如果我要自己往裏面加cpp怎麼辦,畢竟上面都是點點點沒有自己建一個cpp啊,那自己建cpp要怎麼做呢
首先建文件這些操作我建議到project視圖裏去做,因爲這個視圖纔是真正的項目文件結構,如果出現了建了文件之後沒有編寫gradle和cmakelist沒有刷新android結構的時候可以看到文件在哪裏,心裏踏實,然後可以看到在src/main/下面有cpp文件夾,我們自己建立的cpp就放在這裏面,我這裏以一個test.cpp爲例,並且給他一個頭文件,頭文件選項就在建立cpp文件的時候可以勾選(雖然我不用頭文件主要是試一下萬一以後要用),建立後是這樣的
但是這時候如果你切回去會發現在android視圖裏還是沒有這個文件夾,不截圖了,看到這裏的可以自己試一下,這就是第二個問題了
2.爲什麼我建了cpp它不見了啊?
這個問題常見於在android視圖下就建立這個cpp文件的情況,這樣會更直觀,建完了cpp文件在cpp文件夾下面,結果什麼都沒有發生(最怕空氣突然安靜),這是因爲你在cmakelist裏面還沒有加上這個源文件呢,這時候你要這樣做:
-
cmake_minimum_required(VERSION 3.4.1)#說明要求的cmake最低版本
-
add_library( #這裏三行從下往上看分別是源文件、生成庫類型、生成庫名稱
-
native-lib
-
SHARED
-
src/main/cpp/native-lib.cpp src/main/cpp/test.cpp)
-
find_library( #這裏是找到兩個庫
-
log-lib
-
log )
-
target_link_libraries( #這裏鏈接了我們上面生成的庫
-
native-lib
-
${log-lib} )
在源文件那裏添加一下這個新加進來的cpp文件,最後回到你建立好的cpp文件編輯界面,會發現它說沒有同步(sy..那個),這時候你要點擊一下讓它自己去同步
進來啦!完美,這個問題困擾我很久,是在網上找到的一個小夥伴的方法救了我,我一時找不到當時那個頁面了,等我找到了我會把原鏈接貼上來,感謝這個小天使!
特別說明一下,這是把這兩個cpp都合到一個庫裏面去了,沒有生成兩個庫,如果是生成兩個庫,我做了嘗試,就是直接把add_lib那裏複製了一遍,並且把源文件改爲新的cpp文件,把生成的庫文件名字改了一下,在link那裏把新的庫也添加上,這樣生成的項目結構是(android視圖中)會有兩個子文件夾,都是shared的庫,然後每個文件夾下面有一個cpp,分別是這兩個庫引入的cpp文件,說的羅嗦,看代碼和圖
-
add_library( # Sets the name of the library.
-
native-lib
-
-
# 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/main/cpp/native-lib.cpp )
-
add_library( # Sets the name of the library.
-
mylib
-
-
# 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/main/cpp/test.cpp )
-
target_link_libraries( # Specifies the target library.
-
native-lib
-
mylib
-
# Links the target library to the log library
-
# included in the NDK.
-
${log-lib} )
3.解決好了添加cpp問題,再來看cpp示例的自動生成問題,我現在有一個新的方法交返回值爲String的叫做JNI的方法,怎麼讓軟件自動生成一個對應的c++函數呢,這樣做:
首先在java代碼裏寫你的native方法名字,仿照原來的那個寫就好,當然這時候肯定是標紅的,畢竟你靜態調用進來的庫裏面並沒有會這麼個函數對應啊,自然就調用不到了,這時候等..別方,等到這個native方法的前面出現一個紅色的小燈泡(總覺得小燈泡有槽要吐)這時候點一下,就會自動創建一個cpp的標準啦,對應的參數也會幫我們填上去啦,很方便對吧。(親測要把原先的那個直接複製下來,然後改名字之後更容易出現這個紅色的小燈泡,比較奇怪,可能有什麼原因)
會在cpp文件裏添加這一段
-
JNIEXPORT jstring JNICALL
-
Java_cn_ndk_jni1_MainActivity_jni(JNIEnv *env, jobject instance) {
-
-
-
-
-
return env->NewStringUTF(returnValue);
-
}
最後連上之後兩邊的行號欄都會有一個雙向箭頭,點擊可以在兩邊切換。
我還一個更極端的情況,用來測試這種自動生成方式,首先是有兩個cpp,這兩個cpp分別用add_lib那裏生成兩個庫,然後在link裏面把兩個庫都鏈接上,再在java代碼裏用靜態塊把兩個庫都導入進來,這時候再寫底層函數對應的方法,用自動生成的方法,會發現會自動生成到某一個cpp文件裏去(沒找到規律),這時候只要把這一段複製粘貼到我們自己想要它在的cpp裏就可以了(也就是不知道自動會生成到哪裏去,但是可以手動調整),親測複製粘貼到任意的cpp裏都會自動鏈接上去的,雙向箭頭都在哦,當然啊這種比較複雜的情況是後面需要考慮的,如果再出現什麼問題,我會回來更正的。
最後的最後以一個比較蠢蠢的問題結束:
爲什麼提示我們說這段代碼無效呢,明明就是c++代碼,爲什麼還是灰色的,也不像自帶的那樣有編輯器的顏色呢,其實,這是寫的語句沒有意義造成的,只要改成這樣子就好了:
test.h
-
-
-
-
-
#ifndef JNI1_TEST_H
-
-
#include <iostream>
-
#define JNI1_TEST_H
-
-
#endif //JNI1_TEST_H
test.cpp
當然真正JNi的的cpp是不能這樣寫的,是像前面那樣子的,不過問題原因找到了。
Android studio 2.2+下的JNi環境搭建就是這樣,歡迎一起交流學習。