本文基於 Android Studio 3.4.2 、gradle:3.2.1php
JNI 是 Java Native Interface (Java 本地接口)的縮寫,是 Java 與其餘語言通訊的橋樑。在 Android 中的應用主要爲:音視頻開發、熱修復、插件化、逆向開發和系統源碼調用,爲了方便使用 JNI 技術,Android 提供了 NDK 工具集合,它和 JNI 開發本質上沒有區別,NDK 是在 Android 中實現 JNI 的手段,java
幫助開發者快速開發 C/C++ 的動態庫android
NDK 使用了交叉編譯器,能夠在一個平臺上開發出另外一個平臺的二進制代碼git
在 SDK Tools 裏下載 NDK、LLDB、CMakeshell
NDK : 即咱們須要下載的工具,會生成到 SDK 根目錄下的 ndk-bundle 目錄下api
CMake : 一個跨平臺的編譯構建工具,能夠用簡單的語句來描述全部平臺的安裝過程bash
LLDB : 一個高效的 C/C++ 的調試工具架構
這裏的界面很簡單,一個 TextView 和一個 Button ,點擊 Button 後調用 JNI 的方法修改 TextView 的值。app
<?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=".JNIDemo.JNIActivity">
<TextView android:id="@+id/jni_tv" android:layout_marginTop="20dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" android:text="I'm a TextView" android:textAllCaps="false" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<Button android:id="@+id/jni_btn" app:layout_constraintTop_toBottomOf="@id/jni_tv" android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="parent" android:text="Call Method From JNI" android:layout_width="wrap_content" android:layout_height="wrap_content" />
</android.support.constraint.ConstraintLayout>
複製代碼
/* * Copyright (c) 2019\. Lorem ipsum dolor sit amet, consectetur adipiscing elit. * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. * Vestibulum commodo. Ut rhoncus gravida arcu. */
package com.learnandroid.learn_android.JNIDemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.learnandroid.learn_android.R;
public class JNIActivity extends AppCompatActivity {
private TextView jni_tv;
private Button jni_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jni);
jni_tv = findViewById(R.id.jni_tv);
jni_btn = findViewById(R.id.jni_btn);
jni_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
jni_tv.setText(JNIUtils.sayHelloFromJNI());
}
});
}
}
複製代碼
package com.learnandroid.learn_android.JNIDemo;
public class JNIUtils {
static {
System.loadLibrary("MyJNIHello");
}
public static native String sayHelloFromJNI();
}
複製代碼
這裏的靜態代碼塊中首先加載了須要使用的動態庫,而後建立 native 方法。ide
在 Android Studio 的 Terminal 中,cd 到當前項目的根目錄,而後執行 javah 命令
(注意將含有本地方法的類寫完整的包名)
# binguner @ binguner in ~/AndroidStudioProjects/learn_android/app/src/main/java [10:12:17]
$ javah -d ../cpp com.learnandroid.learn_android.JNIDemo.JNIUtils
複製代碼
-d ../cpp 指定了頭文件的生成位置:當前目錄上一級下的 cpp 文件夾中。
而後打開 Project 目錄下的 com_learnandroid_learn_android_JNIDemo_JNIUtils.h 能夠看到它爲咱們生成的方法原型
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_learnandroid_learn_android_JNIDemo_JNIUtils */
#ifndef _Included_com_learnandroid_learn_android_JNIDemo_JNIUtils
#define _Included_com_learnandroid_learn_android_JNIDemo_JNIUtils
#ifdef __cplusplus
extern "C" {
#endif
/* * Class: com_learnandroid_learn_android_JNIDemo_JNIUtils * Method: sayHelloFromJNI * Signature: ()Ljava/lang/String; */
JNIEXPORT jstring JNICALL Java_com_learnandroid_learn_1android_JNIDemo_JNIUtils_sayHelloFromJNI (JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
複製代碼
在 cpp 目錄下新建一個 C++ 文件,命名爲 MyJNIDemo
這時候若是直接在 C++ 文件裏編寫代碼,是沒有代碼提示的,這時候須要用 CMake 工具。
首先編輯 app 的 build.gradle 文件,添加以下內容
android -> defaultConfig 下添加
// 使用Cmake工具
externalNativeBuild {
cmake {
cppFlags ""
//生成多個版本的so文件,指定須要編譯的cpu架構
abiFilters "armeabi-v7a"
}
}
複製代碼
android -> 下添加
// 配置CMakeLists.txt路徑
externalNativeBuild {
cmake {
//編譯後so文件的名字
**path "src/main/cpp/CMakeLists.txt"**
}
}
複製代碼
再編輯 CMakeLists.txt 文件(cmake腳本配置文件,cmake會根據該腳本文件中的指令去編譯相關的C/C++源文件,並將編譯後產物生成共享庫或靜態塊,而後Gradle將其打包到APK中)
# 設置 CMake 的最低版本
cmake_minimum_required(VERSION 3.4.1)
# 第一個參數:建立並命名一個 lib,會自動生成這個 lib 的 so 庫,
# 第二個參數:將它設置爲 STATIC (靜態庫,以 .a 結尾)或者 SHARED(動態庫以 .so 結尾),
# 最後一個參:數提供一個相對的源碼路徑
# 能夠用 add_library 設置多個 lib,CMake 會自動構建並把 lib 包打包到 apk 中。
add_library( # Sets the name of the library.
MyJNIHello
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
MyJNIHello.cpp
)
# 搜索一個預置的 lib,並把它的路徑保存爲一個變量
# CMake 默認在搜索路徑中包含了系統的 lib,咱們只須要給想要添加的 NDK lib 設置一個名稱便可。
# CMake 會在完成構建之間檢查它是否存在
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.
MyJNIHello
# Links the target library to the log library
# included in the NDK.
${log-lib}
)
複製代碼
這時候點擊 Sync Now,編寫 C++ 文件有代碼提示了
C++ 的代碼以下
//
// Created by Binguner on 2019-08-13.
//
#include <jni.h>
//#include "com_learnandroid_learn_android_JNIDemo_JNIUtils.h"
extern "C"
JNIEXPORT jstring JNICALL Java_com_learnandroid_learn_1android_JNIDemo_JNIUtils_sayHelloFromJNI (JNIEnv *env, jclass jclass1) {
return env->NewStringUTF("JNIHELLO");
}
複製代碼
這裏實現了 Java_com_learnandroid_learn_1android_JNIDemo_JNIUtils_sayHelloFromJNI
方法,並返回了一個 「JNIHELLO」的字符串, 我爲何註釋掉 #include "com_learnandroid_learn_android_JNIDemo_JNIUtils.h」
呢?
由於 JNI 生成的頭文件目的是幫助咱們獲得 native 方法的原型,本身寫原型容易的話命名可能出錯,你也能夠看到這個方法的名稱很是複雜。可是若是咱們能本身寫對這個名字,不用 include 這個頭文件也能夠,CMake 會咱們自動生成這個方法。
好比我又在 JNIUtils 中添加了一個 test1() 的方法
點擊代碼提示以後,它會自動在 C++ 代碼中生成相應的方法,咱們只要實現其中的方法便可
7)運行 App
點擊按鈕後
8)JNI 中打印日誌
在 C++ 文件中添加以下內容:
//
// Created by Binguner on 2019-08-13.
//
#include <android/log.h>
#include <jni.h>
//#include "com_learnandroid_learn_android_JNIDemo_JNIUtils.h"
#define LOG_TAG "System.out.c"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
extern "C"
JNIEXPORT jstring JNICALL Java_com_learnandroid_learn_1android_JNIDemo_JNIUtils_sayHelloFromJNI (JNIEnv *env, jclass jclass1) {
LOGD("TAGD,a=%d,b=%d",1,2);
return env->NewStringUTF("JNIHELLO");
}
複製代碼
歡迎關注本文做者:
掃碼關注並回復「乾貨」,獲取我整理的千G Android、iOS、JavaWeb、大數據、人工智能等學習資源。