带有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控制台中记录语句。

