Android JNI學習(二)——實戰(zhàn)JNI之“hello world”

本系列文章如下:

本地內容主要簡介如下:

  • 1免姿、環(huán)境展示
  • 2培己、傳統(tǒng)方式的具體流程
  • 3盈咳、傳統(tǒng)方式的相關問題
  • 4油够、傳統(tǒng)方式的so文件
  • 5、通過CMake工具demo演示流程
  • 6腮敌、CMake工具demo的背后原理
  • 7录平、CMake的應用
  • 8、使用experimental-plugin插件編譯
本篇文章大綱.png

一缀皱、環(huán)境展示

操作系統(tǒng)為


操作系統(tǒng).png

Android環(huán)境為:


Android環(huán)境.png

NDK環(huán)境


NDK環(huán)境.png

模擬器為


模擬器.png

二斗这、傳統(tǒng)方式的具體流程

具體流程如下:

(一) 創(chuàng)建項目

首先在Android Studio創(chuàng)建一個Android項目,包名為gebilaolitou.ndkdemo

(二) 創(chuàng)建引用本地庫的工具類

然后創(chuàng)建一個classNDKTools

代碼如下:

package gebilaolitou.ndkdemo;

public class NDKTools {

    public static native String getStringFromNDK();

}

(三) 修改相關UI顯示

MainActivity對應的xml中的textview添加id
如下:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="gebilaolitou.ndkdemo.MainActivity">

    <TextView
        android:id="@+id/tv"
        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" />
</android.support.constraint.ConstraintLayout>

然后修改MainActivity啤斗,在里面調用NDKToolsgetStringFromNDK()方法表箭。

package gebilaolitou.ndkdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String text = NDKTools.getStringFromNDK();
        Log.i("gebilaolitou","text="+text);
        ((TextView)findViewById(R.id.tv)).setText(text);
    }
}

(四) 獲取classes文件

在Android Studio中點擊Build中的Make Project或者Rebuild Project進行編譯來獲取中間文件。如下圖

編譯.png

編譯完成后钮莲,我們就可以獲取class文件如下圖
classes文件.png

(五) 進入相應目錄

點擊Android Studio下面的Terminal免钻,然后跳到NDKDemo/app/build/intermediates/classes/debug下(其中NDKDemo為是項目的根目錄),在Terminal執(zhí)行pwd確認目錄崔拥。

(六) 獲取.h文件

NDKDemo/app/build/intermediates/classes/debug下執(zhí)行下面的命令javah -jni gebilaolitou.ndkdemo.NDKTools极舔。如果沒有問題,則會在NDKDemo/app/build/intermediates/classes/debug下面生成gebilaolitou_ndkdemo_NDKTools.h文件链瓦。如下圖

頭文件.png

其內容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class gebilaolitou_ndkdemo_NDKTools */

#ifndef _Included_gebilaolitou_ndkdemo_NDKTools
#define _Included_gebilaolitou_ndkdemo_NDKTools
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     gebilaolitou_ndkdemo_NDKTools
 * Method:    getStringFromNDK
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

如下圖


頭文件內容.png

(七) 增加對應的.c文件

在工程main目錄下創(chuàng)建一個名字為jni目錄拆魏,然后將剛才的.h文件剪切過來。在jni目錄下新建一個c文件慈俯。命名為ndkdemotest.c渤刃。此時項目目錄如下:

jnipng

(八) 編寫ndkdemotest.c文件

將ndkdemotest.c協(xié)商如下內容

#include "gebilaolitou_ndkdemo_NDKTools.h"

JNIEXPORT jstring JNICALL Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
  (JNIEnv *env, jobject obj){
     return (*env)->NewStringUTF(env,"Hellow World,這是隔壁老李頭的NDK的第一行代碼");
  }
ndkdemotest.png

內容不多贴膘,就是兩部分卖子,第一部分就是 添加gebilaolitou_ndkdemo_NDKTools.h頭文件,然后就是具體實現(xiàn)Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK函數

(九) 添加并編寫Android.mk文件

同樣在jni目錄下刑峡,添加一個Android.mk文件洋闽,其目錄結構如下:


