初識(shí)安卓JNI開發(fā),用Cmake實(shí)現(xiàn)JNi的調(diào)用(二)

初識(shí)安卓JNI開發(fā),ndk-build+Android.mk+Application.mk實(shí)現(xiàn)JNI的調(diào)用(一)
前一篇文章講解到使用Android.mk和Aplication.mk完成JNI的C端的調(diào)用,本篇文章用另一種方式,使用Cmake構(gòu)建jni項(xiàng)目,AS 2.2之后工具中增加了對(duì)CMake的支持,官方也推薦用CMake+CMakeLists.txt的方式,代替ndk-build+Android.mk+Application.mk的方式去構(gòu)建JNI項(xiàng)目。

本篇文章基于Android Studio2.2以上版本進(jìn)行開發(fā)講解,本篇文章使用的版本為Android Studio3.1.0。

Cmake構(gòu)建項(xiàng)目實(shí)現(xiàn)JNI的調(diào)用

1 首先我們需要構(gòu)建c/c++支持的項(xiàng)目:

image.png

這里我們選擇了Native C++進(jìn)行了創(chuàng)建項(xiàng)目,代表我們的項(xiàng)目支持針對(duì)Native的調(diào)用。

image.png

同時(shí)可以看到我們的項(xiàng)目結(jié)構(gòu),相對(duì)在main目錄下增加了cpp這個(gè)目錄,目錄中包含CMakeLists.txt和native-lib.cpp文件。在這里我們 將會(huì)主要對(duì)這兩個(gè)文件進(jìn)行相關(guān)配置

2 查看native-lib.cpp文件:

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnitest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

這里的native-lib.cpp為即將被編譯生產(chǎn)動(dòng)態(tài)庫(kù)的cpp文件 ,這里實(shí)為創(chuàng)建項(xiàng)目時(shí)候?yàn)槲覀兩傻哪J(rèn)文件,根據(jù)Java_com_example_jnitest_MainActivity_stringFromJNI可以看到加載文件以及調(diào)用方法應(yīng)該在MainActivity中:

package com.example.jnitest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = 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();
}

2 查看CMakeLists.txt并進(jìn)行相關(guān)配置:

# 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).
        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) :編譯本地庫(kù)時(shí)我們需要的最小的cmake版本

add_library :相當(dāng)于Android.mk文件, native-lib 設(shè)置生成本地庫(kù)的名字;SHARED 生成庫(kù)的類型;native-lib.cpp 需要的cpp文件路徑,這里和CMakeLists.txt在同一個(gè)目錄下所以直接進(jìn)行引用。否則應(yīng)用絕對(duì)路徑 src/main/cpp/native-lib.cpp;

find_library:添加一些我們?cè)诰幾g我們的本地庫(kù)的時(shí)候需要依賴的一些庫(kù),這里是默認(rèn)用來(lái)打log的庫(kù);

target_link_libraries:關(guān)聯(lián)自己生成的庫(kù)和一些第三方庫(kù)或者系統(tǒng)庫(kù);

使用CMakeLists.txt同樣可以指定so庫(kù)的輸出路徑,但一定要在add_library之前設(shè)置,否則不會(huì)生效:

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY 
    ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}) #指定路徑
#生成的so庫(kù)在和CMakeLists.txt同級(jí)目錄下的libs文件夾下

如果想要配置so庫(kù)的目標(biāo)CPU平臺(tái),可以在build.gradle中設(shè)置:

android {
    ...
    defaultConfig {
        ...
        ndk{
            abiFilters  "arm64-v8a","armeabi-v7a"
        }
    }
    ...
  
}

設(shè)置CMakeList.txt的路徑:

android {
   ......
   externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

在default:


android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {
        // 指定一些編譯選項(xiàng)
        cppFlags "-std=c++11 -frtti -fexceptions"
        ...
 
        // 也可以使用下面這種語(yǔ)法向變量傳遞參數(shù):
        // arguments "-D變量名=參數(shù)".
        arguments "-DANDROID_ARM_NEON=TRUE",
        // 使用下面這種語(yǔ)法向變量傳遞多個(gè)參數(shù)(參數(shù)之間使用空格隔開):
        // arguments "-D變量名=參數(shù)1 參數(shù)2"
                  "-DANDROID_CPP_FEATURES=rtti exceptions"
 
        // 指定ABI
        abiFilters "armeabi-v7a" , "arm64-v8a"
      }
    }

