JNI入门基础

环境安装

想要使用jni进行ndk开发,我们首先要安装下面这些工具,否则直接从入门到放弃。

  • 下载ndk支持
    在这里插入图片描述
    在Android studio中下载上图中框选的两个工具,版本号自己任意选一个。下载完成之后,Android Studio就拥有了进行ndk编译的能力。

  • 新建ndk工程
    如果是一个全新的工程,我们在新建工程时,选择c++工程,然后无脑下一步即可,这样Android Studio就会给你生成一个c++工程的模板代码,我们后续要添加一些c/c++代码都可以仿照这个模板。
    在这里插入图片描述
    在这里插入图片描述
    如果,我们是现在原有的工程上添加一个c/c++的library呢?我们可以新建一个native library,然后也是无脑下一步,跟新建一个java的library流程一致。
    在这里插入图片描述

  • c/c++代码提示设置
    在我们编写c/c++的过程中,有一个十分恶心的东西,就是没有代码提示,有时候编译器抽风,无故报红。这对一些ndk老手来说可能不算什么,但是对一些新手来说就十分难受了,那么我们该如何解决这个问题呢?
    在这里插入图片描述

JNI与NDK

jni,即Java本地接口,它的存在使得Java与本地其他类型语言(如c、c++)能够进行交互。java的很多功能实际上的驱动都是通过c/c++开发的,通过JNI,Java可以调用c/c++实现的驱动,从而扩展jvm的能力。另外,在高效率的数学运算、游戏的实时渲染、音视频的编码和解码方面,一般都是用c开发的。

  • jni在线api:https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html

  • jni和ndk的关系

    1. jni是jdk提供的一套非常强大的框架,可供c/c++与java互相调用,一般jni的头文件在jdk安装路径的include文件夹里面。
    2. ndk是Android平台提供的Native开发工具集开发包,可以认为是把前面的jni额外又封装了一层。一般ndk中的jni安装在如下目录下:C:\Users\dell\AppData\Local\Android\Sdk\ndk\21.4.7075529\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include
  • 案例讲解
    我们来看看ndk开发中涉及到的几个文件:

    1. cpp/CMakeLists.txt
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.18.1)

# Declares and names the project.

project("jnidemo")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        jnidemo

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        jnidemo

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})
  1. cpp/native-lib.cpp:我们所写的c/c++就是放在该文件中或者新建一个新的c/c++文件
#include <jni.h>
#include <string>

// 日志输出
#include <android/log.h>

#define TAG "Brett"

// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG,  __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG,  __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG,  __VA_ARGS__);

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