添加Android.mk文件.png

同樣在Android.mk文件里面編寫如下內容

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := ndkdemotest-jni

LOCAL_SRC_FILES := ndkdemotest.c

include $(BUILD_SHARED_LIBRARY)
Android.mk內容.png

關于Android.mk語言后面會單獨寫一篇文章進行講解玄柠,這里重點說上面代碼的內容

  • LOCAL_PATH := $(call my-dir):每個Android.mk文件必須以定義開始。它用于在開發(fā)tree中查找源文件诫舅。宏my-dir則由Build System 提供羽利。返回包含Android.mk目錄路徑。
  • include $(CLEAR_VARS)CLEAR_VARS變量由Build System提供骚勘。并指向一個指定的GNU Makefile铐伴,由它負責清理很多LOCAL_xxx撮奏。例如LOCAL_MODULE俏讹,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等等畜吊。但不是清理LOCAL_PATH泽疆。這個清理是必須的,因為所有的編譯控制文件由同一個GNU Make解析和執(zhí)行玲献,其變量是全局的殉疼。所以清理后才能便面相互影響。
  • LOCAL_MODULE := ndkdemotest-jni:LOCAL_MODULE模塊必須定義捌年,以表示Android.mk中的每一個模塊瓢娜。名字必須唯一且不包含空格。Build System 會自動添加適當的前綴和后綴礼预。例如眠砾,demo,要生成動態(tài)庫托酸,則生成libdemo.so褒颈。但請注意:如果模塊名字被定義為libabd,則生成libabc.so励堡。不再添加前綴谷丸。
  • LOCAL_SRC_FILES := ndkdemotest.c:這行代碼表示將要打包的C/C++源碼。不必列出頭文件应结,build System 會自動幫我們找出依賴文件刨疼。缺省的C++ 源碼的擴展名為.cpp。
  • include $(BUILD_SHARED_LIBRARY)BUILD_SHARED_LIBRARY是Build System提供的一個變量鹅龄,指向一個GUN Makefile Script币狠。它負責收集自從上次調用include $(CLEAR_VARS)后的所有LOCAL_xxxxinx。并決定編譯什么類型
    • BUILD_STATIC_LIBRARY:編譯為靜態(tài)庫
    • BUILD_SHARED_LIBRARY:編譯為動態(tài)庫
    • BUILD_EXECUTABLE:編譯為Native C 可執(zhí)行程序
    • BUILD_PREBUILT:該模塊已經預先編譯

PS:這里不編寫Android.mk會提示如下問題:

Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio.  Please switch to a supported build system.
  Consider using CMake or ndk-build integration. For more information, go to:
   https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
   To get started, you can use the sample ndk-build script the Android
   plugin generated for you at:
   /Users/gebilaolitou/AndroidStudioProjects/JNIDemo/app/build/intermediates/ndk/debug/Android.mk
  Alternatively, you can use the experimental plugin:
   https://developer.android.com/r/tools/experimental-plugin.html
  To continue using the deprecated NDK compile for another 60 days, set 
  android.deprecatedNdkCompileLease=1523001628930 in gradle.properties

全是英文砾层,簡單的翻譯下如下:

錯誤:執(zhí)行app:compileDebugNdk任務失敗
錯誤:不再支持android.useDeprecatedNdk標志漩绵,并且將會在未來的Android Studio版本中刪除這個標志。請切換到CMake構建系統(tǒng)或者ndk-build中集成肛炮。更多的信息請參考https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile止吐。您可以使用Android的示例ndk-build腳本在以下位置生成的插件:
/Users/gebilaolitou/AndroidStudioProjects/JNIDemo/app/build/intermediates/ndk/debug/Android.mk宝踪。另外,你也可以使用實驗性插件https://developer.android.com/r/tools/experimental-plugin.html
如果你還想繼續(xù)再使用已經被棄用的NDK編譯60天碍扔,你需要再gradle.properties中設置android.deprecatedNdkCompileLease=1523001628930

因為以上原因瘩燥,我所以我們需要設置Android.mk

(十) 修改相應的配置文件

