[NDK开发]Android JNI开发之动态注册

NDK  2022年5月29日 am8:08发布2年前 (2022)更新 91es.com站长
71 0 0

前言

我们第一篇文章 [NDK开发]Android JNI 开发之第一个 JNI实例就是静态注册的。

除了有静态注册,还有动态注册

静态注册

静态注册:先由Java得到本地方法的声明,然后再通过JNI实现该声明方法。

  1. 优点: 理解和使用方式简单, 属于傻瓜式操作, 使用相关工具按流程操作就行, 出错率低
  2. 缺点: 当需要更改类名,包名或者方法时, 需要按照之前方法重新生成头文件, 灵活性不高

动态注册

动态注册:动态注册JAVA的Native方法,使得c/c++里面方法名 可以和 java 的Native方法名可以不同, 动态注册是将将二者方法名关联起来,以后在修改Native方法名时,只需修改动态注册关联的方法名称即可

  1. 优点: 灵活性高, 更改类名,包名或方法时, 只需对更改模块进行少量修改, 效率高
  2. 缺点: 对新手来说稍微有点难理解, 同时会由于搞错签名, 方法, 导致注册失败

正文

下面简单记录一下动态注册的使用。

系统初始化JNI在加载时,会调用JNI_OnLoad(),而卸载时会调用JNI_UnLoad();所以,我们可以通过重写JNI_OnLoad(),在JNI_OnLoad()中将函数注册到Android中,以便能通过Java访问。

第一步

MyDynamic.java代码

package com.biumall.mydynamic2;

public class MyDynamic {

    static {
        System.loadLibrary("MyDynamic");
    }

    /***
     *   1. javac MyDynamic2.java
     *   2. javap -s MyDynamic2.class
     *
     *  public static native java.lang.String StringFormJNI();
     *     Signature: ()Ljava/lang/String;
     *
     *   public static native void StringToJNI(java.lang.String);
     *     Signature: (Ljava/lang/String;)V
     *
     */

    public static native String StringFormJNI();
    public static native void StringToJNI(String message);
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    String TAG= "Dynamic_Android_Log";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d(TAG, "onCreate  : "+ MyDynamic.StringFormJNI());
        MyDynamic.StringToJNI("Hello from Android.");
    }
}

第二步

MyDynamic.c

#include<string.h>
#include<jni.h>
#include<stdio.h>
#include <android/log.h>

#define LOG_TAG "Dynamic_JNI_Log"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#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 ARRAY_LENGTH(x) ((int)(sizeof(x) / sizeof((x)[0])))
//定义MyDynamic.java类目录(就是包名+类名,只不过.换成了/)
#define DYNAMIC_CLASS "com/biumall/mydynamic2/MyDynamic"

// 定义native 实现的StringFormJNI
JNIEXPORT jstring JNICALL native_StringFormJNI(JNIEnv *env , jclass clasz) {
    LOGD("--------native_StringFormJNI----------");
    return (*env)->NewStringUTF(env, "Hello form JNI.");
}

// 定义native 实现的StringToJNI
JNIEXPORT void JNICALL native_StringToJNI(JNIEnv *env, jclass clasz, jstring message){
     LOGD("native_StringToJNI===%s",(*env)->GetStringUTFChars(env, message, 0));
}

//定义JavaJNI函数的绑定表
// 方法数组,分别为:
// 方法名 | 方法签名 | 函数指针
// 可以通过javacjavap 获取 方法签名
JNINativeMethod method_table[]= {
 {"StringFormJNI", "()Ljava/lang/String;", (void*) native_StringFormJNI},
 {"StringToJNI", "(Ljava/lang/String;)V", (void *) native_StringToJNI},
};


