Jni使用方法

这篇博客介绍了在Android Studio中如何配置和使用JNI。首先,讲述了下载NDK并配置环境变量的过程,然后详细讲解了静态项目配置,包括gradle设置和创建JNI类。接着,博主指导读者进行Native编译,创建C语言源文件并编写Android.mk和Application.mk。在Java层,展示了如何调用JNI方法以及添加Log。最后,博主分享了在配置过程中遇到的问题及解决办法,并简单提到了动态项目配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android stuido创建第一个Hello Jni项目,使用的是Mac

下载NDK,配置Tool

Android stuido sdk下载ndk包
在这里插入图片描述
配置环境变量
终端输入 vim ~/.bash_profile ,配置NDK和path
在这里插入图片描述
终端输入ndk-build,显示如下没找到directory为配置安装成功
在这里插入图片描述
导入 ndk包,project structure
在这里插入图片描述
Android stuido 配置ExternalTools,左下角新增如下3个选项
在这里插入图片描述
(1)jni create

Program:javah
Arguments:-v -jni -d $ModuleFileDir$/src/main/jni $FileClass$
Working directory :  $SourcepathEntry$

在这里插入图片描述
(2)ndk-build

Program:/Users/你自己的用户/Library/Android/sdk/ndk/21.1.6352462/ndk-build
Working directory : $ProjectFileDir$/app/src/main

在这里插入图片描述
(3)ndk-build-clean 与ndk-build一致,增加了clean
在这里插入图片描述

静态项目配置

1.app的gradle增加如下,hello为so文件库名称,
jniLibs.srcDir很重要,没有无法加载

android {
 defaultConfig {
 		ndk {
            moduleName "hello"
        }
    }
 buildTypes {
           sourceSets{
            main {
                jni.srcDirs = []
                jniLibs.srcDir "src/main/libs"
            }
        }
    }
}

2.根目录gradle.properties

android.useDeprecatedNdk=true

3.创建Hellojni.class
方法名getStringFromC()
以后生成so文件module名hello

package com.test.buttontest.ndkdemo;

/**
 * @author 
 * @time 2020-04-21.
 */
public class Hellojni {

    public static native String getStringFromC();

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

    public Hellojni(){}

}

Native编译

1.main下新建jni空文件夹
在这里插入图片描述
2.在刚才Hellojni,点击右键->External Tools->jni create
会自动生成上图红色com_tayh_buttontest_ndkdemo_Hellojni.h文件,为头文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_tayh_buttontest_ndkdemo_Hellojni */

#ifndef _Included_com_tayh_buttontest_ndkdemo_Hellojni
#define _Included_com_tayh_buttontest_ndkdemo_Hellojni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_tayh_buttontest_ndkdemo_Hellojni
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_tayh_buttontest_ndkdemo_Hellojni_getStringFromC
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

3.编写hello.c C语言文件,从头文件拷方法名进行修改

#include <stdio.h>
#include <stdlib.h>
#include "com_tayh_buttontest_ndkdemo_Hellojni.h"

JNIEXPORT jstring JNICALL Java_com_tayh_buttontest_ndkdemo_Hellojni_getStringFromC
  (JNIEnv * env, jclass jclass){

   return (*env)-> NewStringUTF(env,"helloword");
}

4.新增Android.mk文件

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)

#生成的库的名称

LOCAL_MODULE := hello

#库编译需要的源码文件和依赖库

LOCAL_SRC_FILES := hello.c

#告诉Ndk编译成动态库

include $(BUILD_SHARED_LIBRARY)

新增Application.mk

APP_PLATFORM := android-19
APP_ABI := all

5.在Hellojni.class,点击右键->External Tools->ndk build,
会在main目录下自动生成obj和libs文件夹,里面生成了so文件

在这里插入图片描述

Java上层调用

NdkOneActivity.class

package com.test.buttontest.ndkdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import com.tayh.buttontest.R;

/**
 * @author
 * @time 2020-04-21.
 */
public class NdkOneActivity extends AppCompatActivity {