首先檢查local.properties文件中是否有NDK路徑,如果有沒有NDK路徑不同,則添加NDK路徑厉膀,比如我的如下:

ndk.dir=/Users/debilaolitouLibrary/Android/sdk/ndk-bundle
sdk.dir=/Users/debilaolitouLibrary/Library/Android/sdk

其次修改app module目錄下的build.gradle中的內容,如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "gebilaolitou.ndkdemo"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        ndk{

            moduleName "ndkdemotest-jni"
            abiFilters "armeabi", "armeabi-v7a", "x86"

        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        externalNativeBuild {
            ndkBuild {
                path 'src/main/jni/Android.mk'
            }
        }
        sourceSets.main {
            jni.srcDirs = []
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }
}

這樣就有了so文件(此時還沒生成so文件)

(十一) 修改引用類

最后NDKTools類中添加靜態(tài)初始化代碼二拐,如下:

public class NDKTools {

    static {
        System.loadLibrary("ndkdemotest-jni");
    }

    public static native String getStringFromNDK();
}

最后run一下即可服鹅,如下圖


顯示.png

三、傳統(tǒng)方式的相關問題

有的同學在運行的時候百新,會報如下錯誤:

Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Your project contains C++ files but it is not using a supported native build system.
  Consider using CMake or ndk-build integration. For more information, go to:
   https://d.android.com/r/studio-ui/add-native-code.html
  Alternatively, you can use the experimental plugin:
   https://developer.android.com/r/tools/experimental-plugin.html

首先把檢查你項目中gradle.properties文件后面加上一句

Android.useDeprecatedNdk=true 

四企软、傳統(tǒng)方式的so文件

大家可能會有疑問,那so去哪里了饭望,我們平時使用第三方的sdk的so的時候仗哨,會要粘貼復制到項目里面,而我們上所述整個過程铅辞,并沒有出現(xiàn).so這個文件厌漂,那么這個.so去哪里了?

其實Android Studio自動幫我們把so放到apk里面斟珊,如果我們想找也能找到苇倡,如下圖:


so文件的位置.png

上面這套方式是傳統(tǒng)的Android Studio的模式,那有沒有更簡單的方式倍宾,是有的雏节,那下面我們就繼續(xù)來看下

五、通過CMake工具demo演示流程

(一) 首先確保你本地有CMake高职,我們來看下SDK Tools

SDK Tools.png

上面看到第三個 CMake 我本地沒有钩乍,所以我要進行安裝

(二) 勾選Include C++ Support復選框。

在向導的 Configure your new project 部分怔锌,選中 Include C++ Support 復選框寥粹。
如下圖


勾選.png

這里有個坑,就是有好多同學說我沒有這個Include C++ Support復選框埃元,這是因為Android Studio設計的的"bug"涝涤,你把這個對話框進行拉大,就出現(xiàn)了岛杀,因為一般的Android 項目用不到阔拳,所以在設計這個的時候,如果不特意的拉大类嗤,就選擇性的"隱藏"了,太JB坑了糊肠。

然后一直下一步辨宠,直到Customize C++ Support部分

(三) Customize C++ Support的自定義項目

如下:


模式.png

里面有個三個項目

  • C++ Standard:即C++標準,使用下拉列表選擇你希望使用的C++的標準货裹,選擇Toolchain Default 會使用默認的CMake設置嗤形。
  • Exceptions Support:如果你希望啟用對C++異常處理的支持,請選擇此復選框弧圆。如果啟動此復選框赋兵,Android Studio 會將-fexceptions標志添加到模塊級build.gradle文件的cppFlags中,Gradle會將其傳遞到CMake搔预。
  • Runtime Type Information Support:如果開發(fā)者希望支持RTTI霹期,請選中此復選框。如果啟用此復選框斯撮,Android Studio 會將-frtti標志添加到模塊級build.gradle文件的cppFlags中经伙,Gradle會將其傳遞到CMake扶叉。

最后點擊 Finish勿锅。

(四) 檢查 Android 目錄

在Android Studio 完成新項目的創(chuàng)建后,請從IDE左側打開Project 礦口并選擇Android 視圖枣氧。如下圖所示溢十,Android Studio 將添加cppExternal Build Files 組