defaultConfig 可配置CMake下編譯的一些語(yǔ)法,CMake的語(yǔ)法規(guī)則這里不進(jìn)行詳細(xì)講解。

配置完成之后,運(yùn)行項(xiàng)目cpp目錄下生成了動(dòng)態(tài)so文件:

image.png

同時(shí)項(xiàng)目運(yùn)行完成,可以看到調(diào)用動(dòng)態(tài)庫(kù)的結(jié)果輸出:

image.png

這里我們通過(guò)Android Studio自動(dòng)創(chuàng)建的demo完成了CMake方式的一些配置工作。

現(xiàn)在我們?cè)赾pp目錄下面又新建了一個(gè)goodBye.cpp的文件。

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnitest_MainActivity_sayGoodBye(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Bye Bye Little Java";
    return env->NewStringUTF(hello.c_str());
}

新增了goodBye.cpp文件,定義了sayGoodBye方法并返回"Bye Bye Little Java"字符串。我們同時(shí)應(yīng)該把它打入到動(dòng)態(tài)so庫(kù)當(dāng)中,于是我們需要修改一下CMakeLists.txt的配置:

FILE(GLOB SRC_LIST_CPP ${PROJECT_SOURCE_DIR}/*.cpp)
FILE(GLOB SRC_LIST_C ${PROJECT_SOURCE_DIR}/*.c)


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_LIST_CPP} ${SRC_LIST_C})

可以看到我們使用的兩個(gè)FILE相關(guān)的語(yǔ)句,PROJECT_SOURCE_DIR表示當(dāng)前CMakeLists.txt所在的路徑,那么這里把此路徑下的cpp文件給遍歷存入到SRC_LIST_CPP變量中,同理SRC_LIST_C 則為當(dāng)前路徑下遍歷的c文件。而add_library則通過(guò)兩個(gè)變量把所有文件加入了進(jìn)來(lái)。此時(shí)生成的so庫(kù)文件就包含了所有的cpp和c文件。

我們運(yùn)行項(xiàng)目,點(diǎn)擊textview:

image.png

此時(shí)textview顯示調(diào)用sayGoodBye方法返回的字符串。表明so庫(kù)可被正常調(diào)用所含所有文件。

假如現(xiàn)在我們又需要把goodBye.cpp單獨(dú)放入一個(gè)so庫(kù)中呢,在項(xiàng)目中我們就會(huì)經(jīng)常根據(jù)不同功能調(diào)用打不同的so文件。

接下來(lái)我們繼續(xù)要去修改CMakeList.txt文件:

add_library( # Sets the name of the library.
        hello

        SHARED

        native-lib.cpp)

target_link_libraries( # Specifies the target library.
        hello

        ${log-lib})


add_library( # Sets the name of the library.
        goodBye

        SHARED

        goodBye.cpp)


target_link_libraries( # Specifies the target library.
        goodBye

        ${log-lib})

我們把goodBye.cpp和 native-lib.cpp分開分別生成本地庫(kù),分別取名goodBye和hello。

名字既然已經(jīng)改了,我們同時(shí)需要更改加載so庫(kù)處的代碼。

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("hello");
        System.loadLibrary("goodBye");
    }
}
image.png

libs中分別生成了兩份so文件,和我們配置期待的結(jié)果一致。同時(shí)再去點(diǎn)擊textview。正常則成功顯示返回的字符串。

上面簡(jiǎn)單使用Android Studio生成的JNI項(xiàng)目,修改CMakeList.txt以及相關(guān)文件,幫助我們生產(chǎn)so文件,具體CMake使用同時(shí)還有其他許多的語(yǔ)法內(nèi)容可查找相關(guān)文檔進(jìn)行查看 。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容