带有NDK的Android JNI应用程序
在本教程中,我们将讨论JNI并使用NDK工具开发基本的Android应用程序。
尽管不是前提条件,但使用c/c ++的背景将很有用。
Android NDK是Android SDK的配套工具,可让我们在开发中使用本机代码C/C ++。
要使用NDK工具,请确保从SDK Manager下载了以下两个突出显示的工具:
Android Jni Ndk安装
什么是JNI?
JNI代表Java本机接口。
它充当Java/Kotlin代码可以调用本机代码的桥梁,反之亦然。
此外,游戏中通常需要JNI,因为大多数引擎都是使用c/c ++构建的。
为了在Java中调用本机方法,您需要首先在Java代码中使用native关键字对其进行定义。
示例:public native String stringFromJNI()
native关键字将方法转换为抽象方法,我们需要在c/c ++的共享库中实现该方法。
要在c/c ++中定义方法,我们需要使用以下签名:
extern "C" JNIEXPORT jstring JNICALL Java_com_theitroad_androidjnibasics_MainActivity_stringFromJNI(JNIEnv* env,jobject)
我们在软件包名称前添加Java_。
程序包名称后跟活动名称,不带扩展名和本机方法名称。
" JNIEXPORT" –将函数标记为可导出到共享库中,因此将其包含在函数表中,因此JNI可以找到它" JNICALL" –结合" JNIEXPORT",可确保我们的方法可用于JNI框架。
JNIEnv是JNI空间中最重要的组件。
它充当访问Java对象的桥梁。
下面给出了JNI类型以及Java中的等效类型:
- 布尔值:jboolean
- 字节:jbyte
- 字符:jchar
- 双重:jdouble
- float:jfloat
- int:jint
- long:jlong
要在启动时加载本机库,请在代码中添加以下代码块:
static { System.loadLibrary("native-lib"); }
" native-lib"是c/c ++的本机文件的名称。
在下一部分中,我们将在Android中创建JNI应用程序。
我们将介绍:
如何获取/发送Java字符串到本地代码。
在Android Studio日志控制台中从本机代码查看日志。
从Java中的c ++获取字符串数组。
入门
首先,创建一个新项目,然后选择C ++类型模板以将JNI集成到应用程序中。
Android Jni项目设置1
接下来,从下一个屏幕中选择C ++编译器。
Android Jni项目设置2
在下一部分中,让我们看看我们的项目结构如何。
项目结构
Android Jni项目结构
Android Studio可以使用" CMake"将C和C ++代码编译到本机库中,然后IDE会将其打包到您的APK中。
代码
在默认设置中,我们有一个本机函数,该函数允许我们使用JNI从Java代码中的本机代码中获取字符串。
让我们通过两个新按钮来改进布局,以使用更多JNI函数。
下面给出了" activity_main.xml"布局的代码:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/sample_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" <Button android:id="@+id/btnJni" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" android:text="PASS YOUR NAME TO JNI" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/sample_text" <Button android:id="@+id/btnJniStringArray" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" android:text="RETURN STRING ARRAY" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/btnJni" </android.support.constraint.ConstraintLayout>
MainActivity.java类的代码如下:
package com.theitroad.androidjnibasics; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { //Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } Button btnJNI, btnJNIStringArray; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Example of a call to a native method TextView tv = findViewById(R.id.sample_text); tv.setText(stringFromJNI()); btnJNI = findViewById(R.id.btnJni); btnJNIStringArray = findViewById(R.id.btnJniStringArray); btnJNI.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String result = sendYourName("Anupam", "Chugh"); Toast.makeText(getApplicationContext(), "Result from JNI is " + result, Toast.LENGTH_LONG).show(); } }); btnJNIStringArray.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String[] strings = stringArrayFromJNI(); Toast.makeText(getApplicationContext(), "First element is "+strings[0], Toast.LENGTH_LONG).show(); } }); } /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public native String stringFromJNI(); public native String sendYourName(String firstName, String lastName); public native String[] stringArrayFromJNI(); }
我们定义了三种本地方法-字符串,使用本地代码计算字符串以及返回,返回字符串数组。
让我们深入研究" native-lib.cpp"文件,其中包含用C ++定义的功能:
#include <jni.h> #include <string> #include <android/log.h> #include <string.h> extern "C" JNIEXPORT jstring JNICALL Java_com_theitroad_androidjnibasics_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++"; __android_log_write(ANDROID_LOG_DEBUG, "API123", "Debug Log"); return env->NewStringUTF(hello.c_str()); } extern "C" JNIEXPORT jstring JNICALL Java_com_theitroad_androidjnibasics_MainActivity_sendYourName( JNIEnv* env, jobject, jstring firstName, jstring lastName) { char returnString[20]; const char *fN = env->GetStringUTFChars(firstName, NULL); const char *lN = env->GetStringUTFChars(lastName, NULL); strcpy(returnString,fN); //copy string one into the result. strcat(returnString,lN); //append string two to the result. env->ReleaseStringUTFChars(firstName, fN); env->ReleaseStringUTFChars(lastName, lN); __android_log_write(ANDROID_LOG_DEBUG, "API123", returnString); return env->NewStringUTF(returnString); } extern "C" JNIEXPORT jobjectArray JNICALL Java_com_theitroad_androidjnibasics_MainActivity_stringArrayFromJNI(JNIEnv *env, jobject jobj) { char *days[]={"Java", "Android", "Django", "SQL", "Swift", "Kotlin", "Springs"}; jstring str; jobjectArray day = 0; jsize len = 7; int i; day = env->NewObjectArray(len,env->FindClass("java/lang/String"),0); for(i=0;i<7;i++) { str = env->NewStringUTF(days[i]); env->SetObjectArrayElement(day,i,str); } return day; }
要将JNI字符串转换为本地char数组,可以使用env的GetStringUTFChars方法。
但使用后需要释放。
android_log_write用于在Android控制台中记录语句。