`
abc20899
  • 浏览: 928972 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

jni开发之一

 
阅读更多
JNI在Android系统中有着广泛的应用。Android系统底层都是C/C++实现的,上层提供的API都是Java的,Java通过JNI调用底层的实现。比如:Android API多媒体接口MediaPlayer类,其实底层通过JNI调用libmedia库。由于JNI的存在可以让我们重用很多已经存在C/C++的库,省去了重复开发的麻烦,并且可以利用很多开源的库(Android库中就有很多开源库,比如libjpeg,libpng等等),并且让我们开发的程序更有效率(C/C++代码发挥硬件最佳性能)。

交叉编译环境
    首先是要搭建交叉编译环境,因为Java层的应用程序是和硬件无关的,JDK编译即可;但是Native C/C++代码是和硬件相关的,必须要用交叉编译器编译成特定硬件可执行代码。请根据你的硬件平台搭建你的交叉编译环境,我的MIPS平台,当然选择的是MIPS的交叉编译器,如果你是Arm的请配置自己的交叉编译器。

    我们首先编译一个Native C的helloworld程序,

Java编写的Android应用程序
    我们首先用java编写helloworld应用程序(APK),这个代码很简单创建一个HelloWorld activity。代码如下:

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class HelloWorld extends Activity {
    private static final String TAG = "HelloWorld";
	static {
		System.loadLibrary("helloworld");
	}

	private native String printJNI();

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Log.d(TAG, "Activity call JNI: " + printJNI());
    }
}


这个HelloWorld Activity非常简单,只是调用JNI接口printJNI()打印一些信息到Android logger上面。我们需要关注一下的是printJNI()的声明,有一个native的关键字,说明他是一个用native代码实现的函数,需要用JNI调用Native代码。另外注意static代码段,这段代码意思是当类HelloWorld第一次被加载的时候,加载libhelloworld.so(请注意这里写的是库名称,在Linux中共享库名为xxx共享库,文件存在形式为libxxx.so。所以loadLibrary的参数不是libhelloworld.so,而是helloworld。如果写错误了将会加载库失败,将会收到异常)。


C语言实现helloworld共享库
     接下来我们需要来完成Native 代码部分了,这里需要强调一下,Android JNI实现中为C/C++提供了两套不同的API,调用的时候需要注意,否则非常有可能你会受到一些libc库的崩溃信息,没准儿会把你整“崩溃”,呵呵!下面先实现Native C来实现helloworld库。

     如果你对Java标准JNI熟悉的话,肯定知道javah工具,可以根据java源程序,生成Native代码的头文件,如果你是在Eclipse中开发apk的话,可以在打开终端进入bin目录,然后执行:

javah com.simon.HelloWorld  javah 包名类名

你将会得到,一个头文件com_simon_Helloworld.h,这里包含有printJNI接口的C/C++声明。这个声明肯定正确,如果你把printJNI接口声明写错了,HelloWorld将找不到printJNI接口,然后产生崩溃。

     我们创建com_simon_Helloworld.c文件,并在该文件中输入:

#include <jni.h>
#define LOG_TAG "HelloWorld"
#include <utils/Log.h>

/* Native interface, it will be call in java code */
JNIEXPORT jstring JNICALL Java_com_simon_HelloWorld_printJNI(JNIEnv *env, jobject obj)
{
    LOGI("Hello World From libhelloworld.so!");

    return (*env)->NewStringUTF(env, "Hello World!");
}

/* This function will be call when the library first be load.
 * You can do some init in the libray. return which version jni it support.
 */
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    void *venv;
    LOGI("JNI_OnLoad!");

    if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed");
        return -1;
    }

     return JNI_VERSION_1_4;
}

请注意Java_com_simon_HelloWorld_printJNI函数的名字,这个名字是符合JNI规范的,Java_开头,后面紧跟着调用他类名(包含包名和类名,com_simon_HelloWorld),然后才是接口的名字printJNI。这样java虚拟机就可以在com.simon.HelloWorld类调用printJNI接口的时候自动找到这个C实现的Native函数调用。你可能注意到这个名字非常的长,作为一个函数名它非常有可能不是一个好的选择。JNI API允许你提供一个函数映射表,注册给Jave虚拟机,这样Java虚拟机就可以用函数映射表来调用相应的函数,就可以不必通过函数名来查找需要调用的函数了。这样你的函数名也可以随便定义了(可以定义最能表现函数功能的函数名),这个将会在helloworld共享库的C++实现中演示。但是Android系统中,还是推荐用JNI标准的函数名定义的。

    JNI_OnLoad函数JNI规范定义的,当共享库第一次被加载的时候会被回调,这个函数里面可以进行一些初始化工作,比如注册函数映射表,缓存一些变量等,最后返回当前环境所支持的JNI环境。本例只是简单的返回当前JNI环境。

注意网上很多例子都说可以不实现JNI_OnLoad,我发现如果不是实现的话,printJNI只可以返回整型类型的值,如果返回其他类型的值都会崩溃。并且GetEnv的调用也是必须的,否则也是崩溃,但是GetEnv的返回值我并没有用到,这些地方使我非常迷惑,期待达人解惑。

接下来编写Android.mk文件,创建之,并输入:

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

LOCAL_SRC_FILES:=com_simon_Helloworld.c

LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)

LOCAL_MODULE := libhelloworld

LOCAL_SHARED_LIBRARIES := libutils

LOCAL_PRELINK_MODULE := false

include $(BUILD_SHARED_LIBRARY)


这里面有几个标签需要说明:

1、LOCAL_C_INCLUDES说明包含的头文件,这里需要包含JNI的头文件。

2、LOCAL_MODULE当前模块的名称

3、LOCAL_SHARED_LIBRARIES当前模块需要依赖的共享库,因为在hellowold中我们调用Android打印系统输出到logger,所以我们必须要依赖libutils库。

4、LOCAL_PRELINK_MODULE指明该模块是否被启动就加载,可以参考《动态库优化——Prelink(预连接)技术》。我们的helloworld库不需要prelink,所以置为false。


LOCAL_SRC_FILES -  编译的源文件
LOCAL_C_INCLUDES -  需要包含的头文件目录
LOCAL_SHARED_LIBRARIES - 链接时需要的外部库
LOCAL_PRELINK_MODULE -
是否需要prelink处理(参考prelink的详细介绍:《动态库优化——Prelink(预连接)技术》,Android的Toolchain, prelink工具:《Android Toolchain与Bionic Libc》)
LOCAL_MODULE -    编译的目标对象
BUILD_SHARED_LIBRARY -  指明要编译成动态库。

接下来回到Android顶层目录,并执行make libhello来编译:
# cd $(YOUR_ANDROID) && make libhello
target thumb C: libhello <= development/hellolib/hellolib.c
target SharedLib: libhello (out/target/product/generic/obj/SHARED_LIBRARIES/libhello_intermediates/LINKED/libhello.so)
target Non-prelinked: libhello (out/target/product/generic/symbols/system/lib/libhello.so)
target Strip: libhello (out/target/product/generic/obj/lib/libhello.so)
Install: out/target/product/generic/system/lib/libhello.so


编译结果可得到位于out/target/product/generic/system/lib/目录的动态共享库libhello.so






使用ndk编译:
在android项目中建立一个jni目录,
把*.h与*.c两个文件拷贝到jni目录
把android项目拷贝到NDK的sample目录
然后进入jni目录,ndk-build
分享到:
评论

相关推荐

    Android Studio jni开发之经典入门demo

    Android Studio JNI开发是Android应用程序与本地C/C++代码交互的重要技术。JNI,全称为Java Native Interface,它允许Java代码和其他语言写的代码进行交互。在Android应用中,JNI常用于优化性能,利用已有的C/C++库...

    Jni开发之旅

    本示例"Jni开发之旅"将带你了解如何在Android应用中使用NDK进行JNI开发,实现Java代码调用C语言的接口函数。 首先,我们需要了解Android NDK。NDK(Native Development Kit)是Google为Android平台提供的一个工具集...

    安卓app开发之Android JNI即NDK C语言开发教程,适合初学者.zip

    在Android JNI开发中,你需要掌握以下几个关键知识点: 1. **HelloJni**:这是所有JNI学习者必经的第一步,通常包括创建一个简单的Java类,声明本地方法,并在C/C++代码中实现这些方法。通过这个例子,你可以理解...

    Android JNI开发手册

    Android JNI开发手册涵盖了使用Java本地接口(Java Native Interface,JNI)进行Android平台应用开发的核心知识。JNI是一种编程框架,允许Java代码和其他语言写的代码(主要是C或C++)进行交互。本手册是Android开发者...

    安卓jni开发资源

    - `android之JNI开发步骤总结.doc`:这份文档很可能详细介绍了JNI开发的基本流程,包括创建本地方法、编写C/C++代码、配置Android.mk和Application.mk文件、编译链接、以及在Java代码中调用本地方法等步骤。...

    JNI开发之NDK下载

    资源是个TXT文件,里面是百度云盘链接跟提取码 一共两个文件,一个32位的一个64的,每个450M左右

    JNI攻略之一――建立一个简单的JNI程序

    ### JNI攻略之一——建立一个简单的JNI程序:详细解析 #### 一、理解JNI与跨语言编程 JNI(Java Native Interface)是Java平台标准的一部分,它允许Java代码与其他语言(如C/C++)编写的代码进行交互。这在需要高...

    JNI开发相关资料

    理解JNI的基本概念、开发流程和应用实例是每个Android开发者必备的技能之一。通过阅读提供的文档和教程,如`JNI_编程技术__网文整理.doc`和`jni详解.pdf`,你将能够更深入地掌握JNI的使用,并在实践中发挥它的优势。

    JNI开发之javah生成静态和非静态的两个头文件区别代码

    在JNI开发中,我们常常需要通过`javah`工具来生成C或C++的头文件,这些头文件定义了Java层与本地层交互的函数原型。`javah`工具可以从已编译的Java类中提取方法签名,生成对应的C/C++函数原型。 在`javah`命令中,...

    JNI入门之HelloWorld(一)

    ### JNI入门之HelloWorld(一)详解 #### 一、引言 JNI(Java Native Interface)是Java平台标准的一部分,它允许Java代码与其他语言写的代码进行交互。JNI接口提供了若干公共服务,并为Java虚拟机和本机应用程序或...

    Java再说JNI之实例

    在开发JNI应用时,需要注意以下几点: - **错误处理**:JNI中的错误处理不如Java那么直观,需要谨慎处理异常情况。 - **内存管理**:Java与本地代码之间的内存管理需要协调,避免内存泄漏。 - **线程安全**:由于...

    NDK开发 之JNI 文档

    JNI(Java Native Interface)是Java平台的一个重要组成部分,它允许Java代码和其他语言写的代码进行交互。在Android开发中,NDK(Native ...通过阅读和实践,开发者可以掌握使用JNI开发Android应用的全面技能。

    尚硅谷Android高级开发技术之JNI和NDK开发

    这算是尚硅谷培训学校推出的Android视频教程的高级教程了吧,而本套教程正是在Android开发中的核心重点开发技术(JNI/NDK...本教程照顾了不会C/C++的同学,会讲一些C/C++的基本知识,是一个JNI/NDK开发不可多得的教程。

    JNI开发总结

    下面是对JNI开发的一些核心知识点的详细讲解。 1. **JNI基础概念**: - JNI接口:Java虚拟机(JVM)提供的一组C/C++接口,用于在Java代码中调用本地方法(Native Method)。 - 本地方法库(Native Library):...

    Android-在AndroidStudio2.3.2下JNI开发的详细步骤

    在Android Studio 2.3.2版本中,JNI的开发流程是开发者需要掌握的重要技能之一。以下是使用Android Studio 2.3.2进行JNI开发的详细步骤: 1. **创建项目** 首先,打开Android Studio并创建一个新的Android项目。...

    初中级Android开发社招面试之JNI.pdf

    总之,JNI和NDK是Android开发中不可或缺的部分,它们允许开发者充分利用C/C++的优势,提高代码效率,实现与硬件的紧密交互,同时也能提高程序的安全性。然而,使用时需谨慎,确保正确管理和调用,避免引入额外的复杂...

    JNI接口开发

    - **获取Env**:每个线程都有一个与之关联的`JNIEnv*`。 - **Attach和Detach**:使用`AttachCurrentThread`和`DetachCurrentThread`来管理线程与VM的关联。 - **垃圾回收**:通过`JNIEnv*`创建的对象可以在适当时候...

    JNI攻略之十一――启动虚拟机调用java类

    标题与描述均指向了一个...总之,JNI提供了一种强大而灵活的机制,允许Java与本地代码之间的交互,拓宽了软件开发的边界,特别是在需要结合不同语言优势的场景下。掌握这一技术,能够显著提升软件系统的效率和功能性。

    android之jniDemo

    在移动开发领域,Android与C/C++的交互是一个重要的技术环节,这主要得益于JNI(Java Native Interface)的存在。JNI允许Java代码与其他编程语言(如C/C++)编写的代码进行交互,使得我们可以利用C/C++的高性能特性...

    JNI设计实践之路

    ### JNI设计实践之路 #### 一、前言 在当今软件开发领域,Java作为一种广泛应用的编程语言,其灵活性和跨平台特性受到了众多开发者的青睐。然而,在某些特定场景下,如高性能计算、底层硬件访问或是集成已有C/C++...

Global site tag (gtag.js) - Google Analytics