android JNI的使用示例详解

这篇博客详细介绍了如何在Android中使用JNI技术。首先讲解了JNI的作用,然后通过一个实例演示了从安装NDK环境到创建Android工程,编写Java接口,生成头文件,实现C/C++源代码,编译生成.so动态链接库,最后在Java代码中调用JNI函数的全过程。

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


众所周知,android是使用java进行开发,但是在开发过程中可能会用到之前的c,c++代码,或者乳沟你想一套代码同时在iphone和android平台复用就会用到JNI啦。

JNI起的是一个桥梁的作用,可以转化java与c/c++的一些区别,比如数据类型等。之后我们将转换好的c代码生成动态链接库,供java调用。

下面以一个示例做说明,希望对刚接触的童鞋有所帮助。

1、安装NDK环境:这个可以参照我的前一篇文章“windows系统上安装与使用Android NDK r5”。这是一个童鞋写的,照着做就可以完全没有问题的安装好。

2、当你按照步骤1安装好环境后,比如安装在了D盘D:\android-ndk-r6,本文以下默认此路径,如果你的路径不同 做相应改变就好了。然后在D:\android-ndk-r6下新建apps文件夹,里面存放的就是你将来要搞的android jni 工程相关文件啦。

现在我在D:\android-ndk-r6\apps 下创建我的第一个工程目录Angry,注意这个Angry并不是你的android工程的根目录,在Angry下创建project文件,这下面存放的将是你的android工程。 以下是我的目录结构:

 

之所以这样是因为,大家可以看到在Angry下还有个Application.mk文件(下面讲这个文件的生成),这个文件里是我们将c/c++编译成动态链接库(.so)的时候需要使用到的配置信息。除这个文件外我们还会用到一个Android.mk文件,在下面会讲到。

3.好了,接下来就创建我们的android工程吧。创建Android工程,指定工程路径:D:\android-ndk-r6\apps\Angry\project,以下是我创建的工程:

              

创建好了之后就可以写我们的java接口了(相当于函数声明),真正的功能实现是要用c/c++实现的吆。你可以新建一个java文件,或者直接在你继承Activity的那个java中直接写,我是新建了一个Jni.java文件:

                                                               

Jni.java中的代码如下:
package com.Angry;

public class Jni {
	public native String getString ();
}

注意接口要声明成native的吆。如果你不想单独放在一个文件中也可以直接放在MainAct.java中,将 public native String getString (); 放进去就好了。

4.生成头文件以及源文件:

将Jni.java复制到D:\android-ndk-r6\apps\Angry\project\bin下面,然后在D:\android-ndk-r6\apps\Angry\project\bin用“javac jni.java”编译生成Jni.class文件,将生成的Jni.class复制到 D:\android-ndk-r6\apps\Angry\project\bin\com\Angry下覆盖掉原先的Jni.class文件。

然后再在D:\android-ndk-r6\apps\Angry\project\bin下使用命令 "javah -jni com.Angry.Jni" 这是将会在bin下生成一个com_Angry_Jni.h的头文件,大家可以看一下我的生成:、

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

#ifndef _Included_com_Angry_Jni
#define _Included_com_Angry_Jni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_Angry_Jni
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_Angry_Jni_getString
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

其实就是将我们原先Jni.java中的public native String getString ();函数声明形式做了些许改动,使之符合JNI规则。

之后我们要在D:\android-ndk-r6\apps\Angry\project下即Android工程下新建jni文件夹,将com_Angry_Jni.h文件放入其中。这个jni文件夹将是盛放我们c/c++源文件以及头文件和Android.mk文件的场所。

接下来就是实现我们的函数功能了:新建c/c++源文件:com_Angry_Jni.c(同样放在jni文件夹下):

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

JNIEXPORT jstring JNICALL Java_com_Angry_Jni_getString
  (JNIEnv *env, jobject thiz) {
 return (*env)->NewStringUTF(env, "I,M ANGRY");
}

像我就只是单纯的实现了返回一字符串的功能,具体功能大家可以自己添加。程序中的jstring 等数据类型其实jni对c/c++的数据类型做了形式的改变,jstring 就是String。

现在功能都实现好了,我们可以将上面的c代码编译成so文件供java调用(因为android是基于linux的,而linux的动态链接库文件是以.so为后缀,windows以.dll为后缀)。

5、编译生成.so文件需要两个.mk文件,就是我们上面提到的Application.mk文件和Android.mk,在D:\android-ndk-r6\apps\Angry\project\jni下创建Android.mk,内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := Angry
LOCAL_SRC_FILES := com_Angry_Jni.c

include $(BUILD_SHARED_LIBRARY)


LOCAL_PATH: 表示位于工程的根目录。

include $(CLEAR_VARS) 功能还不太清楚我。

LOCAL_MODULE := Angry 这个表明你的一个动态链接库的名字,所以你的链接库生成后会叫libAngry.so,libxxx.so是系统加的,xxx即此处的名字。

LOCAL_SRC_FILES := com_Angry_Jni.c 这是你要编译的源文件。

还有好多配置指令,有兴趣大家可以自己查。

然后再在D:\android-ndk-r6\apps\Angry 下创建Applicetion.mk文件:

APP_PROJECT_PATH := $(call my-dir)/project
APP_MODULES := Angry

APP_MODULES := Angry  指明了你的应用模块。

6、到此为止,我们都把准备工作做好了,接下来生成.so文件:

启动cygwin,进入NDK根目录:D:\android-ndk-r6,运行命令:make APP=Angry   如果显示:

                                                                                        

则表示.so生成并安装成功,你可以看到在 D:\android-ndk-r6\apps\Angry\project 下多了一个两层目录libs\armeabi\ 下面放着我们生成的libAngry.so。

7、到此为止,基本大功告成,下面我们验证一下c函数的功能能否实现:

在MainAct.java中实现如下简单代码:

package com.Angry;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MainAct extends Activity {
 private TextView tv;
    /** Called when the activity is first created. */
 static
 {
  System.loadLibrary("Angry");
 }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Jni jni = new Jni();
        tv = (TextView)findViewById(R.id.tv);
        
        tv.setText(jni.getString());
    }
}

运行下,看看效果:


 
已经正确调用了呀。大家试试吧!微笑

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值