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路徑哦!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章