1. 程式人生 > >Android Studio 3.0 JNI開發入門

Android Studio 3.0 JNI開發入門

本入門教程使用的作業系統為ubuntu18.04

開發環境準備

在AS中新建一個專案,開啟專案的File–>Settings–>Android SDK–>SDK Tool,下載安裝CMake、LLDB、NDK。在這裡插入圖片描述

建立一個支援C/C++的Android專案

開啟專案的File–>New–>New Object… 在這裡插入圖片描述

建立過程一直Next下去,直到最後一步

在這裡插入圖片描述

專案建立成功後,專案自動建立了src/main/cpp目錄和一個native-lib.cpp原始檔,並在app/目錄裡生成CMakeLists.txt檔案: 在這裡插入圖片描述 並且在Module:app的gradle裡配置好了相關資訊,如下圖所示:

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.wong.testjni"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"  //配置C++標準
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    // 配置CMakeLists.txt路徑
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt" // 設定所要編寫的c原始碼位置,以及編譯後so檔案的名字
        }
    }
}
dependencies {
  ......
}

CMakeLists.txt檔案的內容如下:

#設定CMake的最低版本構建本機所需庫
cmake_minimum_required(VERSION 3.4.1)

# 建立並命名庫,將其設定為靜態的
# 或共享,並提供其原始碼的相對路徑。
# 你可以定義多個library庫,並使用CMake來構建。
# Gradle會自動將包共享庫關聯到你的apk程式。

add_library( #設定so檔名稱
             native-lib
             # 設定這個so檔案為共享
             SHARED
             # 設定 c檔案原始碼位置
             src/main/cpp/native-lib.cpp )

# 搜尋指定預先構建的庫和儲存路徑變數。因為CMake包括系統庫搜尋路徑中預設情況下,只需要指定想新增公共NDK庫的名稱,在CMake驗證庫之前存在完成構建

find_library( # 設定path變數的名稱
              log-lib
              # Specifies the name of the NDK library that
              # 在CMake定位前指定的NDK庫名稱
              log )
              
# 指定庫CMake應該連結到目標庫中,可以連結多個庫,比如定義庫,構建指令碼,預先構建的第三方庫或者系統庫
target_link_libraries( # 制定目標庫
                       native-lib
                       # 目標庫到日誌庫的連結 包含在NDK
                       # included in the NDK.
                       ${log-lib} )

建立一個方法測試一下NDK環境

  • 在MainActivity.java中增加一個native方法
public native String sayHelloJNI();

你會發現sayHelloJNI( )方法是紅色的。但是,按Alt+Enter快捷鍵後,系統會自動在之前.cpp檔案中建立一個sayHelloJNI( )的C++程式碼,如下所示:

extern "C"
JNIEXPORT jstring JNICALL
Java_com_wong_testjni_MainActivity_sayHelloJNI(JNIEnv *env, jobject instance) {
    // TODO
    return env->NewStringUTF("Hello android ,welcome to JNI world...");
}

可以看到,sayHelloJNI()方法在C++程式碼的方法名是所在的類的完整包名加上方法名組成的。

  • 使用native方法,要先將so庫引入,引入的方法,只須在要使用native方法的類里加入如下程式碼:
    static {
        System.loadLibrary("native-lib");
    }

注:native-lib是在CMakeLists.txt檔案裡指定的。

  • 在MainActivity.java中呼叫sayHelloJNI()方法:
public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(sayHelloJNI());
    }
    private native String sayHelloJNI();
}

至此,如果tv可以顯示出"Hello android ,welcome to JNI world…",則說明NDK環境配置成功了。

其實在AS 3.0進行NDK開發已經很方便的了。只需要建立支援C/C++開發的專案就可以完成NDK 的環境搭建了。

定義一個包含native方法的類

package com.wong.testjni;
public class TestJNI {
    public static native int init(Object obj);
    public static native int deInit(int index);
}
  • 在AS的Terminal中通過命令列的指令生成.h檔案: 在這裡插入圖片描述

  • 生成的.h檔案在app/src/main/java下,將.h檔案移到app/src/main/cpp目錄下。

  • 在app/src/main/cpp中新建一個.cpp的C/C++原始檔,該檔案中的方法名對應生成的.h檔案中宣告的 方法名,java層通過呼叫該檔案中的方法達到呼叫C/C++中方法的目的,如.cpp原始檔是TestJNI.cpp,如下所示:

#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jint JNICALL
Java_com_wong_testjni_TestJNI_init(JNIEnv *env, jclass type, jobject cb) {
    return 10;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_wong_testjni_TestJNI_deInit(JNIEnv *env, jclass type, jint index) {
    return index;
}
  • 在CMakeLists.txt檔案裡設定 TestJNI.cpp檔案原始碼位置
cmake_minimum_required(VERSION 3.4.1)
add_library( #  # 設定so檔名稱.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/TestJNI.cpp  #設定 TestJNI.cpp檔案原始碼位置
             src/main/cpp/native-lib.cpp )

find_library( # Sets the name of the path variable.
              log-lib
              log )
target_link_libraries( # Specifies the target library.
                       native-lib
                       ${log-lib} )
  • 在MainActivity呼叫
public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(TestJNI.deInit(18)+"");
    }
}

謝謝觀賞!