日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android JNI开发流程介绍

發(fā)布時間:2024/9/21 Android 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android JNI开发流程介绍 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1.什么是JNI

JNI全稱是Java Native Interface,中文稱為Java本地接口。JNI是JAVA語言和C/C++語言溝通的協(xié)議,通過JNI,Java代碼可以調(diào)用C、C++等語言寫的代碼,或者反過來C、C++等語言代碼通過JNI調(diào)用Java 寫的代碼。

為什么使用JNI?

我們知道,Java語言的特性是一次編寫,到處運(yùn)行,跨平臺是Java的優(yōu)點(diǎn),但有得就有失,跨平臺的特性導(dǎo)致了Java和底層交互能力不夠強(qiáng)大,而這正是C/C++語言所擅長的,另一方面,Java通過JNI可以復(fù)用大量現(xiàn)有的C/C++庫文件,避免重復(fù)造輪子。

2.什么是NDK

NDK全稱是Native Develop Kit,可譯為本地開發(fā)套件,NDK是Android提供的工具集,用于進(jìn)行C/C++的開發(fā)。

JNI和NDK又是什么關(guān)系?

JNI是一個接口協(xié)議,NDK是一個開發(fā)套件,他們之間有個共同點(diǎn)是英文都包含Native(本地),都和C/C++語言扯上關(guān)系,不過以這兩點(diǎn)硬說他們有關(guān)系就有點(diǎn)太勉強(qiáng)了,事實上,JNI是Java的,NDK是Android的,兩者沒有相互依存關(guān)系,硬要說有什么關(guān)系的話,就是使用NDK,可在Android中快速開發(fā)符合JNI接口要求的C/C++動態(tài)庫,方便Java調(diào)用。

可能有人會問了,Android開發(fā)語言不是Java語言,為何還另外提供C/C++語言開發(fā)工具?

要知道,Android開發(fā)語言雖然是Java,但內(nèi)核是Linux,而Linux核心庫是用C/C++編寫,因此Google提供NDK工具,方便開發(fā)者和核心庫交互。不過,一般開發(fā)純業(yè)務(wù)的應(yīng)用,不會涉及到NDK,如果涉及到以下需求則需要用到NDK:

  • 1、提供代碼安全性。因為so庫文件反編譯較難,將核心代碼封裝成so庫文件,大大提高了代碼的安全性;
  • 2、便于平臺間移植其應(yīng)用。通過NDK,開發(fā)人員可以方便生成指定平臺的動態(tài)庫;
  • 3、重復(fù)使用現(xiàn)有C/C++開源庫;
  • 4、提升程序執(zhí)行效率,特別是一些計算密集型應(yīng)用。

3.JNI開發(fā)流程

以兩個數(shù)相加作為例子,兩個數(shù)相加使用C/C++實現(xiàn),再通過JNI調(diào)用。這和日常生活中的外包流程很類型,Java自己不想做,發(fā)了一個外包需求,C接下了這個活。

  • 第一步:第一步是Java發(fā)布需求。首先創(chuàng)建包名為com.test.jnitest的工程,在工程中創(chuàng)建名為JNIUtils的class,并在類中聲明一個native方法。代碼如下:
