• jni与ndk之简单使用


     先介绍环境:

    window:win7 32位 jdk1.7

    ubuntu 11.04 64位 jdk1.6 ndk-r8b

      

    JNI(Java Native Interface)----java本地接口,它的好处是:允许java代码在java虚拟机里面相互操作使用其他语言(例如C、C++、汇编等等)编写的类库或者应用程序.

       什么时候用:当你的应用程序用java编写的时候没有办法完成所有的功能的时候,就要用到JNI了.(比如你需要在应用层驱动底层的硬件工作)

    在此先介绍使用javah工具、arm-linux-gcc等工具生成.so的共享库文件,提供给上层的android应用.(需要window和ubuntu环境)

         1.先在eclipse中新建一个NdkC的android工程,修改MainActivity.java如下

    package com.undergrowth.ndkc;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.widget.TextView;
    
    public class MainActivity extends Activity {
    
    	private TextView textView;
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		textView=(TextView) findViewById(R.id.text);
    		ArithOpera add=new ArithOpera();
    		int result=add.add(10, 20); //调用本地方法
    		textView.append("
     10+20="+result);
    	}
    }
    


    提供本地算术运算的ArithOpera.java如下

      

    package com.undergrowth.ndkc;
    /*
     * 提供一个本地方法 计算两数之和
     */
    public class ArithOpera {
    	//本地加法方法
    	public native int add(int a,int b);
    	
    	//加载共享库名为libarith.so  
    	//遵循Unix的习惯  前缀lib 和后缀.so都不加 系统会自动加上
    	static{
    		System.loadLibrary("arith");
    	}
    
    }
    


      2.使用javah工具生成含有本地方法的头文件

         在cmd下进入到上面NdkC的目录下,如我的

           

       输入javah -classpath ./bin/classes/ com.undergrowth.ndkc.ArithOpera

    就会在NdkC的目录下生成含有本地方法的头文件com_undergrowth_ndkc_ArithOpera.h

       内容许下:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_undergrowth_ndkc_ArithOpera */
    
    #ifndef _Included_com_undergrowth_ndkc_ArithOpera
    #define _Included_com_undergrowth_ndkc_ArithOpera
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_undergrowth_ndkc_ArithOpera
     * Method:    add
     * Signature: (II)I
     */
    JNIEXPORT jint JNICALL Java_com_undergrowth_ndkc_ArithOpera_add
      (JNIEnv *, jobject, jint, jint);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    


     

    对上面的文件简单解释一下:

    #include <jni.h>   包含jni.h头文件 jni.h头文件声明/定义了在c语言中使用的数据类型 接口函数 宏之类的 位于 Javajdk1.7.0_01include目录下

    #ifndef _Included_com_undergrowth_ndkc_ArithOpera

    #define _Included_com_undergrowth_ndkc_ArithOpera 

    #endif

    这两句还有最后面的一个#endif,用于保证头文件在预编译阶段只被包含一次

    #ifdef __cplusplus

    extern "C" {

    #endif

    .....

    #ifdef __cplusplus

    }

    #endif

    这几句代码是说如果是c++源文件,则使用c语言的方式进行编译和链接

    中间的

    /*
     * Class:     com_undergrowth_ndkc_ArithOpera
     * Method:    add
     * Signature: (II)I
     */
    JNIEXPORT jint JNICALL Java_com_undergrowth_ndkc_ArithOpera_add(JNIEnv *, jobject, jint, jint);

    是利用jni本地方法的命名规则生成的一个方法声明,规则如下

    3.上面的操作都是在window环境下,接着在ubuntu11.04环境下使用交叉编译器生成.so文件

        将com_undergrowth_ndkc_ArithOpera.h拷贝到ubuntu环境中的 在com_undergrowth_ndkc_ArithOpera.h的同一目录中新建add.c文件 内容如下:

    #include <jni.h>
    #include "com_undergrowth_ndkc_ArithOpera.h"
    
    JNIEXPORT jint JNICALL Java_com_undergrowth_ndkc_ArithOpera_add(JNIEnv *env,jobject obj,jint a,jint b)
    {
    	return (a+b);
    }
    


       使用arm-linux-gcc进行编译 arm-linux-gcc  -I ~/java/jdk1.6.0_30/include/ -I ~/java/jdk1.6.0_30/include/linux/ -fPIC -c add.c -o add.o

          上面命令生成add.o的目标文件

     -I 用于指点add.c中用到的jni.h头文件的位置 第二个-I是因为jni.h中包含jni_md.h 而jni_md.h则在第二个位置

    -fPIC(位置独立代码)用于指定生成与位置无关的代码,即在内存的任意地方都可以运行

    上面生成add.o的文件后,在使用arm-linux-ld进行链接,生成.so的文件  arm-linux-ld -share add.o -o libarith.so

    即生成了libarith.so的共享库 将之拷贝到NdkC的libs/armeabi/目录下  没有该目录则创建即可 该目录名不可为其他的

    4.运行 效果如下

    现在介绍使用ndk的方式达到上面的一样效果.下面的操作都是在ubuntu 11.04的环境中

    先对ndk做个简单的介绍:ndk相当于是jni的超集,

    主要做这么两件事: a.利用Android.mk生成一个jni兼容的共享库

                                     b.将生成的共享库复制到你项目的libs/armeabi/目录下

    1.在eclipse中新建一个ndk1的android工程,将Ndk1Activity.java修改如下

    package com.undergrowth.ndk;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.TextView;
    
    public class Ndk1Activity extends Activity {
        /** Called when the activity is first created. */
        private TextView textView;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            textView=(TextView) findViewById(R.id.text);
            textView.append("
     10+20="+add(10,20));
        }
        
        public  native int add(int a,int b);
        static{
            System.loadLibrary("arith");
        }
    } 
    

    2.然后再ndk1的项目文件夹中新建jni目录(与src目录在同一级别),在jni中新建add.c源文件 内容如下:

    #include <jni.h>
    
    jint Java_com_undergrowth_ndk_Ndk1Activity_add(JNIEnv *env,jobject thiz,jint a,jint b)
    {
        return (a+b);
    }
    


    同样使用上面提到的jni本地方法的命名规则 以Java_ 开头 接着是类的全名 接着是方法名 都用_(下划线)分隔

    同时在jni的目录中新建Android.mk文件 用于告诉ndk的编译系统编译什么 如何编译   内容如下:

    LOCAL_PATH    :=$(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE    :=arith
    LOCAL_SRC_FILES    :=add.c
    include $(BUILD_SHARED_LIBRARY)
    


    上面的Android.mk文件主要涉及到NDK的三个概念:

    a.模块变量-----用于向编译系统描述你的模块

    如LOCAL_PATH-----用于定位源代码在开发者目录中的位置

    LOCAL_MODULE----用于定义你要生成的模块
    LOCAL_SRC_FILES-----用于告诉编译系统你要编译的源文件

    b.宏函数---必须要使用$(call <function name>)的方式使用,并且返回文本信息

       如$(call my-dir)-----my-dir----用于返回当前文件的目录

    c.ndk提供的变量-----具有特定的功能

    include $(CLEAR_VARS)-----CLEAR_VARS---指向一个gnu的特殊脚本文件,用于清除(LOACL_XXX)变量,LOCAL_PATH除外,因为所有的编译控制文件都会被解析成一个全局文件,而全局文件里面的所有变量均为全局的

    include $(BUILD_SHARED_LIBRARY)------编译成共享库,即.so的后缀

    3.进行编译,进入到ndk1的目录中,使用ndk-build(前提是你以前配置好了ndk的环境),会有如下信息,并会在ndk1的目录中的libs/armeabi/下找到libarith.so文件

     

    u1@u1:~/java/workspace/ndk1$ ndk-build
    Compile thumb  : arith <= add.c
    SharedLibrary  : libarith.so
    Install        : libarith.so => libs/armeabi/libarith.so
    


    4.运行

     

          上面即是jni与ndk操作的简单实现.其实还可以使用JNI_OnLoad的方式进行编写,在之前的使用中,我倒是两种方式都用,用JNI_OnLoad主要是我觉得可以自己在C组件中定义函数名,只需要通过RegisterNatives进行注册即可,即把C组件中的函数与上层应用的函数进行一一的映射.

         JNI_OnLoad的具体使用,参见: http://blog.csdn.net/undergrowth/article/details/9163745

  • 相关阅读:
    SMC状态机笔记
    Centos重启关机命令
    什么是YAML?
    什么是TOML?
    一键彻底关闭Win10自带Windows Defender杀毒软件
    【接单】找我付费定制Python工具软件或网站开发、Chrome浏览器插件、油猴脚本
    不定积分的符号定义问题
    林群:从数学谈教育
    linux没有ifconfig命令解决办法
    linux使用mitmproxy报错
  • 原文地址:https://www.cnblogs.com/liangxinzhi/p/4275608.html
Copyright © 2020-2023  润新知