Android NDK实例详解

本文详细介绍如何在Android环境中使用Java调用C++代码,包括创建C++文件、编写Java调用逻辑、编译C++代码生成动态链接库、集成JNI库到项目以及调试信息的打印方法。

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


> 创建步骤:

1)在 app/src 的目录下新建 Jni 文件夹用来写C++代码



     java调用C++的代码,需要把C++的代码编译成动态库,然后java调用,需要遵循JNI规范。
那么这样C++的函数名称就要遵循JNI的规范。

2)在main/java/包名 的目录下新建java文件Jni,用来调用C++函数



3)写java文件


4)写C++文件

hellloworld.cpp
#include <jni.h>
#include <stdio.h>

void foo()
{
    printf("hello world\n");
}

// 这个函数名是JNI规范的
// env表示Java虚拟机
// obj表示Java对象,在具体实现时表示调用这个函数的对象
// extern 要求编译器不要对这个函数进行表名,否则java虚拟机找不到该函数
extern "C" void Java_com_example_administrator_myapplication_Jni_foo(JNIEnv *env, jobject obj)
{
    // 在这个里面可以调用自己想要写的C++代码
    foo();
}


5)用NDK来编译C++代码,使其能够生成java能够调用的动态库(makefile)

     在app/src/Jni (C++代码同级目录)目录下新建一个Android.mk 文件用来编译C++代码

Android.mk

# 设置路径是当前路径
LOCAL_PATH:=$(call my-dir)
# 清除变量
include $(CLEAR_VARS)

# 需要参与编译的源代码文件
LOCAL_SRC_FILES:= helloworld.cpp

# 目标文件 要生成的动态库
LOCAL_MODULE:= mylib # libmylib.so

# andriod log库
LOCAL_LDLIBS += -llog

# 目标文件的类型(表示要生成动态库)
include $(BUILD_SHARED_LIBRARY)

# BUILD_STATIC_LIBRARY


6)运行Android.mk文件 在当前文件目录cmd下:ndk-build,然后在libs文件夹里面有生成的
相应的动态库。

7)在java文件的的Jni类中要加载前面生成的动态库



 8)这个时候java文件还是不能调用C++文件,原因是接下来还要把JNI的库加入到项目工程里面去

> 右键项目(Android模式)> Link C++ Project with Gradle

Gradle: 可以看做是C++的makefile,是用来管理和构建java程序的



至此java文件的Jni类中可以调用foo函数了。

9)接着要生成Android应用程序的包,工具栏>Build>Build APK


10)打印调试信息,先编写C++文件

#include <jni.h>
#include <stdio.h>
// 如果希望打印调试信息
#include <Android/log.h>

static const char *tag = "mylibtag";

//为了方便打印
#define MyLog(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, tag, fmt, ##__VA_ARGS__);

void foo()
{
    //__android_log_print(ANDROID_LOG_ERROR, tag, "helloworld: %d", 100);
    MyLog("hello world: %d", 100);

    printf("hello world\n");
}

// 这个函数名是JNI规范的
// env表示Java虚拟机
// obj表示Java对象,在具体实现时表示调用这个函数的对象
// extern 要求编译器不要对这个函数进行表名,否则java虚拟机找不到该函数
extern "C" void Java_com_example_administrator_myapplication_Jni_foo(JNIEnv *env, jobject obj)
{
    // 在这个里面可以调用自己想要写的C++代码
    foo();
}

extern "C" jint Java_com_example_administrator_myapplication_Jni_add(JNIEnv *env, jobject obj, jint a, jint b)
{
    MyLog("add is = %d", a + b);
    return a + b;
}

java文件 Jni类:



要在Activity页面调用函数:

Jni jni = new Jni();
jni.foo();
jni.add(10, 20);

11)如果要打印出字符串的话

extern "C" void Java_com_example_administrator_myapplication_Jni_print(JNIEnv *env, jobject obj, jstring content)
{
    // 要把java的字符串对象转换成C语言的字符串
    MyLog("print content is %s", J2C(env, content).c_str());
}

