Android Studio第一個JNI開發入門
概述
該篇主要描述如何通過AS進行開發自己的JNI so庫文件,通過一個簡單的測試用例引導初學者瞭解其中的過程。
NDK環境的安裝
開發so庫就必須用到NDK(本地開發環境包),在這裏只需知道它是專門用來編譯C/C++的工程文件的。其他更細節的內容可以自行查閱瞭解。
那麼如何進行安裝呢? 大致可以分爲兩種:一種手動下載安裝,另一種通過IDE自行下載安裝;
- 手動下載安裝
這種方法首先,從官網下載最新的NDK下載鏈接
其次,解壓到一個指定的安裝目錄,這個目錄不能有中文或空格,否則會有問題
最後,配置path環境變量 - 這一步可以省略,後續用到的時候可以在增加
這樣就完成了,其實就是下載,解壓就完了 非常簡單明瞭。
完成之後我們需要複製一下安裝的目錄全路徑如:C:\Users\User\AppData\Local\Android\Sdk\ndk\21.0.6113669
這是我解壓後的路徑,這個後面會用到。 - IDE下載安裝
IDE的安裝我們這裏只介紹AS的方法。
首先打開AS->Tools->SDK Manager->Android SDK->SDK Tools選中NDK/LLDB -> apply 就自行開始下載
如此AS就自行下載並安裝。 注意在這裏大家最好把Cmake也選擇上,後面也會用到。因爲在後面配置build.grandle的時候AS3.0以下的版本已經不能再使用android.useDeprecatedNdk不再支持了這樣的屬性進行配置so庫了,必須採用Cmake。
AS配置關聯NDK
NDK安裝完成之後,就需要讓AS知道怎麼使用NDK。其實很簡單只需要讓AS關聯一下NDK的路徑就好。
還記得前面保存下來的路徑嗎,將它們複製粘貼到下面的輸入框即可。
首先, 我們需要先創建一個測試工程ndkdemo。
然後,打開File->Project Structure
找到SDK Location:這樣將前面複製的ndk路徑粘貼到NDK Location即可。如果是通過IDE自行安裝的,不知道安裝到哪個目錄時,我們可以通過上面SDK location的路徑來找到它,通常都是在sdk的目錄下。
還有一種方法是直接找到local.properties這個文件,增加ndk.dir宏即可。
# local.properties
sdk.dir=C\:\\Users\\User\\AppData\\Local\\Android\\Sdk
ndk.dir=C\:\\Users\\User\\AppData\\Local\\Android\\Sdk\\ndk\\21.0.6113669
這兩種方式作用都是一樣的, 第一種方式修改完後最終也是作用到這個文件。
NDK開發JNI流程
在JAVA裏面寫native代碼
- 首先在新建工程的包下面創建一個JNI.java文件,用它來實現native聲明。
- 編寫native 聲明
// JNI.java
package com.example.sven.ndkdemo;
/**
* Creator: @By Sven 2020-02-26
* 作用: java調用對應的C代碼
**/
public class JNI {
{
// 在這裏我們需要加載這個jni so庫, 這個Hello名字就是最終編譯產出的so的名字,也可以起其他的名字,但必須要和最終的so庫名相同。
System.loadLibrary("Hello");
}
/* *
* 定義native方法
* 調用C代碼對應的方法
* @return
*/
public native String sayHello(); // 在這裏我們聲明瞭一個sayHello的本地接口。
}
寫C/C++代碼實現本地接口
在前面聲明瞭一個sayHello本地接口,那麼在這裏就需要進行實現它。
- 首先獲取函數名。
JNI本地接口函數名的命名規則爲Java_全類名_函數名。即本例sayHello來說:
Java_com_example_sven_ndkdemo_sayHello();
不過我們有個簡單方便的操作可以將鼠標放到sayHello接口上,然後右鍵->copy reference這樣就獲取到了:
com.example.sven.ndkdemo.JNI#sayHello這樣的一個名稱,在這裏我們只需要稍微改變將所有的 '.‘換成’’, #也換成 ''即可。 這樣對特別長或者難記的名稱就方便多了呢?
- 創建C文件
在main目錄下創建jni目錄,然後創建Hello.c文件, 目錄結構如下:
- 編輯C文件實現本地代碼
前面已經簡單獲取了函數名com.example.sven.ndkdemo.JNI#sayHello,在這裏我們只需要將所有的 '.‘換成’’, #也換成 ''即可。
//
// Created by User on 2020/2/26.
//
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
/*
* jstring: 返回值
* Java_全類名_方法名
* JNIEnv: 裏面有很多方法
* jobject: 誰調用了這個方法就是誰的事例
*/
jstring Java_com_example_sven_ndkdemo_JNI_sayHello(JNIEnv *env, jobject jobj) {
char *text = "I am from c";
return (*env)->NewStringUTF(env, text);
}
配置動態鏈接庫名稱
在這裏我們需要指定編譯產出的so庫名,如前面指定的Hello。
配置通常有兩種方式,一種通過ndk屬性進行配置, 另一種通過cmake配置
- ndk配置
這種方式好像AS3.0以上就不支持了,即使網上有很多說需要增加android.useDeprecatedNdk=true屬性進行兼容, 但是在AS3.5我使用的這個版本是不支持的,報以下錯誤。
Flag android.useDeprecatedNdk is no longer supported and will be removed in the next……
即使這樣在這裏也列出使用方式:
我們需要編輯build.grandle文件進行配置ndk。
# build.grandle
defaultConfig {
applicationId "com.example.sven.ndkdemo"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
# 指定庫名稱
moduleName "Hello"
# 指定需要產出哪些架構平臺
abiFilters "armeabi", "armeabi-v7a", "x86", "x86_64"
}
}
然後編輯:grandle.properties文件增加android.useDeprecatedNdk=true屬性
- cmake配置
在這裏我們實際操作採用的是cmake方式進行編譯鏈接。
和ndk方式一樣也需要編輯build.grandle文件進行配置增加cmake屬性。
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.example.sven.ndkdemo"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// ndk {
// moduleName "Hello"
// abiFilters "armeabi", "armeabi-v7a", "x86"
// }
// 增加cmake控制屬性
externalNativeBuild{
cmake{
// 指定編譯架構
abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'
}
}
}
// 在android節點下
// 指定CMakeLists.txt路徑
externalNativeBuild{
cmake{
// 在該文件種設置所要編寫的c源碼位置,以及編譯後so文件的名字
path 'CMakeLists.txt'
}
}
- 添加編寫CMakeLists.txt文件
首先,添加CMakeLists.txt文件到build.gradle文件同級目錄下即app目錄中添加
其次, 編寫控制內容,具體語法和正常在Linux環境創建cmake工程一樣,如果開發過linux應用經過使用cmake維護編譯工程的話,那對於這個就很容易理解了。具體內容如下:
# 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.
#CMakeLists.txt
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.
add_library(
# 設置so文件名稱.
Hello
# 設置這個so文件爲共享.
SHARED
# Provides a relative path to your source file(s).
src/main/jni/Hello.c)
# 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.
# 制定目標庫.
Hello
# Links the target library to the log library
# included in the NDK.
${log-lib} )
測試使用JNI方法
接下來我就可以編寫應用測試jni方法是否可以使用了。
在MainActivity.java中增加實例:
package com.example.sven.ndkdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
String TAG="NDKDemo";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 創建JNI實例,並調用本地聲明的方法
String result = new JNI().sayHello();
System.out.println(result);
// 打印JNI本地方法返回的字符串。
Log.d(TAG, "the string from JNC C '"+result + "'");
}
}
編譯測試
編譯完成後會看到產出的so庫文件了,和我們測試用的apk;
庫文件所在目錄如下:
是不是前面所指定的幾個架構平臺都有了呢???
最後在模擬器上運行測試ndkdemo.apk將會看到輸出信息了:
2020-02-27 10:21:04.080 28711-28711/com.example.sven.ndkdemo D/NDKDemo: the string from JNC C 'I am from c'
總結:
ndkdemo工程實例可以在這裏下載
下載工程使用的小夥伴記得要修改成自己的ndk和sdk路徑哦!!!