Android模式.png

該圖為開發(fā)者的原生源文件和外部構建腳本的Android 視圖組。

PS:(此視圖無法反應磁盤上的實際文件層次結構达吞,而是將相似文件分到一組中张弛,簡化項目導航)。如果為Project模式則如下:

Project模式.png

那我們簡單介紹下這兩個多出來的文件夾:

  • cpp 文件夾中:可以找到屬于項目的所有原生源文件等構建庫酪劫。對于新項目吞鸭,Android Studio會創(chuàng)建一個示例C++源文件 native-lib.cpp,并將其置于應用模塊src/main/cpp/目錄中覆糟。這個示例代碼提供了一個簡單的C++函數stringFromJNI()刻剥,此函數可以返回字符串“Hello from C++”
  • External Build Files 文件夾中:可以找到CMake或 ndk-build 的構建腳本。與build.gradle文件指示Gradle構建應用一樣滩字,CMake和ndk-build需要一個構建腳本來了解如何構原生庫造虏。對于新項目,Android Studio 會創(chuàng)建一個CMake 構建腳本CMakeLists.txt麦箍,并將其置于模塊根目錄中漓藕。

(五) 直接運行項目

我們來直接 run一下這個項目,看下結果


結果1.png

(六) 修改native-lib.cpp

這時候我們修改下native-lib.cpp挟裂,native-lib.cpp內容如下:

native-lib.cpp內容.png

再直接run一下項目享钞,看下結果。如下:


結果2.png

我們看到對應的文字已經修改了

六诀蓉、CMake工具demo的背后原理

我們看打了栗竖,我們什么都沒做寝姿,就自動實現(xiàn)了C++的實現(xiàn),它的背后原理是什么那划滋?我們大家就思考一下饵筑?

(一)CMake的入口

它既然可以跑起來,一定有一個入口处坪,那這個入口在哪里那?

~~~~~~~~~~~~~~~~~~~~~~~~~~分隔符~~~~~~~~~~~~~~~~~~~~

先和大家說下我是怎么想象的根资,首先我們在點擊Android Studio中的run按鈕的時候,它是執(zhí)行Gradle來進行打包的同窘,所以說關于CMake的是怎么植入進去的玄帕,一定在項目的build.gradle,有相應的入口想邦。

通過上面的思想牺六,我們能舉一反三得到什么今艺?對的,就是類似于這種操作,一般都是在build.gradle里面實現(xiàn)的票灰,因為在目前Android Studio就是通過Gradle是實現(xiàn)的

那我們就來看下它的build.gradle里面的代碼刑棵,如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "gebilaolitou.cmakendkdemo"
        minSdkVersion 23
        targetSdkVersion 26
        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 {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

和我們平時搭建的項目差不多砚婆,就是多出來一塊內容意蛀,externalNativeBuild。那這里我們重點說下externalNativeBuild

(二) externalNativeBuild

我們在build.gradle里面看到夺饲,有兩個地方用到了externalNativeBuild奸汇,一個是在defaultConfig里面,是一個是在defaultConfig外面往声。

  • defaultConfig外面的externalNativeBuild里面的cmake指明了CMakeList.txt的路徑(在本項目下擂找,和是build.gradle在同一個目錄里面)。
  • defaultConfig里面的externalNativeBuild里面的cmake主要填寫的是CMake的命令參數浩销。即由arguments中的參數最后轉化成一個可執(zhí)行的CMake的命令贯涎,可以在

defaultConfig外面的 externalNativeBuild - cmake,指明了 CMakeList.txt 的路徑撼嗓;
defaultConfig 里面的 externalNativeBuild - cmake柬采,主要填寫 CMake 的命令參數。即由 arguments 中的參數最后轉化成一個可執(zhí)行的 CMake 的命令且警,可以在 app/externalNativeBuild/cmake/debug/{abi}/cmake_build_command.txt中查到粉捻。如下
路徑位置如下圖:

路徑.png

內容如下:

arguments : 
-H/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app
-B/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app/.externalNativeBuild/cmake/debug/x86
-DANDROID_ABI=x86
-DANDROID_PLATFORM=android-23
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app/build/intermediates/cmake/debug/obj/x86
-DCMAKE_BUILD_TYPE=Debug
-DANDROID_NDK=/Users/gebilaolitou/Library/Android/sdk/ndk-bundle
-DCMAKE_CXX_FLAGS=
-DCMAKE_TOOLCHAIN_FILE=/Users/gebilaolitou/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake
-DCMAKE_MAKE_PROGRAM=/Users/gebilaolitou/Library/Android/sdk/cmake/3.6.4111459/bin/ninja
-GAndroid Gradle - Ninja
jvmArgs : 

更多的可以填寫的命令參數和含義可以參見Android NDK-CMake文檔

ok上面既然提到了CMakeLists.txt,那我們就來看下CMakeLists.txt

(三) CMakeLists.txt

CMakeLists.txt這個文件主要定義了哪些文件需要編譯斑芜,以及和其他庫的關系等肩刃,那讓我們來看下我們項目中的CMakeLists.txt的內容

# 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)

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 )


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 )

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