    TextView textView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ndk_one);
        textView = findViewById(R.id.tv_show);
        try {
            String str = Hellojni.getStringFromC();
            textView.setText(str);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

增加Log

1.Android.mk

LOCAL_LDLIBS    := -lm -llog

  1. 可以定义个方法,并用C实现
public static native void callLogFromJni();

3.C中添加android/log.h,并实现LOGI

#include <android/log.h>

#define LOG_TAG "TAG"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

JNIEXPORT void JNICALL Java_com_tayh_buttontest_ndkdemo_Hellojni_callLogFromJni
  (JNIEnv * env, jclass jclass){

  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,"from jni log");
}

JNIEXPORT jstring JNICALL Java_com_tayh_buttontest_ndkdemo_Hellojni_getStringFromC
  (JNIEnv * env, jclass jclass){
  LOGI("helloword");
   return (*env)-> NewStringUTF(env,"helloword");
}

4.build.,gradle可以配置也可以不配置

android {
      ndk {
             moduleName "hello"
             ldLibs 'log'
            //   ldLibs.addAll([ 'log']);  这个写法会报空指针
        }
}
遇到问题

1.Android NDK: ERROR:jni/Android.mk:native: LOCAL_SRC_FILES should point to a file ending with “.so”

原因:Android.mk文件写错,用的rebuilt

#告诉Ndk编译成动态库

#include $(PREBUILT_SHARED_LIBRARY)

include $(BUILD_SHARED_LIBRARY)

2.No rule to make target ‘native’, needed by ‘ndk-app-local’
原因:build.gradle和Android.mk 库名都要写对

3.undefined reference to `__android_log_print’
原因:Log包导入错误,Android.mk 没有导入log

4.NDK ERROR: Cannot invoke method addAll() on null object
原因:log的gradle导入空指针ldLibs.addAll

动态项目配置

1.jni目录直接创建active.cpp get_random_num返回一个随机数

#ifndef ACTIVE_CPP
#define ACTIVE_CPP
// jni头文件
#include <jni.h>

#include <cassert>
#include <cstdlib>
#include <iostream>
using namespace std;


//native 方法实现
jint get_random_num(){
    return rand();
}

/*需要注册的函数列表,放在JNINativeMethod 类型的数组中,
以后如果需要增加函数,只需在这里添加就行了
参数:
1.java中用native关键字声明的函数名
2.签名(传进来参数类型和返回值类型的说明)
3.C/C++中对应函数的函数名(地址)
*/
static JNINativeMethod getMethods[] = {
        {"getRandomNum","()I",(void*)get_random_num}
};
//此函数通过调用RegisterNatives方法来注册我们的函数
static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* getMethods,int methodsNum){
    jclass clazz;
    //找到声明native方法的类
    clazz = env->FindClass(className);
    if(clazz == NULL){
        return JNI_FALSE;
    }
   //注册函数 参数:java类 所要注册的函数数组 注册函数的个数
    if(env->RegisterNatives(clazz,getMethods,methodsNum) < 0){
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

static int registerNatives(JNIEnv* env){
    //指定类的路径,通过FindClass 方法来找到对应的类
    const char* className  = "com/tayh/buttontest/ndkdemo/ActiveJni";
    return registerNativeMethods(env,className,getMethods, sizeof(getMethods)/ sizeof(getMethods[0]));
}
//回调函数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv* env = NULL;
   //获取JNIEnv
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);
    //注册函数 registerNatives ->registerNativeMethods ->env->RegisterNatives
    if(!registerNatives(env)){
        return -1;
    }
    //返回jni 的版本
    return JNI_VERSION_1_6;
}

#endif

2.Android.mk ,ndk-build直接创建so文件

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

#生成的库的名称

LOCAL_MODULE := active

#库编译需要的源码文件和依赖库

LOCAL_SRC_FILES :=active.cpp

LOCAL_LDLIBS  := -lm -llog

include $(BUILD_SHARED_LIBRARY)

3.ActiveJni.getRandomNum() 即可直接调用


public class ActiveJni {
    static {
        System.loadLibrary("active");
    }

    public static native int getRandomNum();

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值