Android Studio Cmake & OpenCV3.2環境
前言:
Cmake是跨平臺的一個編譯配置工具,android studio 2.2之後就開始支援這個cmake工具了,之前Android studio開發c++都是利用NDK的方式,也就是Android.mk + Application.mk + soucre.cpp的構建方式,然後在gradle構建工具下執行一條命令生成c++ so庫並打包進去。而cmake則是 CMakeLists.txt + source.cpp 的形式。
對比起來,我更喜歡用CMakeLists.txt的形式,結構看起來比較清晰,比較適合從linux下轉到android上的人來使用。
官方的說明:
● Android 原生開發工具包 (NDK):這套工具集允許您為 Android 使用 C 和 C++ 程式碼,並提供眾多平臺庫,讓您可以管理原生 Activity 和訪問物理裝置元件,例如感測器和觸控輸入。
● CMake:一款外部構建工具,可與 Gradle 搭配使用來構建原生庫。如果您只計劃使用 ndk-build,則不需要此元件。
準備資源:
下載安裝CMake工具
開啟SDK Manager :
複選CMake和NDK 然後下載。
簡單的CMake工程
新建一個Android Studio工程,記得勾選 Include C++ Supports。這樣子新建完的工程,目錄結構是這樣的:
紅色方框標出來的就是跟普通android工程不一樣的地方,多了CMake工程一些需要的。
這三個東西都是NDK部分:
1. .externalNativeBuild資料夾:cmake編譯好的檔案, 顯示支援的各種硬體等資訊。系統生成。
2. cpp資料夾:存放C/C++程式碼檔案,native-lib.cpp檔案是該Demo中自帶的,可更改。需要自己編寫。
3. CMakeLists.txt檔案:CMake指令碼配置的檔案。需要自己配置編寫。
編譯的過程:
開啟和CMkaeLists.txt的同級目錄下的gradle檔案:
關注其中兩部分,一個是defaultconfig 模組中的externalNativeBuild,這裡面主要是配置cmake一些編譯屬性,標誌等,具體語法android官方有說明。下面那個模組主要是指定編譯的CMakeLists.txt的檔案。
CMakeLists.txt檔案用於配置JNI專案屬性,主要用於宣告CMake使用版本、so庫名稱、C/CPP檔案路徑等資訊,下面是該檔案內容:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
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 them 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).
src/main/cpp/native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries 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 this
# 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最小版本使用的是3.4.1。
*add_library*()
配置so庫資訊(為當前當前指令碼檔案新增庫)
native-lib
這個是宣告引用so庫的名稱,在專案中,如果需要使用這個so檔案,引用的名稱就是這個。值得注意的是,實際上生成的so檔名稱是libnative-lib。當Run專案或者build專案是,在Module級別的build檔案下的intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下會生成相應的so庫檔案。
SHARED
這個引數表示共享so庫檔案,也就是在Run專案或者build專案時會在目錄intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下生成so庫文。此外,so庫檔案都會在打包到.apk裡面,可以通過選擇選單欄的*Build->Analyze Apk...**檢視apk中是否存在so庫檔案,一般它會存放在lib目錄下。
src/main/cpp/native-lib.cpp
構建so庫的原始檔。
STATIC:靜態庫,是目標檔案的歸檔檔案,在連結其它目標的時候使用。
SHARED:動態庫,會被動態連結,在執行時被載入。
MODULE:模組庫,是不會被連結到其它目標中的外掛,但是可能會在執行時使用dlopen-系列的函式動態連結。
標頭檔案
也可以配置標頭檔案路徑,方法是(注意這裡指定的是目錄而非檔案):
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 …])
下面的配置實際上與自定義的JNI專案(自定義的so庫)沒有太大關係。
find_library()
這個方法與我們要建立的so庫無關而是使用NDK的Apis或者庫,預設情況下Android平臺集成了很多NDK庫檔案,所以這些檔案是沒有必要打包到apk裡面去的。直接宣告想要使用的庫名稱即可(猜測:貌似是在Sytem/libs目錄下)。在這裡不需要指定庫的路徑,因為這個路徑已經是CMake路徑搜尋的一部分。如示例中使用的是log相關的so庫。log-lib
這個指定的是在NDK庫中每個型別的庫會存放一個特定的位置,而log庫存放在log-lib中
log
指定使用log庫
target_link_libraries()
如果你本地的庫(native-lib)想要呼叫log庫的方法,那麼就需要配置這個屬性,意思是把NDK庫關聯到本地庫。
native-lib
要被關聯的庫名稱
${log-lib}
要關聯的庫名稱,要用大括號包裹,前面還要有$符號去引用。
更具體的資訊可以去android官網查詢。
程式碼裡面也自動載入庫:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
點選執行,就會出現Hello from C++的字串了。
構建OpenCV cmake環境
下載下來的opencv 包目錄如下:
- apk
- samples
- sdk
這邊搭建的android opencv的 jni環境,所以只需要sdk/native/jni/include 標頭檔案和sdk/native/libs 對應平臺的so檔案即可。其他的一些cmake裡面原生的配置檔案是opencv自帶的為了和ndk結合,需要在Android.mk 去執行OpenCV.mk 檔案,動態生成opencv的庫,這樣的好處是可以修改opencv原始碼,比較複雜。
把include檔案拷貝到cpp目錄下,在main目錄下建立一個jniLibs/ 目錄,把libs下的對應平臺的資料夾拷貝進去。
再次配置gradle檔案:
- 加入abi 過濾:
ndk {
abiFilters 'x86'
}
這個模組加在defaultconfig 模組中 externalNativeBuild的下面,這樣子編譯的時候就只會去找你過濾的平臺了,我這邊實在電腦上執行虛擬機器,所以是 ‘x86’,可以根據實際情況改,比如手機裝置可以改成 armeabi-v7a 。
- 修改CMakeLists.txt檔案
set(CMAKE_VERBOSE_MAKEFILE on)
set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
#設定標頭檔案路徑
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
#新增庫
add_library(libopencv_java3 SHARED IMPORTED )
set_target_properties(libopencv_java3 PROPERTIES
IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so")
#......
#......
#新增連結的opencv庫和 android一些常用的庫比如ljnigraphics,bitmap需要用到
target_link_libraries( # Specifies the target library.
native-lib android log -ljnigraphics libopencv_java3
# Links the target library to the log library
# included in the NDK.
${log-lib} )
這麼一來opencv的cmake環境就配置好了,開啟native-lib.cpp
#include <jni.h>
#include <string>
#include <opencv/cv.h>
#include <opencv2/opencv.hpp>
using namespace cv;
extern "C"
JNIEXPORT jstring JNICALL
Java_com_opencv_wmy_opencvtest_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
Mat m = imread("bike.jpg");
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
opencv的標頭檔案加在正確,在此環境下函式都有程式碼提示,點選執行,成功執行,在介面會顯示一個 Hello from C++,表示opencv環境已經好了。
opencv主要是用來影象計算和處理的,放在C++層效率會更快一點,後面會陸續推出幾篇部落格,如何在java層和jni傳影象資料,敬請期待!