带有NDK的Android JNI应用程序

时间:2020-02-23 14:29:01  来源:igfitidea点击:

在本教程中,我们将讨论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控制台中记录语句。