国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android Studio中通過CMake使用NDK并編譯自定義庫和添加預(yù)編譯庫

2019-10-22 18:17:04
字體:
供稿:網(wǎng)友

Note:這篇文章是基于Android Studio 3.01版本的,NDK是R16。

step1:創(chuàng)建一個包含C++的項(xiàng)目

android,studio,NDK,編譯自定義庫,CMake

android,studio,NDK,編譯自定義庫,CMake

其他默認(rèn)就可以了。

C++ Standard

指定編譯庫的環(huán)境,其中Toolchain Default使用的是默認(rèn)的CMake環(huán)境;C++ 11也就是C++環(huán)境。兩種環(huán)境都可以編庫,至于區(qū)別,后續(xù)會跟進(jìn),當(dāng)前博文使用的是CMake環(huán)境。

Exceptions Support

如果選中復(fù)選框,則表示當(dāng)前項(xiàng)目支持C++異常處理,如果支持,在項(xiàng)目Module級別的build.gradle文件中會增加一個標(biāo)識 -fexceptions到cppFlags屬性中,并且在so庫構(gòu)建時,gradle會把該屬性值傳遞給CMake進(jìn)行構(gòu)建。

Runtime Type Information Support

同理,選中復(fù)選框,項(xiàng)目支持RTTI,屬性cppFlags增加標(biāo)識-frtti

切換到project 模式,生成的目錄的結(jié)構(gòu)如下:

android,studio,NDK,編譯自定義庫,CMake

android,studio,NDK,編譯自定義庫,CMake

3、認(rèn)識CMakeLists.txt構(gòu)建腳本文件

CMakeLists.txt文件用于配置JNI項(xiàng)目屬性,主要用于聲明CMake使用版本、so庫名稱、C/CPP文件路徑等信息,下面是該文件內(nèi)容:

# 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庫信息(為當(dāng)前當(dāng)前腳本文件添加庫)

native-lib

這個是聲明引用so庫的名稱,在項(xiàng)目中,如果需要使用這個so文件,引用的名稱就是這個。值得注意的是,實(shí)際上生成的so文件名稱是libnative-lib。當(dāng)Run項(xiàng)目或者build項(xiàng)目是,在Module級別的build文件下的intermediates/transforms/mergeJniLibs/debug/folders/2000/1f/main下會生成相應(yīng)的so庫文件。

SHARED

這個參數(shù)表示共享so庫文件,也就是在Run項(xiàng)目或者build項(xiàng)目時會在目錄intermediates/transforms/mergeJniLibs/debug/folders/2000/1f/main下生成so庫文。此外,so庫文件都會在打包到.apk里面,可以通過選擇菜單欄的Build->Analyze Apk...*查看apk中是否存在so庫文件,一般它會存放在lib目錄下。

src/main/cpp/native-lib.cpp

構(gòu)建so庫的源文件。

STATIC:靜態(tài)庫,是目標(biāo)文件的歸檔文件,在鏈接其它目標(biāo)的時候使用。

SHARED:動態(tài)庫,會被動態(tài)鏈接,在運(yùn)行時被加載。

MODULE:模塊庫,是不會被鏈接到其它目標(biāo)中的插件,但是可能會在運(yùn)行時使用dlopen-系列的函數(shù)動態(tài)鏈接。

頭文件

也可以配置頭文件路徑,方法是(注意這里指定的是目錄而非文件):

include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])

下面的配置實(shí)際上與自定義的JNI項(xiàng)目(自定義的so庫)沒有太大關(guān)系。

find_library()

這個方法與我們要創(chuàng)建的so庫無關(guān)而是使用NDK的Apis或者庫,默認(rèn)情況下Android平臺集成了很多NDK庫文件,所以這些文件是沒有必要打包到apk里面去的。直接聲明想要使用的庫名稱即可(猜測:貌似是在Sytem/libs目錄下)。在這里不需要指定庫的路徑,因?yàn)檫@個路徑已經(jīng)是CMake路徑搜索的一部分。如示例中使用的是log相關(guān)的so庫。

log-lib

這個指定的是在NDK庫中每個類型的庫會存放一個特定的位置,而log庫存放在log-lib中

log