package com.test.jnitest;public class JNIUtils {static{//加載動態(tài)庫,即加法實現(xiàn)所在的鏈接庫。System.loadLibrary("jni_method");}//native關(guān)鍵字提示該方法由本地方法實現(xiàn)public static native int add(int a,int b);}

native關(guān)鍵字表示這是一個要外包實現(xiàn)的函數(shù),那么C完成外包工作后以什么形式給Java交差呢,用so庫文件,然后Java通過System.loadLibrary(“jni_method”)加載,so庫文件名jni_method值隨意,只要Java和C之間約定好就行。

  • 第二步:這一步還是Java這邊的,是Java這邊拿到C交差工作后做的事。我們要實現(xiàn)的功能是在兩個文本框分別輸入兩個數(shù)字后,點(diǎn)擊相加按鈕,然后代碼調(diào)用C完成的兩個數(shù)進(jìn)行相加功能,并把結(jié)果顯示在界面上,界面布局xml代碼如下:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.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=".MainActivity">```<EditTextandroid:id="@+id/etNum1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="120"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><EditTextandroid:id="@+id/etNum2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="60dp"android:text="240"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/bAdd"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="120dp"android:text="加"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="相加結(jié)果"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

Activity里調(diào)用本地相加代碼如下:

package com.test.jnitest;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView;public class MainActivity extends AppCompatActivity {Button Add;EditText Num1;EditText Num2;TextView Result;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Add=findViewById(R.id.bAdd);Num1=findViewById(R.id.etNum1);Num2=findViewById(R.id.etNum2);Result=findViewById(R.id.tvResult);Add.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String sN1=Num1.getText().toString().trim();String sN2=Num2.getText().toString().trim();int iN1 = Integer.parseInt(sN1);int iN2 = Integer.parseInt(sN2);//調(diào)用本地方法進(jìn)行相加int iResult=JNIUtils.add(iN1,iN2);//記得不要直接打印Result.setText(iResult),這樣代碼會認(rèn)為iResult是個 資源IDResult.setText(“相加結(jié)果”+iResult);}});}}
  • 第三步:Java這邊代碼是準(zhǔn)備好了,接下來的工作是把外包工作布置給C的過程,因為Java和C是兩種語言,他們之間不能直接溝通,需要做一下處理,以下就是溝通過程:首先編譯Java源文件得到.class文件,方法是點(diǎn)擊Visual Studio的Build菜單下的Make Project,然后在下圖的路徑找到生成的.class文件,注意,該路徑和有些博客所說的不一致,他們生成的.class文件路徑在intermediates的classes文件夾下。

-第四步:生成.class文件C語言還是看不懂,因此還是再做一次處理,處理方式是通過.class文件得到C的頭文件,這樣C語言就能看懂了,生成頭文件使用javah命令,可以在終端中執(zhí)行(我的是MAC)或者使用Android Studio里的終端,兩者其實一樣。首先進(jìn)入在終端里進(jìn)入classes路徑下,注意一定要在classes這一級目錄,不然會提示找不到class文件。我的環(huán)境是:
/Users/lan/AndroidStudioProjects/JNITest/app/build/intermediates/javac/debug/classes,然后執(zhí)行命令:

javah -jni -cp . com.test.jnitest.JNIUtils

注:命令應(yīng)包含完整的包名,并且.class文件不能帶“.class”后綴,另外之前按照網(wǎng)上其他博客的命令,一直報找不到類的錯誤,加上-cp參數(shù)解決

執(zhí)行命令成功后,即可得到.h文件,如下圖所示。

生成得到的.h文件內(nèi)容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_test_jnitest_JNIUtils */#ifndef _Included_com_test_jnitest_JNIUtils #define _Included_com_test_jnitest_JNIUtils #ifdef __cplusplus extern "C" { #endif /** Class: com_test_jnitest_JNIUtils* Method: add* Signature: (II)I*/ JNIEXPORT jint JNICALL Java_com_test_jnitest_JNIUtils_add(JNIEnv *, jclass, jint, jint);#ifdef __cplusplus } #endif #endif

-第五步:得到頭文件后,就可以根據(jù).h文件,實現(xiàn)對應(yīng)的C代碼。在java目錄下,新建一個jni文件夾,將生產(chǎn)的.h文件拷貝到這個目錄下,然后新建一個名為JNIUtils的C文件,在該代碼中實現(xiàn)頭文件聲明的函數(shù),代碼實現(xiàn)如下:

#include <jni.h> #include "com_test_jnitest_JNIUtils.h"JNIEXPORT jint JNICALL Java_com_test_jnitest__ADD(JNIEnv *jnienv, jclass obj, jint num1, jint num2){return num1+num2;}

-第六步:C代碼實現(xiàn)后,已經(jīng)實現(xiàn)了Java發(fā)布的外包需求,接下來的工作是C以Java看得懂的形式交差外包工作,在第一步中Java已經(jīng)說明了,給他交差工作用so庫文件的形式,因此接下來的工作就是想辦法生成so庫文件。首先在jni目錄下創(chuàng)建Android.mk文件,Android.mk作用是指定源碼編譯的配置信息,Android.mk內(nèi)容如下:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := jni_calLOCAL_SRC_FILES := JNITool.cinclude $(BUILD_SHARED_LIBRARY)

其中

  • LOCAL_PATH := $(call my-dir)得到Android.mk文件本身所在的路徑,宏my-dir則由編譯系統(tǒng)提供,返回當(dāng)前目錄(Android.mk 文件本身所在的目錄)的路徑;

  • include $(CLEAR_VARS) 宏CLEAR_VARS 變量由編譯系統(tǒng)提供。并指向一個指定的GNU Makefile,由它負(fù)責(zé)清理LOCAL_PATH之外的LOCAL_xxx,例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等。為什么要執(zhí)行這個清理操作,因為所有的編譯控制文件由同一個GNU Make解析和執(zhí)行,其變量是全局性的,清理后才能避免相互影響,因此在描述每個庫之前,必須有該聲明;

  • LOCAL_MODULE:=jni_method 表示的是要生成的庫,這個庫就是在java里加載的庫,每個庫名稱必須唯一,且不含任何空格。編譯系統(tǒng)在生成最終的庫名稱里自動添加lib前綴和so后綴。例如,上述示例會生成名為libjni_cal.so,如果在LOCAL_MODULE定義的名稱已經(jīng)帶lib了,則編譯系統(tǒng)不會再添加lib前綴,例如名稱是libmodule,那么編譯系統(tǒng)輸出的是libmodule.so,而不是liblibmodule.so;

  • LOCAL_SRC_FILES :=JNIUtils.c包含要編譯到庫中的 C 和/或 C++ 源文件列表,不必列出頭文件,編譯系統(tǒng)會自動幫我們找出依賴文件;

  • include $(BUILD_SHARED_LIBRARY),其中BUILD_SHARED_LIBRARY 變量指向一個GNU Makefile腳本,該腳本會收集您自最近include以來在 LOCAL_XXX 變量中定義的所有信息。此腳本確定要編譯的內(nèi)容以及編譯方式:

    • BUILD_STATIC_LIBRARY:編譯為靜態(tài)庫
    • BUILD_SHARED_LIBRARY:編譯為動態(tài)庫
    • BUILD_EXECUTABLE:編譯為Native C 可執(zhí)行程序
    • BUILD_PREBUILT:該模塊已經(jīng)預(yù)先編譯
      最后一行幫助系統(tǒng)將所有內(nèi)容連接到一起:

第七步,在jni目錄下創(chuàng)建Application.mk文件,其中內(nèi)容就一句話:APP_ABI:=all。在上一步中,Android.mk解決的問題是編譯誰,但還沒解決編譯出來給哪個平臺用,這是Application.mk要做的工作,常見的平臺有Arm,x86,MIPS,配置方法是在APP_ABI字段設(shè)置成對應(yīng)的值,例如如果想配置成基于Arm平臺的so文件,則APP_ABI := armeabi,至于要生成哪個平臺,這就看Java代碼準(zhǔn)備運(yùn)行在哪個平臺了,我這里設(shè)置成配置支持所有平臺,對應(yīng)的字段是APP_ABI := all。

第八步,到目前為止,生成so文件的準(zhǔn)備工作已經(jīng)差不多了,接下來要做的則是使用NDK工具生成so文件。關(guān)于配置NDK環(huán)境不再贅述(折騰過程可以寫成一篇博客了),這里假設(shè)NDK環(huán)境已經(jīng)配置好了。生成NDK過程很簡單:在終端進(jìn)入到j(luò)ni目錄,終端在Android Studio底部,進(jìn)到目錄后,輸入ndk-build命令,編譯成功后,在src/main/會多了兩個文件夾libs & obj,其中l(wèi)ibs下存放的是生成的so庫文件,因為我在Application.mk設(shè)置的全平臺,因此生成所有平臺的so文件,如果Application.mk設(shè)置成特定平臺,則只生成特定平臺的so文件。拿到so文件后,C可以給Java交差任務(wù)了。

第九步,這一步要做的工作把so庫文件交給Java。首先在src/main/中創(chuàng)建一個名為jniLibs的文件夾,并將上一步生成的so文件夾放到該目錄下,這里有兩點(diǎn)需要注意一下,一是因為要拷貝哪些so庫,需要看Android程序準(zhǔn)備運(yùn)行在哪些平臺上,如果拷貝的so庫文件不正確,則應(yīng)用不能正常安裝,會提示ABI不匹配錯誤,因為我的模擬器是x86平臺,因此我拷貝了x86平臺的so庫文件到j(luò)niLibs下,二是拷貝文件是文件夾一起拷貝,不能單拷貝單個so文件到j(luò)niLibs下。

第十步,因為在第一步時,Java端的代碼已經(jīng)準(zhǔn)備好了,現(xiàn)在so庫文件也拷貝到指定目錄下,Java代碼可以直接運(yùn)行,下圖是在模擬器上運(yùn)行的效果。

4.總結(jié)

把JNI開發(fā)流程當(dāng)成Java和C之間的一次外包需求開發(fā)理解起來就容易了,首先是Java發(fā)布需求,使用native關(guān)鍵字聲明函數(shù)由外包實現(xiàn),并聲明了外包任務(wù)交差以so庫文件的形式,Java外包任務(wù)通過頭文件的形式交給C語言,之后C語言實現(xiàn)后,再想辦法生成so庫文件來交差任務(wù),最后將so庫文件拷貝到指定目錄下,Java能正常加載,整個流程也到處結(jié)束。

與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的Android JNI开发流程介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。