extern "C"
JNIEXPORT void JNICALL
Java_com_mvp_jnidemo_MainActivity_changeStr(JNIEnv *env, jobject thiz) {
    //jclass获取方式一
//    jclass cls = env->FindClass("com/mvp/jnidemo/MainActivity");
    //方式二
    jclass cls = env->GetObjectClass(thiz);
    jfieldID nameFid = env->GetFieldID(cls, "name", "Ljava/lang/String;");

    jstring value = env->NewStringUTF("Beyond");
    env->SetObjectField(thiz, nameFid, value);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_mvp_jnidemo_MainActivity_changeAge(JNIEnv *env, jclass clazz) {
    jfieldID ageFid = env->GetStaticFieldID(clazz, "age", "I");

    int age = env->GetStaticIntField(clazz, ageFid);
    env->SetStaticIntField(clazz, ageFid, age + 10);

}

//c调用java
extern "C"
JNIEXPORT jint JNICALL
Java_com_mvp_jnidemo_MainActivity_callJavaMethod(JNIEnv *env, jobject thiz) {
    jclass mainActivitCls = env->GetObjectClass(thiz);

    // jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    jclass cls = env->GetObjectClass(thiz);
    jmethodID addMid = env->GetMethodID(cls, "add", "(II)I");

    // jint CallIntMethod(jobject obj, jmethodID methodID, ...)
    int result = env->CallIntMethod(thiz, addMid, 1, 1);
    LOGD("result:%d\n", result)
 // ++++++++++++++++++++++ C调用 public String showString(String str,int value) 函数


    jmethodID showStringMid = env->GetMethodID(mainActivitCls, "showString", "(Ljava/lang/String;I)Ljava/lang/String;");

    // jobject     (*CallObjectMethod)(jobject, jmethodID, ...);
    jstring value = env->NewStringUTF("李元霸");
    jstring resultStr = (jstring) env->CallObjectMethod(mainActivitThis, showStringMid, value, 9527);
    // jstring是在jni中的,c/c++中需要转为char*
    const char * resultCstr = env->GetStringUTFChars(resultStr, NULL);
    LOGD("r==:%s\n", resultCstr);
}

extern “C” // 表示下面的代码,采用C的编译方式 如果是新建了一个.c文件,则需要删掉否则编译会报错,如果是新建了一个c++文件,则需要添加
JNIEXPORT // JNIEXPORT: JNI重要标记关键字,不能少 可以认为是为不同os平台设定的一个调用规则(标记为该方法可以被外部调用) Windows内部规则,与Linux内部规则 不同
void // void 代表java中的 void
JNICALL // 也是一个关键字,(可以少的) jni call (约束函数入栈顺序,和堆栈内存清理的规则)
JNIEnv *env JNI JNIEnv是整个jni的核心所在,我们学习jni其实就是学习JNIEnv里面提供的api方法

如果当前是 native-lib.c
// (*env)->xxx函数
// (*env)->DeleteLocalRef()
// C语言是 JNIEnv *env 二级指针
// (*env)->DeleteLocalRef(env, NULL); // C是没有对象的,想持有env环境,就必须传递进去
(*env)->NewStringUTF(env,“AAAA”);
如果当前是 native-lib.cpp ->调用一级指针下的函数
// env->xxx函数
// env->DeleteLocalRef()
// C++语言是 JNIEnv *env 一级指针
// env->DeleteLocalRef(NULL); // C++是有对象的,本来就会持有this,所以不需要传

为简单通用起见,我们就使用c++语言来编写相关的逻辑。

  1. 调用者(拥有native方法的类)——>MainActivity
package com.mvp.jnidemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.mvp.jnidemo.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {
    private String name = "Brett";
    private static int age = 20;

    // Used to load the 'jnidemo' library on application startup.
    //可以认为哪里又native方法,哪里就需要System.loadLibrary("xxx");方法
    static {
        System.loadLibrary("jnidemo");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Example of a call to a native method
        TextView tv = binding.sampleText;
        tv.setText(stringFromJNI());
        Log.e("MainActivity", "name is " + name+" ,age is "+age);
        changeStr();
        changeAge();
        Log.e("MainActivity", "name is " + name+" ,age is "+age);
        callJavaMethod();
    }
     public int add(int num1, int num2) {
        Log.e("MainActivity", "num1 is " + num1 + " ,num2 is " + num2);

        return num1 + num2;
    }
    public String showString(String str,int value) {
        System.out.println("C居然调用了我 showString str:" + str + " value:" + value);
        return "【" + str + "】";
    }

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

    public native void changeStr();

    public static native void changeAge();
        public native int callJavaMethod();

}

附:类型签名

代码类型
“I”int
“B”byte
“C”char
“D”double
“F”float
“J”long
“S”short
“Z”boolean
“V”void
“[…;”数组
“[[…;”二维数组
“[[[…;”三维数组
String[][Ljava/lang/String;
String[][][[Ljava/lang/String;
int,String,String[]ILjava/lang/String;[Ljava/lang/String;
int,boolean,long,String[],doubleIZJ[Ljava/lang/String;D
Class<?>,String,Object…paramTypeLjava/lang/Class;Ljava/lang/String;[Ljava/lang/Object;
int[][I
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值