CMakeLists.txt我們看到這里主要是分為四個部分,下面我們就依次來看下

  • cmake_minimum_required(VERSION 3.4.1):指定CMake的最小版本
  • add_library:創(chuàng)建一個靜態(tài)或者動態(tài)庫盈包,并提供其關聯(lián)的源文件路徑沸呐,開發(fā)者可以定義多個庫,CMake會自動去構建它們呢燥。Gradle可以自動將它們打包進APK中崭添。
    • 第一個參數——native-lib:是庫的名稱
    • 第二個參數——SHARED:是庫的類別,是動態(tài)的還是靜態(tài)的
    • 第三個參數——src/main/cpp/native-lib.cpp:是庫的源文件的路徑
  • find_library:找到一個預編譯的庫叛氨,并作為一個變量保存起來呼渣。由于CMake在搜索庫路徑的時候會包含系統(tǒng)庫,并且CMake會檢查它自己之前編譯的庫的名字寞埠,所以開發(fā)者需要保證開發(fā)者自行添加的庫的名字的獨特性屁置。
    • 第一個參數——log-lib:設置路徑變量的名稱
    • 第一個參數—— log:指定NDK庫的名子,這樣CMake就可以找到這個庫
  • target_link_libraries:指定CMake鏈接到目標庫仁连。開發(fā)者可以鏈接多個庫蓝角,比如開發(fā)者可以在此定義庫的構建腳本,并且預編譯第三方庫或者系統(tǒng)庫饭冬。
    • 第一個參數——native-lib:指定的目標庫
    • 第一個參數——${log-lib}:將目標庫鏈接到NDK中的日志庫使鹅,

這其實是一個最基礎的CMakeLists.txt ,其實CMakeLists.txt里面可以非常強大伍伤,比如自定義命令并徘、查找文件遣钳、頭文件包含扰魂、設置變量等等。這里推薦CMake官網文檔蕴茴,不過是英文的劝评,不好閱讀,大家可以參考中文的CMake手冊

上面分析完畢CMakeLists.txt倦淀,我們就大致的知道了CMake整體的構建流程蒋畜,那我們就來看下

(四) CMake的運轉流程

  • 1、Gradle 調用外部構建腳本CMakeLists.txt
  • 2撞叽、CMake 按照構建腳本的命令將 C++ 源文件 native-lib.cpp 編譯到共享的對象庫中姻成,并命名為 libnative-lib.so ,Gradle 隨后會將其打包到APK中
  • 3愿棋、運行時科展,應用的MainActivity 會使用System.loadLibrary()加載原生庫。應用就是可以使用庫的原生函數stringFromJNI()糠雨。

PS:這里注意一點就是:Instant Run 與使用原生的項目不兼容

如果想看Gradle是否將原生庫打包到APK中才睹,可以使用Analyze APK來檢測。

七、CMake的應用

我們在做日常需求的時候琅攘,往往會遇到一個問題垮庐,即在已有的項目中,添加C庫坞琴,這樣就不能通過上面的創(chuàng)建流程哨查,來使用CMake。那怎么辦剧辐?