指定使用log庫

target_link_libraries()

如果你本地的庫(native-lib)想要調(diào)用log庫的方法,那么就需要配置這個屬性,意思是把NDK庫關(guān)聯(lián)到本地庫。

native-lib

要被關(guān)聯(lián)的庫名稱

${log-lib}

要關(guān)聯(lián)的庫名稱,要用大括號包裹,前面還要有$符號去引用。

實(shí)際上,我們可以自己創(chuàng)建CMakeLists.txt文件,而且路徑不受限制,只要在build.gradle中配置externalNativeBuild.cmake.path來指定該文件路徑即可。

add_subdirectory 可以執(zhí)行子路徑的CMakeLists.txt

添加自定義的C++庫mathlib

創(chuàng)建源文件

我的項(xiàng)目名稱為OpenCVTest,所以右鍵這個項(xiàng)目點(diǎn)擊New->Module,然后選Android Library,輸入庫的名稱MathLib,然后Finish,系統(tǒng)就會生成對應(yīng)的模塊,并構(gòu)建好初始的目錄樹。系統(tǒng)將庫命名為MathLib,但是目錄樹中還是小寫的mathlib。這個時候系統(tǒng)會自動在頂級settings.gradle添加對于這個新模塊的include語句。并且在模塊目錄下構(gòu)建好了初始的build.gradle。

現(xiàn)在我們開始創(chuàng)建自己的C++庫,首先右鍵mathlib目錄下的src/main,然后選擇New->Directory,輸入cpp并確定。這個目錄就是我們要創(chuàng)建的庫的源文件的位置。

右鍵add,點(diǎn)擊New->C/C++ Source File,輸入add.cpp,并選中Create an associated header。
在.cpp文件中定義好一個簡單的加法函數(shù),并在.h文件中添加好對應(yīng)聲明。

add.cpp

#include "add.h"int add(int a,int b) { return a + b;}add.h#ifndef OPENCVTEST_ADD_H#define OPENCVTEST_ADD_Hint add(int a,int b);#endif //OPENCVTEST_ADD_H

將源文件關(guān)聯(lián)到構(gòu)建系統(tǒng)中

我們用CMake來構(gòu)建C++庫,然后CMake又要和gradle結(jié)合,在Android Studio里面協(xié)作管理C++和Java的代碼。

我們在模塊mathlib的根目錄下創(chuàng)建一個名為CMakeLists.txt的文件,寫入