int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod * methods, int methods_size){
    jclass clazz = NULL;
    //反射Java
    clazz = (*env)->FindClass(env, className);
    if(NULL == clazz){
        return JNI_ERR;
    }
    int result = (*env)->RegisterNatives(env, clazz, methods, methods_size);
    LOGD("--------registerNativeMethods---------- result : %d", result);
    if(result < 0){
        return JNI_ERR;
    }
    return JNI_OK;
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved){
    JNIEnv * env = NULL;
    int result = (*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6);
    LOGD("--------JNI_OnLoad----------1 result : %d", result);
    if(result != JNI_OK){
        return JNI_ERR;
    }
    result = registerNativeMethods(env , DYNAMIC_CLASS, method_table, ARRAY_LENGTH(method_table));
    LOGD("--------JNI_OnLoad----------2 result : %d", result);
    if(result != JNI_OK){
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

第四步

Android.mk和Application.mk,这个代码跟之前一样的。

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_LDLIBS := -llog

LOCAL_MODULE    := MyDynamic
LOCAL_SRC_FILES := MyDynamic.c

include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := all

第五步

在工程中的build.gradle,新增如下内容:

android {
    compileSdkVersion 31

    // 

    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
}

参考文章

  1. Android JNI和NDK学习(03)--动态方式实现JNI
  2. [NDK开发]Android JNI 开发之第一个 JNI实例
  3. JNI静态注册和动态注册区别
  4. 你应该了解的JNI知识(一)——静态注册与动态注册

 历史上的今天

  1. 2023: Android广播在子线程中运行(0条评论)
  2. 2021: 哑孩子:第一次来到人间(0条评论)
  3. 2019: Android (/sys/路径)属性文件节点值的读写(0条评论)
  4. 2019: [摘]Android IO流读写文件实例2(0条评论)
  5. 2019: [摘]Android IO流读写文件实例(0条评论)
版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站内容: 部分来源于网络,仅供学习和参考,若侵权请留言
3、 本站申明: 个人流水账日记,内容并不保证有效

暂无评论

暂无评论...

随机推荐

adb push有中文名的apk或者路径会出现名字不全

前言最近在调试时,push的apk目录中带有中问题,就存在push的apk后缀丢失正文偶然apk中带有中文或者存在的路径中带有中,由于疏忽,push后发现apk没有正常启动,最后发现,push的文件存在问题。如下。adb push Desktop\中国125la.apk /sdcard...

Android Studio工程中.idea没有*.iml文件

前言我使用其他同事电脑时,Android Studio(Android Studio Dolphin | 2021.3.1 Patch 1)的版本新建的工程中.idea目录没有对应module和*.iml。百度或谷歌后解决了。记录一下,方便自己查阅。正文解决这个问题很简单,就是Androi...

[摘]Java中String首字母大写方法

摘抄于《Java中String首字母大写方法》,最近自己也写过,使用的很普通的方法。传统思路是:先将String 的首字母单独接取下来转成大写,然后再拼接上剩余字符串。public String upperCase(String str) { return str.substring...

叔本华:比读书更重要的是什么

不管任何藏书丰富的图书馆,假如不加整顿杂乱无章的话,它给予我们的利益还不如那些规模小藏书少,但整理得条理井然、分类清楚的图书馆,同理,不管你学识如何的渊博,如若不能反复思维咀嚼消化的话,它的价值,远逊于那些所知不多但能予以深思熟虑的知识。何以言之?因为我们若要将所学得的知识消化吸收,变为己有,并且能...

我常用的Monkey命令

Monkey的概念“猴子测试”是指没有测试经验的人甚至对计算机根本不了解的人(就像猴子一样)不需要知道程序的任何用户交互方面的知识,如果给他一个程序,他就会针对他看到的界面进行操作,其操作是无目的的、乱点乱按的。这种测试方式在产品周期中的早期阶段会找到很多很好的bug,为用户节省不少的时间。...

[摘]WindowManager.LayoutParams的各种flag含义

FLAG_ALLOW_LOCK_WHILE_SCREEN_ON只要这个window对用户是可见的,则允许在屏幕开启的时候锁定屏幕这个flag可以单独的使用,也可以配合FLAG_KEEP_SCREEN_ON和(或者) FLAG_SHOW_WHEN_LOCKED使用FLAG_DIM_BEHIND...