C语言JNI的动态注册详解

目录
  • 总结

JNI的静态注册就是Javah生成头文件,本章第一篇已经讲过,现在我们来讲讲第二种方式,JNI动态注册。首先是module的build.gradle:

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.jhzl.a7_jni_2way"
        minSdkVersion 21
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

那我们的Activity的本地方法贴出来把:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView addTv = findViewById(R.id.add_tv);
        TextView subTv = findViewById(R.id.sub_tv);
        addTv.setText("加法计算:"+add(10,20));
        subTv.setText("减法计算:"+sub(10,20));
    }
    static {
        //加载静态库
        System.loadLibrary("native-lib");
    }

    public native int add(int a,int b);
    public native  int sub(int a,int b);

}

接着就是CMakeLists.txt

# 指定cmke版本
cmake_minimum_required(VERSION 3.4.1)
add_library(native-lib
        SHARED
        src/main/cpp/Hello.cpp) #添加.c源文件
include_directories(src/main/cpp/include) #添加头文件的位置
target_link_libraries(native-lib
        log) #选择要使用的库

最后,我们贴出今天的重头戏Hello.h:

#ifndef PRACTICLE_HELLO_H
#define PRACTICLE_HELLO_H
extern "C"
#endif //PRACTICLE_HELLO_H

Hello.cpp:

#include <jni.h>
#include "Hello.h"
#include<android/log.h>
#define TAG "helloworld-jni" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
#ifdef __cplusplus
extern "C" {
#endif

//对应MainActivity的add
jint add(JNIEnv *env, jobject clazz, jint a, jint b) {
    return a + b;
}

//对应MainActivity的sub
jint sub(JNIEnv *env, jobject clazz, jint a, jint b) {
    return a - b;
}

//回调函数 在这里面注册函数
jint  JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv* env = NULL;
    //env赋值
    LOGD("GetEnv");
    //获取JniEnv
    if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!= JNI_OK){
        LOGD("GetEnv Failed");
        return -1;
    }
    //获取一个class对象,对应java类MainActivity
    jclass  mainActivity = env->FindClass("com/jhzl/a7_jni_2way/MainActivity");

    /**
     * add : java方法名
     * (II)I 里面是两个jint,返回jnit
     * add,c函数指针,传递的参数应该是env,jobject,包括签名的(II)两个jint,一个jnit返回值
     *
     */
    JNINativeMethod methods_MainActivity[] = {
            {"add", "(II)I", (jint *) add},
            {"sub", "(II)I", (jint *) sub}

    };
    env->RegisterNatives(mainActivity,methods_MainActivity,sizeof(methods_MainActivity)/ sizeof(methods_MainActivity[0]));
    env->RegisterNatives(mainActivity,methods_MainActivity,sizeof(methods_MainActivity)/ sizeof(methods_MainActivity[1]));
    //返回jni 的版本
    return JNI_VERSION_1_6;
}

#ifdef __cplusplus
}
#endif

最后运行的效果是:

jni参数对照表:

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • windows下vscode+vs2019开发JNI的示例

    JNI全称是Java Native Interface(Java本地接口)单词首字母的缩写,本地接口就是指用C和C++开发的接口.由于JNI是JVM规范中的一部份,因此可以将我们写的JNI程序在任何实现了JNI规范的Java虚拟机中运行.同时,这个特性使我们可以复用以前用C/C++写的大量代码. 开发JNI程序会受到系统环境的限制,因为用C/C++语言写出来的代码或模块,编译过程当中要依赖当前操作系统环境所提供的一些库函数,并和本地库链接在一起.而且编译后生成的二进制代码只能在本地操作系统环境下

  • 详解JNI到底是什么

    目录 一.前言 二.准备java代码 三.生成头文件 四.gcc环境安装 五.生成动态链接库 六.总结 一.前言 首先回顾一下jni的主要功能,从jdk1.1开始jni标准就成为了java平台的一部分,它提供的一系列的API允许java和其他语言进行交互,实现了在java代码中调用其他语言的函数.通过jni的调用,能够实现这些功能: 通常情况下我们一般使用jni用来调用c或c++中的代码,在上一篇文章中我们用了下面的流程来描述了native方法的调用过程: Java Code -> JNI ->

  • Android Studio 3.5版本JNI生成SO文件详解

    学习在于记录,把自己不懂得容易忘记得记录下,才是最好得选择. 废话不多说,想要在Android开发中嵌入c/c++代码,直接开始如下步骤 1.创建需要调用的Java类 在你某个指定的包下创建如下类 package com.journey.org; public class JniHello{ static { System.loadLibrary("JniHello") } public static native String welcomeJniStudy(); } 2.创建通用工

  • 鸿蒙手机版JNI实战案例解析(JNI开发、SO库生成、SO库使用)

    鸿蒙JNI开发现状 现阶段,不仅鸿蒙JNI相关的开发资料较少,而且Phone相关的JNI开发资料几乎没有,这对于新入行的鸿蒙开发者们来说,非常不友好. 也可能会给Android工程(使用了SO库的工程)在迁移至鸿蒙系统时造成了阻碍. 案例演示 废话不多说了,接下来,我们来演示鸿蒙手机版工程是如何做JNI开发的. 案例1:Native项目 如果开发者们只是想做简单的Native开发,并非为第三方提供SO库,这就非常简单了,详细如下: 1.创建Native C++工程 目前,通过DevEco-Stu

  • 实例详解Android中JNI的使用方法

    目录 前言 1.导入C语言的类 2.接着导入Android.mk文件 3.我们配置一下build.gradle文件 4.好了,此时可以编译一下项目了 5.此时我们可以找一下我们生成的so包了 6.将生成的so文件拷入src/main/jniLibs中 7.调用C语言方法的Activity如下 总结 前言 做Android开发的程序员应该都知道,Android的开发语言我们都是在使用JAVA(Kotlin和Flutter我们暂时不考虑).但是,有时候我们也需要使用到C语言进行一些功能的开发.这个时

  • C语言JNI的动态注册详解

    目录 总结 JNI的静态注册就是Javah生成头文件,本章第一篇已经讲过,现在我们来讲讲第二种方式,JNI动态注册.首先是module的build.gradle: android { compileSdkVersion 30 buildToolsVersion "30.0.3" defaultConfig { applicationId "com.jhzl.a7_jni_2way" minSdkVersion 21 targetSdkVersion 30 versi

  • Oracle静态注册与动态注册详解

    一.概述: Oracle的注册就是将数据库作为一个服务注册到监听程序.客户端不需要知道数据库名和实例名,只需要知道该数据库对外提供的服务名就可以申请连接到这个数据库.这个服务名可能与实例名一样,也有可能不一样. 在数据库服务器启动过程中,数据库服务器会向监听程序注册相应的服务(无论何时启动一个数据库,默认地都有两条信息注册到监听器中:数据库服务器对应的实例和服务.) 相当于是这样:在数据库服务器和客户端之间有一监听程序(Listener),在监听程序中,会记录相应数据库对应的服务名(一个数据库可

  • C语言 指针与数组的详解及区别

    C语言 指针与数组的详解及对比 通俗理解数组指针和指针数组 数组指针: eg:int( *arr)[10]; 数组指针通俗理解就是这个数组作为指针,指向某一个变量. 指针数组: eg:int*arr[10]; 指针数组简言之就是存放指针的数组: --数组并非指针&&指针并非数组 (1)定义一个外部变量: eg:int value=10; int *p=&value; 举例:当需要在一个函数中用这个变量时:externa int*p;而非extern int p[]; 分析:当用:e

  • IOS 静态方法与动态方法详解

    IOS 静态方法与动态方法详解 1.问题提出 iOS中有静态方法与动态方法,那么两种方法的异同是什么? 2.问题分析 因为每个对象都由相应的数据结构与方法相构成,一个程序可能有多个属于同一个类的对象,而每个对象的数据结构应该是不一的,但方法是相同的,若为每个对象开辟内存空间来存储方法,必然是对内存空间极大的浪费.因此apple是通过类对象与元类来解决这个问题的. 从根本来说,c++.objective-c.java都发源于c语言,因此这些语言实际上可以理解了经过封装的c语言,所以它们更加方便使用

  • mybatis的动态sql详解(精)

    MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力.如果你有使用 JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空 格或在列表的最后省略逗号.动态 SQL 可以彻底处理这种痛苦. 通常使用动态SQL不可能是独立的一部分,MyBatis当然使用一种强大的动态SQL语言来改进这种情形,这种语言可以被用在任意映射的SQL语句中. 动态SQL元素和使用 JSTL或其他相似的基于XML的文本处理器相似.在MyBatis之前的版本中,有很多

  • C语言变长数组使用详解

    看如下代码: #include<stdio.h> typedef struct { int len; int array[]; }SoftArray; int main() { int len = 10; printf("The struct's size is %d\n",sizeof(SoftArray)); return 0; } 运行结果: [root@VM-0-7-centos mydoc]# ./a.out The struct's size is 4 我们可以

  • C语言经典指针笔试题详解

    目录 题目一(有关传值调用与非法访问) 题目二 (返回栈空间地址的问题 ) 题目三 (区别传值调用的传址调用) 题目四 (free释放的时机)

  • C语言 function recursion函数递归详解

    目录 function recursion(函数递归) 递归的中心思想为: 程序一 递归的两个必要条件 程序一: 程序二: 练习 求n的阶乘 再来道例题 function recursion(函数递归) 函数递归: 是在 一个 过程 或 函数 在其定义或说明中有 直接 或 间接 调用自身 的一种方法 通常把一个 大型复杂的问题 层层 传化 为一个与 原理相似的 ,规模较小 的问题 递归策略 只需 少量的程序 就可以描述出 解题过程 所需的 多次 重复 计算,大大减少了程序的代码量 递归的中心思想

  • C语言数据结构之单向链表详解分析

    链表的概念:链表是一种动态存储分布的数据结构,由若干个同一结构类型的结点依次串连而成. 链表分为单向链表和双向链表. 链表变量一般用指针head表示,用来存放链表首结点的地址. 每个结点由数据部分和下一个结点的地址部分组成,即每个结点都指向下一个结点.最后一个结点称为表尾,其下一个结点的地址部分的值为NULL(表示为空地址). 特别注意:链表中的各个结点在内存中是可以不连续存放的,具体存放位置由系统分配. 例如:int *ptr ; 因此不可以用ptr++的方式来寻找下一个结点. 使用链表的优点

  • MyBatis的9种动态标签详解

    目录 前言 动态标签用法 1.if 2.choose.when.otherwise 3.where 4.set 5.trim 6.foreach 7.bind 前言 MyBatis提供了9种动态SQL标签:trim.where.set.foreach.if.choose.when.otherwise.bind: 其执行原理为,使用OGNL从SQL参数对象中计算表达式的值,根据表达式的值动态拼接SQL,以此来完成动态SQL的功能. 动态标签用法 1.if If : 当参数满足条件才会执行某个条件

随机推荐