// 要加这个函数
string J2C(JNIEnv* env, jstring jstr)
{
    string ret;

    // jstr ==> ret
    // 调用Java的函数实现该功能:C++怎么调用Java函数
    // C++中的字符串用UTF8格式,Java是Unique格式

    // C++调用Java函数
    // 1. 获得函数的类(String)
    // 2. 获得函数地址(getBytes)
    // 3. 调用该函数
    jclass stringClass = env->FindClass("java/lang/String");

    // byte[] getBytes(String)
    jmethodID getBytesID = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");

    jstring arg = env->NewStringUTF("UTF-8");

    // 调用函数
    // byte[]
    // data中保存的是UTF-8编码的字符串
    jbyteArray data = (jbyteArray)env->CallObjectMethod(jstr, getBytesID, arg);

    // 获取data的长度,将data内容获取出来,拷贝到一个buff中

    // 从jbytes中,提取UTF8格式的内容
    jsize byteLen = env->GetArrayLength(data);
    jbyte* JBuffer = env->GetByteArrayElements(data, JNI_FALSE);

    // 将内容拷贝到C++内存中
    if(byteLen > 0)
    {
        char* buf = (char*)JBuffer;
        copy(buf, buf+byteLen, back_inserter(ret));
    }

    // 释放
    env->ReleaseByteArrayElements(data, JBuffer, 0);

    return ret;
}



> C++文件 helloworld.cpp

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

using namespace std;

static const char* tag = "mylibtag";

#define MyErr(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, tag, fmt, ##__VA_ARGS__)
#define MyWarn(fmt, ...) __android_log_print(ANDROID_LOG_WARN, tag, fmt, ##__VA_ARGS__)

void foo()
{
    //__android_log_print(ANDROID_LOG_ERROR, tag, "helloworld %d", 100);
    MyErr("hello world %d", 100);
}

string J2C(JNIEnv* env, jstring jstr)
{
    string ret;

    // jstr ==> ret
    // 调用Java的函数实现该功能:C++怎么调用Java函数
    // C++中的字符串用UTF8格式,Java是Unique格式

    // C++调用Java函数
    // 1. 获得函数的类(String)
    // 2. 获得函数地址(getBytes)
    // 3. 调用该函数
    jclass stringClass = env->FindClass("java/lang/String");

    // byte[] getBytes(String)
    jmethodID getBytesID = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");

    jstring arg = env->NewStringUTF("UTF-8");

    // 调用函数
    // byte[]
    // data中保存的是UTF-8编码的字符串
    jbyteArray data = (jbyteArray)env->CallObjectMethod(jstr, getBytesID, arg);

    // 获取data的长度,将data内容获取出来,拷贝到一个buff中

    // 从jbytes中,提取UTF8格式的内容
    jsize byteLen = env->GetArrayLength(data);
    jbyte* JBuffer = env->GetByteArrayElements(data, JNI_FALSE);

    // 将内容拷贝到C++内存中
    if(byteLen > 0)
    {
        char* buf = (char*)JBuffer;
        copy(buf, buf+byteLen, back_inserter(ret));
    }

    // 释放
    env->ReleaseByteArrayElements(data, JBuffer, 0);

    return ret;
}

// JNI规范规定的
// env表示Java虚拟机
// obj表示Java对象
// extern C要求编译器不要对该函数进行变名,否则java虚拟机找不到该函数
void Java_cn_itcast_firstproject_Jni_foo(JNIEnv* env, jobject obj)
{
    foo();
}

extern "C" jint Java_cn_itcast_firstproject_Jni_add(JNIEnv* env, jobject obj, jint a, jint b)
{
    MyErr("add is called %d", a+b);
    return a + b;
}

extern "C" void Java_cn_itcast_firstproject_Jni_print(JNIEnv* env, jobject obj, jstring content)
{
    // 把Java的字符串对象转换成C类型字符串
    MyErr("content is %s", J2C(env, content).c_str());
}


这里的string头文件会显示没有加载,那么需要在C++文件同目录下新建Application.mk

内容为:
APP_STL := stlport_static


> java文件 Jni.java

package com.example.administrator.myapplication;

public class Jni {

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

    // native 关键字表示foo函数不是由java实现,而是由C来实现的
    // 当这个函数被调用时,java虚拟机会寻找C语言写的foo函数,进行调用
    // Jni规范中,java调用C++的代码的文件名为:Java_包名_类名_函数名
    // Java_com_example_administrator_myapplication_Jni_foo();
    public native void foo();

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

    public native void print(String content);

}






















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值