本文转发语NCK大佬的博客,并且自己跟着做了一遍 有一点点不同 大致上是大佬的文章:
https://www.cnblogs.com/fuhua/p/12725771.html

前面几篇文章演示的是比较原始的创建JNI项目的方法,旨在了解JNI项目构建原理!
但是构建项目效率很低,开发,调试都存在很大的效率低下问题。
本篇文章将演示利用Android Studio快速构建JNI项目。本篇文章要点

  1. 利用Android Studio快速构建JNI项目
  2. 添加日志打印
  3. Android Studio调试C/C++代码
  4. JNI动态注册
  5. 简易计算器实现。

1. 新建项目

打开Android Studio新建Project,选中Native c++选项,此选项可以帮助开发人员快速创建JNI项目,免去手动配置等麻烦问题。

file

项目取名为JNIRegisterDynamic,点击Next
file

使用 C++17标准,点击Finish
file

2. 安装NDK开发组件

File->Settings->Android SDK->SDK Tools选项下,安装LLDB,NDK,CMake

file

等待安装完毕

file

File->Project Structure->SDK Location->Android NDK Location ,选择Default xxxx选项,从而配置完成NDK

file

为了验证上一步是否配置成功,需要来到local.properties,如果同时出现ndk.dir=xxx sdk.dir=xxx 证明配置成功

file

此时如果我们对native-lib.cpp进行编辑,可以看到出现了智能提示。就代表我们的环境配置没有问题了。
file

3. 添加日志打印

接下来我们添加日志打印输出,将下面代码添加到cpp文件头部,使用的时候直接调用就行,比如:LOGI("我是输出的日志信息");

#include <android/log.h>
#define LOG_TAG    "JniDebugLogger"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGF(...)  __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)

file

接下来我们在C/C++代码下断点,并点击Debug图标。看是否能正确断下
file

注意,当出现如下界面时,千万别点击,只需要什么都不做,等几秒钟,断点断下就行
file

file

JNI动态注册和简易计算器实现

接下来我们在MainActivity中定义4个Native方法,分别为 add(+) sub(-) mul(*) div(/)

    private native int add(int a, int b);

    private native int sub(int a, int b);

    private native int mul(int a, int b);

    private native int div(int a, int b);

file

在native-lib.cpp里实现MainActivity的方法,native-lib.cpp源码如下

#include <jni.h>
#include <string>

#include <android/log.h>

#define LOG_TAG    "JniDebugLogger"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGF(...)  __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jniregisterdynamic_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

jint NativeAdd(JNIEnv *env, jobject obj, jint a, jint b) {
    return a + b;
}

jint NativeSub(JNIEnv *env, jobject obj, jint a, jint b) {
    return a - b;
}

jint NativeMul(JNIEnv *env, jobject obj, jint a, jint b) {
    return a * b;
}

jint NativeDiv(JNIEnv *env, jobject obj, jint a, jint b) {
    return a / b;
}

// JNI函数签名数组
JNINativeMethod jniNativeMethods[]
        {
                {"add", "(II)I", (void *) NativeAdd},
                {"sub", "(II)I", (void *) NativeSub},
                {"mul", "(II)I", (void *) NativeMul},
                {"div", "(II)I", (void *) NativeDiv}
        };

jint RegistNativeMethods(JNIEnv *env) {
    //获得class
    jclass clazz = env->FindClass("com/example/jniregisterdynamic/MainActivity");
    //执行动态注册
    if (env->RegisterNatives(clazz, jniNativeMethods,
                             sizeof(jniNativeMethods) / sizeof(jniNativeMethods[0])) == JNI_OK) {

        return JNI_OK;
    }
    return JNI_ERR;
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *jniEnv = nullptr;
    if (vm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6) != JNI_OK) {
        LOGE("GetEnv ERROR");
        return JNI_ERR;
    }
    if (RegistNativeMethods(jniEnv) != JNI_OK) {
        LOGE("RegistNativeMethods FAILED");
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

file

在MainActivity里添加测试代码,运行测试

tv.setText("add" + add(1, 6) + " sub" + sub(10, 3) + " mul" + mul(3, 6) + " div" + div(9, 3));

file

file

运行测试Native层没有问题,接下来添加Java层代码并在界面增加控件,实现简单的计算器。MainActivity代码如下

package com.example.jniregisterdynamic;

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 {

    // Used to load the "native-lib" library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    private native int add(int a, int b);

    private native int sub(int a, int b);

    private native int mul(int a, int b);

    private native int div(int a, int b);

    @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("add" + add(1, 6) + " sub" + sub(10, 3) + " mul" + mul(3, 6) + " div" + div(9, 3));

//        tv.setText(stringFromJNI());
        Button btn_add = (Button) findViewById(R.id.btn_add);
        Button btn_sub = (Button) findViewById(R.id.btn_sub);
        Button btn_mul = (Button) findViewById(R.id.btn_mul);
        Button btn_div = (Button) findViewById(R.id.btn_div);

        View.OnClickListener ocl = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EditText editA = (EditText) findViewById(R.id.editA);
                int nA = Integer.parseInt(editA.getText().toString());
                EditText editB = (EditText) findViewById(R.id.editB);
                int nB = Integer.parseInt(editB.getText().toString());
                TextView tv = findViewById(R.id.text_value);
                switch (v.getId()) {
                    case R.id.btn_add:
                        tv.setText("" + add(nA, nB));
                        break;
                    case R.id.btn_sub:
                        tv.setText("" + sub(nA, nB));
                        break;
                    case R.id.btn_mul:
                        tv.setText("" + mul(nA, nB));
                        break;
                    case R.id.btn_div:
                        tv.setText("" + div(nA, nB));
                        break;
                }
            }
        };
        btn_add.setOnClickListener(ocl);
        btn_sub.setOnClickListener(ocl);
        btn_mul.setOnClickListener(ocl);
        btn_div.setOnClickListener(ocl);
    }

    /**
     * A native method that is implemented by the "native-lib" native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
}

activity_main.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">

    <EditText
        android:id="@+id/editA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="91dp"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"  />

    <EditText
        android:id="@+id/editB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="56dp"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintStart_toStartOf="@+id/editA"
        app:layout_constraintTop_toBottomOf="@+id/editA" />

    <Button
        android:id="@+id/btn_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:text="@string/btn_add"
        app:layout_constraintBaseline_toBaselineOf="@+id/btn_sub"
        app:layout_constraintEnd_toStartOf="@+id/btn_sub"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"  />

    <Button
        android:id="@+id/btn_sub"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="60dp"
        android:layout_marginEnd="5dp"
        android:text="@string/btn_sub"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/btn_add"
        app:layout_constraintTop_toBottomOf="@+id/editB" />

    <Button
        android:id="@+id/btn_mul"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:text="@string/btn_mul"
        app:layout_constraintBaseline_toBaselineOf="@+id/btn_div"
        app:layout_constraintEnd_toStartOf="@+id/btn_div"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"  />

    <Button
        android:id="@+id/btn_div"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="34dp"
        android:layout_marginEnd="5dp"
        android:text="@string/btn_div"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/btn_mul"
        app:layout_constraintTop_toBottomOf="@+id/btn_sub" />

    <TextView
        android:id="@+id/text_value"
        android:layout_width="92dp"
        android:layout_height="23dp"
        android:text="@string/str_text_value"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.795"  />
</androidx.constraintlayout.widget.ConstraintLayout>

file

测试运行
file

源代码:
https://github.com/luodaoyi/Android_JNI/tree/master/04_JNI_register_dynamic