其實沒關系的解恰,CMake也提供這樣的功能的,現(xiàn)在我們就回到上面的第一個demo中浙于,刪除和NDK的有關的所有代碼护盈,刪除后其目錄如下:


新目錄.png

(一) 創(chuàng)建源文件

即在main目錄下新建一個目錄,我們就叫cpp好了羞酗。然后在該目錄下創(chuàng)建一個C++ Source File(右鍵點擊您剛剛創(chuàng)建的目錄腐宋,然后選擇 New > C/C++ Source File)。我們將其命名為native-lib檀轨。

創(chuàng)建后胸竞,目錄如下:


創(chuàng)建源文件.png

(二) 創(chuàng)建CMake構建腳本

因為目前這個項目沒有CMake的構建腳本,所以咱們需要自行創(chuàng)建一個并包含適當的CMake命令参萄。CMake構建腳本是一個純文本的文件卫枝,而且這個名字必須是是CMakeLists.txt

要常創(chuàng)建一個可以用作CMake構建腳本的純文本文件,請按以下步驟操作:

  • 1讹挎、從Android Studio左側打開Project窗格并從下拉菜單中選擇Project視圖校赤。
  • 2、右鍵點擊 模塊的根目錄并選擇 New——> File筒溃。
    PS:這個位置不是不固定的马篮,位置可以隨意,但是配置構建腳本時怜奖,需要將這個位置寫入構建腳本
  • 3浑测、輸入CMakeLists.txt作為文件并點擊OK

創(chuàng)建后,目錄如下:


CMakeLists.txt.png

(三) 向CMake腳本文件寫入數據

這塊上面講解了過了歪玲,就不詳細說明了迁央,內容如下:

cmake_minimum_required(VERSION 3.4.1)

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 )

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )
              

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the log library to the target library.
                       ${log-lib} )

(四) 向Gradle 關聯(lián)到原生庫

要將Gradle關聯(lián)到原生庫,需要提供一個指向CMake或ndk-build 腳本文件的路徑滥崩。在構建應用時岖圈,Gradle會以依賴項的形式運行CMake或ndk-build,并將共享的庫打包到APK中夭委。Gradle還是用構建腳本來了解將那些文件添加到Android 項目中幅狮。
如果原生文件還沒有構建腳本募强,需要創(chuàng)建CMake構建腳本

關于 關聯(lián)到原生庫有兩種方式,一種是通過Android Studio崇摄,一種是手動擎值,其實其背后的東西是一致的,我們就一一來說明