cmake_minimum_required(VERSION 3.4.1)add_library(add SHARED   src/main/cpp/add.cpp)set(distribution_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../distribution)set_target_properties(add PROPERTIES      LIBRARY_OUTPUT_DIRECTORY      ${distribution_DIR}/libs/${ANDROID_ABI})target_include_directories(add       PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp)       add_custom_command(TARGET add POST_BUILD     COMMAND "${CMAKE_COMMAND}" -E     copy "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/add.h"     "${distribution_DIR}/include/mathlib/add.h"#     **** the following 2 lines are for potential future debug purpose ****#     COMMAND "${CMAKE_COMMAND}" -E#     remove_directory "${CMAKE_CURRENT_BINARY_DIR}"     COMMENT "Copying gmath to output directory")

set可以自定義變量。這里定義生成so文件的目錄

set_target_properties 命令的意思是設(shè)置目標(biāo)的一些屬性來改變它們構(gòu)建的方式。這個命令中設(shè)置了 add的ARCHIVE_OUTPUT_DIRECTORY 屬性。也就是改變了輸出路徑。

add_custom_command 命令是自定義命令。命令中把頭文件也復(fù)制到了 distribution_DIR 中。

target_include_directories,它對創(chuàng)建的庫設(shè)置include路徑,針對目標(biāo)來設(shè)置,可以避免與其他庫的沖突,并且此時對自定義的庫設(shè)置好了此路徑后,后續(xù)導(dǎo)入這個庫就不需要再次設(shè)置了。但對于預(yù)構(gòu)建的庫,就需要設(shè)置,稍后會有詳細(xì)講解。

接下來我們在模塊mathlib的build.gradle中的defaultConfig{}中添加如下語句:

externalNativeBuild {   cmake {    arguments '-DANDROID_PLATFORM=android-19',      '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'    targets 'add'   }  }

這里arguments是編譯參數(shù),而targets則是相比于add_subdirectory更高權(quán)限的方法。一般來說可以把它刪去,即默認(rèn)構(gòu)建所有目標(biāo)。

然后在android{}最后添加如下語句,將CMakeLists.txt關(guān)聯(lián)起來。

externalNativeBuild {  cmake {   path 'CMakeLists.txt'  } }

C++庫已經(jīng)創(chuàng)建好了,接下來就要在主模塊中使用它了。

為了使用自定義C++庫,我們需要一個中間人,它從Android本身的Java程序中獲取請求,然后使用我們的C++庫中的函數(shù)計(jì)算得到結(jié)果,并將數(shù)據(jù)傳回Android本身的Java程序中。

創(chuàng)建一個中間文件native-math.cpp

#include <jni.h>#include <string>#include "mathlib/add.h"extern "C"JNIEXPORT jstringJNICALLJava_com_example_bill_opencvtest_MainActivity_stringFromJNI(  JNIEnv *env,  jobject /* this */) { std::string hello = "Hello from C++ From Android openCVTest"; return env->NewStringUTF(hello.c_str());}extern "C"JNIEXPORT jint JNICALLJava_com_example_bill_opencvtest_MainActivity_addFromCpp(JNIEnv *env, jobject instance, jint a,               jint b) { // TODO return add(a,b);}

在app/CMakeLists.txt 加上這個自定義庫的引用

set(distribution_DIR ${CMAKE_SOURCE_DIR}/../distribution)include_directories(${distribution_DIR}/include)add_library(lib_add SHARED IMPORTED)set_target_properties(lib_add PROPERTIES IMPORTED_LOCATION      ${distribution_DIR}/libs/${ANDROID_ABI}/libadd.so)add_library( # Sets the name of the library.    native-math    # Sets the library as a shared library.    SHARED    # Provides a relative path to your source file(s).    src/main/cpp/native-math.cpp )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 )set_target_properties(native-math PROPERTIES      LIBRARY_OUTPUT_DIRECTORY      ${distribution_DIR}/libs/${ANDROID_ABI})target_link_libraries( # Specifies the target library.      native-math      android      log      lib_add      # Links the target library to the log library      # included in the NDK.      ${log-lib} )

在模塊app的局部build.gradle中,像之前一樣添加好對應(yīng)的語句:

defaultConfig{}中:

externalNativeBuild {   cmake {    arguments '-DANDROID_PLATFORM=android-19',      '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'   }  }  ndk {   //abiFilters 'armeabi-v7a','x86_64'  }

其中ndk指定abi平臺

ABI(Application binary interface)應(yīng)用程序二進(jìn)制接口。不同的CPU 與指令集的每種組合都有定義的 ABI (應(yīng)用程序二進(jìn)制接口),一段程序只有遵循這個接口規(guī)范才能在該 CPU 上運(yùn)行,所以同樣的程序代碼為了兼容多個不同的CPU,需要為不同的 ABI 構(gòu)建不同的庫文件。當(dāng)然對于CPU來說,不同的架構(gòu)并不意味著一定互不兼容。

armeabi設(shè)備只兼容armeabi;
armeabi-v7a設(shè)備兼容armeabi-v7a、armeabi;
arm64-v8a設(shè)備兼容arm64-v8a、armeabi-v7a、armeabi;
X86設(shè)備兼容X86、armeabi;
X86_64設(shè)備兼容X86_64、X86、armeabi;
mips64設(shè)備兼容mips64、mips;
mips只兼容mips;

接著在src/main/java/*/MainActivity.java中的MainActivity類下面,加載庫,以及設(shè)置好對應(yīng)的方法聲明:

static {  System.loadLibrary("native-math"); } /**  * A native method that is implemented by the 'native-lib' native library,  * which is packaged with this application.  */ public native String stringFromJNI(); public native int addFromCpp(int a, int b);

然后就可以在onCreate方法中使用這個C++庫定義的函數(shù),在Java中對應(yīng)的函數(shù)了

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() + "____" + addFromCpp(2,88));

最后別忘了在項(xiàng)目中添加模塊的依賴關(guān)系才可以正常運(yùn)行這個Android App。右鍵項(xiàng)目OpenCVTest,選擇Open Module Settings。選擇app->Dependencies,添加Module dependency,選擇mathlib,確定即可

添加OpenCV庫的支持

導(dǎo)入OpenCV進(jìn)項(xiàng)目

從OpenCV的官網(wǎng)將OpenCV4Android 3.4下載下來,解壓到某個目錄。

點(diǎn)擊Android Studio的File->New->Import Module,然后選擇路徑為OpenCV-android-sdk/sdk/java,確定。并在導(dǎo)入之后,修改build.gradle中的SDK版本。

在Open Module Settings中添加模塊的依賴關(guān)系,使app依賴openCVLibrary340。

現(xiàn)在已經(jīng)可以在.java文件中看得到OpenCV的自動補(bǔ)全了。

配置OpenCV的C++預(yù)構(gòu)建庫

把包含文件夾OpenCV-android-sdk/sdk/native/jni/include和預(yù)構(gòu)建庫文件夾OpenCV-android-sdk/sdk/native/libs也復(fù)制到項(xiàng)目的distribution中。

由于之前已經(jīng)在添加C++庫時修改了app的build.gradle,所以這個步驟現(xiàn)在不需要再執(zhí)行了。

由于OpenCV是預(yù)構(gòu)建庫,所以沒有編譯的過程,因此模塊openCVLibrary320中不需要添加CMakeLists.txt等。我們直接在app模塊中根目錄下的CMakeLists.txt導(dǎo)入OpenCV的庫即可。

set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")include_directories(${distribution_DIR}/include)# set add libadd_library(libopencv_java3 SHARED IMPORTED )set_target_properties(libopencv_java3 PROPERTIES IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so")add_library( # Sets the name of the library.    native-opencv    # Sets the library as a shared library.    SHARED    # Provides a relative path to your source file(s).    src/main/cpp/native-opencv.cpp )set_target_properties(native-opencv PROPERTIES      LIBRARY_OUTPUT_DIRECTORY      ${distribution_DIR}/libs/${ANDROID_ABI})target_link_libraries( # Specifies the target library.      native-opencv      android      log      libopencv_java3      # Links the target library to the log library      # included in the NDK.      ${log-lib} )

需要注意的是.so使用SHARED,.a使用STATIC。

注意:預(yù)構(gòu)建庫:so文件和.a文件必須copy在src/main/jniLibs這個目錄,才可以自動被打包。其他路徑都不可以,連source這個命令也不起作用

現(xiàn)在可以使用openCV庫了,新建一個文件native-opencv.cpp

//// Created by bill on 2018/1/13.//#include <jni.h>#include <opencv2/opencv.hpp>#include <vector>using namespace cv;using namespace std;extern "C"JNIEXPORT void JNICALLJava_com_example_bill_opencvtest_MainActivity_nativeProcessFrame(JNIEnv *env, jobject instance,  jlong addrGray, jlong addrRGBA) {// TODO Mat& gray = *(Mat *) addrGray; Mat& rgba = *(Mat *) addrRGBA; vector<KeyPoint> v; Ptr<ORB> orb = ORB::create(); orb->detect(gray, v, cv::Mat()); for (int i = 0; i < v.size(); ++i) {  const KeyPoint& kp = v[i];  circle(rgba, Point(kp.pt.x, kp.pt.y), 10, Scalar(255,0,0,255)); }}

現(xiàn)在就可以在src/main/java/*/MainActivity.java中按照同樣的方法,載入庫,寫上方法聲明。最后,如下所示。

static {  System.loadLibrary("native-opencv");  System.loadLibrary("native-math"); } /**  * A native method that is implemented by the 'native-lib' native library,  * which is packaged with this application.  */ public native String stringFromJNI(); public native int addFromCpp(int a, int b); private native void nativeProcessFrame(long addrGray, long addrRGBA);

完整的MainActivity

package com.example.bill.opencvtest;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.WindowManager;import android.widget.TextView;import org.opencv.android.CameraBridgeViewBase;import org.opencv.android.OpenCVLoader;import org.opencv.core.CvType;import org.opencv.core.Mat;public class MainActivity extends Activity implements CameraBridgeViewBase.CvCameraViewListener2{ static {  System.loadLibrary("native-opencv");  System.loadLibrary("native-math"); } /**  * A native method that is implemented by the 'native-lib' native library,  * which is packaged with this application.  */ public native String stringFromJNI(); public native int addFromCpp(int a, int b); private native void nativeProcessFrame(long addrGray, long addrRGBA); private static final String TAG = "MainActivity"; private Mat rgba; private Mat gray; private CameraBridgeViewBase mOpenCvCameraView; @Override 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() + "____" + addFromCpp(2,88));  getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.activity_camera_view);  mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);  mOpenCvCameraView.setCvCameraViewListener(this); } @Override public void onPause() {  super.onPause();  if (mOpenCvCameraView != null){   mOpenCvCameraView.disableView();  } } @Override public void onResume() {  super.onResume();  if (!OpenCVLoader.initDebug()) {   Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");  } else {   Log.d(TAG, "OpenCV library found inside package. Using it!");   mOpenCvCameraView.enableView();  } } public void onDestroy() {  super.onDestroy();  if (mOpenCvCameraView != null){   mOpenCvCameraView.disableView();  } } public void onCameraViewStarted(int width, int height){  rgba = new Mat(height, width, CvType.CV_8UC4);  gray = new Mat(height, width, CvType.CV_8UC1); } public void onCameraViewStopped() {  rgba.release();  gray.release(); } public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame){  rgba = inputFrame.rgba();  gray = inputFrame.gray();  nativeProcessFrame(gray.getNativeObjAddr(), rgba.getNativeObjAddr());  return rgba; }}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:opencv="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.bill.opencvtest.MainActivity"> <TextView  android:id="@+id/sample_text"  android:layout_width="wrap_content"  android:layout_height="wrap_content"  android:text="Hello World!"  app:layout_constraintBottom_toBottomOf="parent"  app:layout_constraintLeft_toLeftOf="parent"  app:layout_constraintRight_toRightOf="parent"  app:layout_constraintTop_toTopOf="parent" /> <org.opencv.android.JavaCameraView  android:layout_width="match_parent"  android:layout_height="match_parent"  android:id="@+id/activity_camera_view"  opencv:show_fps="true"  opencv:camera_id="any"/></android.support.constraint.ConstraintLayout>

為了愉快的使用OpenCV Library,可以直接在AndroidManifest.xml里面加入如下權(quán)限

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.bill.opencvtest"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA"/> <uses-feature android:name="android.hardware.camera"/> <uses-feature android:name="android.hardware.camera.autofocus"/> <uses-feature android:name="android.hardware.camera.front"/> <uses-feature android:name="android.hardware.camera.front.autofocus"/> <application  android:allowBackup="true"  android:icon="@mipmap/ic_launcher"  android:label="@string/app_name"  android:roundIcon="@mipmap/ic_launcher_round"  android:supportsRtl="true"  android:theme="@style/AppTheme">  <activity android:name=".MainActivity"   android:screenOrientation="landscape"   android:configChanges="keyboardHidden|orientation">   <intent-filter>    <action android:name="android.intent.action.MAIN" />    <category android:name="android.intent.category.LAUNCHER" />   </intent-filter>  </activity> </application></manifest>

總結(jié)

以上所述是小編給大家介紹的Android Studio中通過CMake使用NDK并編譯自定義庫和添加預(yù)編譯庫,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對VEVB武林網(wǎng)網(wǎng)站的支持!


注:相關(guān)教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 顺平县| 山阴县| 区。| 巩留县| 多伦县| 黑水县| 济宁市| 分宜县| 邹平县| 宜昌市| 武冈市| 潼关县| 元江| 西乡县| 五河县| 来安县| 甘德县| 湘潭市| 大姚县| 大悟县| 江城| 青神县| 海淀区| 锦州市| 蓝山县| 古蔺县| 阿瓦提县| 临江市| 嘉善县| 河北省| 纳雍县| 灵山县| 新郑市| 深泽县| 鹿泉市| 登封市| 来安县| 绥中县| 清河县| 玛沁县| 伊宁县|