1逐抑、通過Android Studio 實現(xiàn)
  • 1鸠儿、從IDE 左側打開Project 窗格 并選擇 Android 視圖
  • 2、右鍵點擊想要關聯(lián)到原生庫的模塊(咱們這里是app 模塊)厕氨,并從菜單中選擇 Link C++ Project with Gradle进每。如下圖
  • 3、在下拉菜單中選擇CMake命斧。使用Project Pat來為外部的CMake項目指定剛剛的``CMakeLists.txt`腳本文件
  • 4田晚、點擊OK
Link C++ Project with Gradle.png
2国葬、手動實現(xiàn)

要手動配置Gradle 以關聯(lián)到原生庫贤徒,需要將externalNativeBuild{} 塊添加到模塊級 build.gradle 文件中,并使用cmake {}對其進行配置

代碼如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "gebilaolitou.ndkdemo"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

    }

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

(五) 編寫native-lib.cpp

這塊很簡單汇四,內容如下:

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

extern "C"
JNIEXPORT jstring

JNICALL
Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK(
        JNIEnv *env, jobject /* this */) {
    std::string hello = "(*^__^*) 嘻嘻……~Hello from C++ 隔壁老李頭";
    return env->NewStringUTF(hello.c_str());
}

然后在NDKTools.java添加引用接奈,如下:

package gebilaolitou.ndkdemo;


public class NDKTools {

    static {
        System.loadLibrary("native-lib");
    }

    public static native String getStringFromNDK();
}

然后直接運行,即可通孽,結果如下:


結果3.png

八序宦、使用experimental-plugin插件簡介

我們在使用NDK開發(fā)有件比較麻煩的事情,就是編寫Android.mk和Application.mk背苦,兒Android Studio的插件gradle-experimental就是用來解決這個問題的互捌。所以使用gradle-experimental插件可以不用再編寫.mk文件情況下進行NDK開發(fā)。

gradle-experimental是Android Studio的一個實驗性的項目糠惫,是基于gradle的一個插件疫剃,主要用來自動化NDK的配置實現(xiàn),無需自己編寫Android.mk和Android.mk硼讽,對于調試NDK項目也更加友好,不過現(xiàn)在已經 不支持 牲阁,詳細請看Experimental Plugin User Guide

Note to experimental Android plugin users: The experimental plugin will no longer be supported after version 0.11.0 (released October 25, 2017). That's because the experimental plugin is designed around a Software Component Model that Gradle announced they will no longer support (read their blog post here). Gradle has backported many features from the component model, which are now available with Android plugin 3.0.0, such as variant-aware dependency resolution, and api and implementation dependency configurations. Gradle is working on backporting built-in support for compiling C/C++ code, and the Android plugin will integrate that support when it becomes available. Until then, you can either keep using experimental plugin 0.11.0 with Android Studio 3.0 or later, or migrate to Android Studio's support for using external native build tools.

簡單翻譯下如下:

對使用experimentalAndroid插件的用戶請注意:自2017年10月25日發(fā)布的0.11.0后固阁,我們將不再支持experimental插件了。因為Gradle不再支持這個依靠軟件組件模型設計experimental插件了(通過他們的博客)城菊。在Gradle Android插件的3.0.0版本备燃,現(xiàn)在已經支持組建模型中的許多功能。例如variant-aware dependency resolutionapi and implementation dependency configurations凌唬。Gradle現(xiàn)在支持編譯C/C++代碼的內置支持并齐,并且Android插件再可用時集成該支持。在此之間,您可以繼續(xù)使用Android Studio3.0或者更高版本的experimental插件况褪,或者使用Android Studio支持的外部原生構建工具撕贞。

上一篇文章Android JNI(一)——NDK與JNI基礎
下一篇文章Android JNI學習(三)——Java與Native相互調用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市测垛,隨后出現(xiàn)的幾起案子捏膨,更是在濱河造成了極大的恐慌,老刑警劉巖食侮,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件号涯,死亡現(xiàn)場離奇詭異,居然都是意外死亡锯七,警方通過查閱死者的電腦和手機链快,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來眉尸,“玉大人久又,你說我怎么就攤上這事〉叵” “怎么了畏妖?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長戒劫。 經常有香客問我半夷,道長迅细,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任茵典,我火速辦了婚禮湘换,結果婚禮上统阿,老公的妹妹穿的比我還像新娘。我一直安慰自己帆离,他們只是感情好结澄,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布岸夯。 她就那樣靜靜地躺著猜扮,像睡著了一般王悍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鲜漩,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天集惋,我揣著相機與錄音刮刑,去河邊找鬼。 笑死泛烙,一個胖子當著我的面吹牛翘紊,可吹牛的內容都是我干的。 我是一名探鬼主播鹉究,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼踪宠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了柳琢?” 一聲冷哼從身側響起染厅,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤肖粮,失蹤者是張志新(化名)和其女友劉穎尔苦,沒想到半個月后涩馆,有當地人在樹林里發(fā)現(xiàn)了一具尸體魂那,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了活逆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔗候。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖纫事,靈堂內的尸體忽然破棺而出丽惶,到底是詐尸還是另有隱情爬立,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站陵霉,受9級特大地震影響,放射性物質發(fā)生泄漏乍桂。R本人自食惡果不足惜睹酌,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一剩檀、第九天 我趴在偏房一處隱蔽的房頂上張望沪猴。 院中可真熱鬧采章,春花似錦悯舟、人聲如沸砸民。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽承璃。三九已至蚌本,卻和暖如春程癌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背进萄。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工锐峭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沿癞,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓惫搏,卻偏偏與公主長得像筐赔,于是被迫代替她去往敵國和親揖铜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容