Android中JNI调用–文件操作

JNI 是 Java Native Interface 的缩写,译为 Java 本地接口。它允许 Java 代码和其他语言编写的代码进行交互。在android 中提供 JNI 的方式,让 Java 程序可以调用 C/C++语言程序。 android 中很多 Java 类都具有 native 接口,这些接口由本地实现,然后注册到系统中。


1、开发环境

Windows xp sp3 +MyEclipse 8.6+android2.3.3+jdk1.6+Android-ndk-r6b

2、JNI概述

JNI 是 Java Native Interface 的缩写,译为 Java 本地接口。它允许 Java 代码和其他语言编写的代码进行交互。在android 中提供 JNI 的方式,让 Java 程序可以调用 C/C++语言程序。 android 中很多 Java 类都具有 native 接口,这些接口由本地实现,然后注册到系统中。在android系统中实现JNI库需要连接.so共享库,如:lib<文件名>.so。

3、Android NDK概述

Android NDK是一个工具集,让你的Android应用程序里可以内嵌使用本地代码(C/C++)的组件。
Android应用程序运行在Dalvik虚拟机中。NDK可以让你使用C/C++这样的本地代码语言来实现你的应用程序中某些部分。这对某类程序是有帮助的,比如需要重用已有的C代码,或者为了提高运行速度

NDK 提供:
– 编译文件和工具集,用来将你的C/C++源文件编译成本地库。
– 提供一种方式,将对应的本地库内嵌到应用程序包文件(.apk)中,最终发布到Android设备中。
– 本地系统头文件和库,这些头文件和库从Android1.5开始往后都是被支持的。但使用本地活动(native activity)的程序只能运行在Android 2.3或更高的系统中。
– 文档、示例、指南。

4、JNI调用流程图


5、java调用Native

Android虚拟机允许你的应用程序源代码通过JNI调用实现本地代码的方法,需要在应用程序中使用关键字native声明一个或多个方法表明该方法是通过本地调用实现的,如:

public native static int FileOpen(StringpFileName,int openMode);
public native static int FileLength(int fp);
public native static int FileSeek(int fp,int offset,int origin);
public native static CusBuffer FileRead(int fp,int nCount);
public native static int FileWrite(int fp,byte[] buf,int nCount);
public native static int FileClose(int fp);

除了声明native方法以外还必须为这些方法实现提供本地共享库,该共享库最终会被打包到.apk文件中,这些共享库需要更具标准的unix公约来命名lib<文件名>.so,如:libJNI_FileSys.so,其中JNI_FileSys使我们需要加载的库名。在应用程序中加载共享库的方法为:

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

注:这里使用的文件名不需要lib前缀以及.so后缀名。

FileSys.java完整代码实现:

package com.luoxudong.jni.reader;
import com.luoxudong.jni.bean.CusBuffer;
    
/********************************************************************
* [Summary]
    
*   TODO 文件操作类
* [Remarks]
*   TODO 请在此处详细描述类的功能、调用方法、注意事项、以及与其它类的关系.
 ********************************************************************/
public class FileSys {
static{
    System.loadLibrary("JNI_FileSys");
}
    
/**
 *
 * [Summary]
 *   MjFileOpen 打开文件
 * @param strFileName 文件名
 * @param openMode 打开类型
 * @return 结果
 *
 */
public int MjFileOpen(String strFileName,int openMode){
    return FileOpen(strFileName, openMode);
}

/**
 *
 * [Summary]
 *   MjFileLength 计算文件长度
 * @param fp 文件句柄
 * @return 文件长度
 *
 */
public int MjFileLength(int fp){
    return FileLength(fp);
}
    
/**
 *
 * [Summary]
 *   MjFileSeek 文件seek操作
 * @param fp 文件句柄
 * @param offset 读取数据偏移量
 * @param origin 开始位置指针
 * @return
 *
 */
public int MjFileSeek(int fp,int offset,int origin){
	return FileSeek(fp, offset, origin);
} 

/**
 *
 * [Summary]
 *   MjFileRead 读取文件数据
 * @param fp 文件句柄
 * @param nCount 读取字节数
 * @return 实际读取字节数
 *
 */
public CusBuffer MjFileRead(int fp,int nCount){
   return FileRead(fp, nCount);
}

/**
 *
 * [Summary]
 *   MjFileWrite 写文件
 * @param fp 文件句柄
 * @param buf 写数据buffer
 * @param nCount 需要写入的字节数
 * @return 实际写入字节数
 *
 */
public int MjFileWrite(int fp,byte[] buf,int nCount){
    return FileWrite(fp, buf, nCount);
}

/**
 *
 * [Summary]
 *   MjFileClose 关闭文件
 * @param fp 文件句柄
 * @return 关闭文件状态
 *
 */
public int MjFileClose(int fp){
	return FileClose(fp);
}

//本地调用
public native static int FileOpen(String pFileName,int openMode);
public native static int FileLength(int fp);
public native static int FileSeek(int fp,int offset,int origin);
public native static CusBuffer FileRead(int fp,int nCount);
public native static int FileWrite(int fp,byte[] buf,int nCount);
public native static int FileClose(int fp);
}

6、实现本地方法调用接口

为了方便我们可以使用javah命令先生成对应C/C++语言中的.h然后再实现这些函数。FileSys.java编译成FileSys.class文件后,使用命令(当前目录为工程bin目录下)javah -jni com.luoxudong.jni.reader.FileSys,此时会在bin目录下生成一个.h文件,文件名格式如下:com_luoxudong_jni_reader_FileSys.h,为了方便本人把文件名改成JNI_FileSys.h。
JNI_FileSys.h代码:

/* DO NOT EDIT THISFILE - it is machine generated */
#include<jni.h>
/* Header for classcom_meijin_dict_reader_FileSys */
    
#ifndef_Included_com_meijin_dict_reader_FileSys
#define_Included_com_meijin_dict_reader_FileSys
#ifdef __cplusplus
extern"C" {
#endif
    /*
    * Class:com_meijin_dict_reader_FileSys
    * Method:   FileOpen
    * Signature: (Ljava/lang/String;I)I
    */
    JNIEXPORT jintJNICALL Java_com_meijin_dict_reader_FileSys_FileOpen
      (JNIEnv *, jclass, jstring, jint);
    
    /*
    * Class:com_meijin_dict_reader_FileSys
    * Method:   FileLength
    * Signature: (I)I
    */
    JNIEXPORT jintJNICALL Java_com_meijin_dict_reader_FileSys_FileLength
      (JNIEnv *, jclass, jint);
    
    /*
    * Class:com_meijin_dict_reader_FileSys
    * Method:   FileSeek
    * Signature: (III)I
    */
    JNIEXPORT jintJNICALL Java_com_meijin_dict_reader_FileSys_FileSeek
      (JNIEnv *, jclass, jint, jint, jint);
    
    /*
    * Class:com_meijin_dict_reader_FileSys
    * Method:   FileRead
    * Signature:(II)Lcom/meijin/dict/bean/CusBuffer;
    */
    JNIEXPORT jobjectJNICALL Java_com_meijin_dict_reader_FileSys_FileRead
      (JNIEnv *, jclass, jint, jint);
    
    /*
    * Class:com_meijin_dict_reader_FileSys
    * Method:   FileWrite
    * Signature: (I[BI)I
    */
    JNIEXPORT jintJNICALL Java_com_meijin_dict_reader_FileSys_FileWrite
      (JNIEnv *, jclass, jint, jbyteArray, jint);
    
    /*
    * Class:com_meijin_dict_reader_FileSys
    * Method:   FileClose
    * Signature: (I)I
    */
    JNIEXPORT jintJNICALL Java_com_meijin_dict_reader_FileSys_FileClose
      (JNIEnv *, jclass, jint);
    
    #ifdef __cplusplus
    }
    #endif
    #endif

其中JNIEXPORT和JNICALL两个宏是JNI的关键字,表示该函数需要被JNI调用,而jint,jstring,jbyteArray是以JNI为中介使JAVA中对应类型与本地类型对接的类型,jobject为需要返回的java对象,类型对应表如下:

7、实现本地方法调用接口

根据对应的.h文件实现其接口。
JNI_FileSys.c代码:

#include"JNI_FileSys.h"
    #define LOG_TAG"JNI_FileSys"
    #define  LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
    #define  LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    #include<android/log.h>
    #include"FileSys.h"
    
    /*
    * Class:com_luoxudong_jni_reader_FileSys
    * Method:   FileOpen
    * Signature: ([BI)I
    */
    JNIEXPORT jintJNICALL Java_com_luoxudong_jni_reader_FileSys_FileOpen
      (JNIEnv *env, jclass jobj, jstring pFileName,jint openMode)
    {
      UINT8 *pbyFileName = (UINT8*)(*env)->GetStringUTFChars(env, pFileName, 0);
      LOGI("file name:%s---opentype:%d", pbyFileName, openMode);
      return FileOpen(pbyFileName,openMode);
    
    }
    
    /*
    * Class:com_luoxudong_jni_reader_FileSys
    * Method:   FileLength
    * Signature: (I)I
    */
    JNIEXPORT jintJNICALL Java_com_luoxudong_jni_reader_FileSys_FileLength
      (JNIEnv *env, jclass jobj, jint fd)
    {
      return FileLength(fd);
    }
    
    /*
    * Class:com_luoxudong_jni_reader_FileSys
    * Method:   FileSeek
    * Signature: (III)I
    */
    JNIEXPORT jintJNICALL Java_com_luoxudong_jni_reader_FileSys_FileSeek
      (JNIEnv *env, jclass jobj, jint fd, jintoffset, jint origin)
    {
    
      return FileSeek(fd, offset,origin);
    }
    
    /*
    * Class:com_luoxudong_jni_reader_FileSys
    * Method:   FileRead
    * Signature:(II)Lcom/luoxudong/jni/bean/CusBuffer;
    */
    JNIEXPORT jobjectJNICALL Java_com_luoxudong_jni_reader_FileSys_FileRead
      (JNIEnv *env, jclass jobj, jint fd, jintcount)
    {
      int nReadLen = 0;
      UINT8 *pBuf = (UINT8*)malloc(count);
      memset(pBuf, 0, count);
    
      nReadLen = FileRead(fd, pBuf,count);
    
      jbyte *pBy = (jbyte *)pBuf;
      jbyteArray jarray =(*env)->NewByteArray(env, nReadLen);
      (*env)->SetByteArrayRegion(env,jarray, 0, nReadLen, pBy);  
      jclassm_cls = (*env)->FindClass(env,"com/luoxudong/jni/bean/CusBuffer");
      jmethodID m_mid =(*env)->GetMethodID(env, m_cls, "<init>", "()V");
      jfieldID  m_fid1 = (*env)->GetFieldID(env, m_cls,"buffer", "[B");
      jfieldID  m_fid2 = (*env)->GetFieldID(env, m_cls,"nBufferLen", "I");
    
      jobject   m_obj = (*env)->NewObject(env, m_cls,m_mid);
      (*env)->SetObjectField(env,m_obj, m_fid1, jarray);
      (*env)->SetIntField(env, m_obj,m_fid2, nReadLen);
      return m_obj;
    }
    
    /*
    * Class:com_luoxudong_jni_reader_FileSys
    * Method:   FileWrite
    * Signature: (I[BI)I
    */
    JNIEXPORT jintJNICALL Java_com_luoxudong_jni_reader_FileSys_FileWrite
      (JNIEnv *env, jclass jobj, jint fd,jbyteArray buf, jint count)
    {
      jbyte *pjb = (jbyte*)(*env)->GetByteArrayElements(env, buf, 0);
      jsize len =(*env)->GetArrayLength(env, buf);
      UINT8 *byBuf = (UINT8 *)pjb;
      pjb[len] = '\0';
      return FileWrite(fd, byBuf,count);
    }
    
    /*
    * Class:com_luoxudong_jni_reader_FileSys
    * Method:   FileClose
    * Signature: (I)I
    */
    JNIEXPORT jintJNICALL Java_com_luoxudong_jni_reader_FileSys_FileClose
      (JNIEnv *env, jclass jobj, jint fd)
    {
      return FileClose(fd);
    }

其中头部定义了些andriod中日志输出所需要的宏,以及其他关联的.h文件,在一些地方C跟C++的使用语法不太一样,如在C中调用UINT8 pbyFileName = (UINT8)(*env)->GetStringUTFChars(env, pFileName, 0),
而C++中的语法为env-> (UINT8 *)GetStringUTFChars(pFileName,0)。

8、生成共享库

把编写好的各种相关联代码放在一个文件夹中,编写android.mk文件,使用ndk生成libJNI_FileSys.so文件。

会在libs\armeabi目录下生成.so文件

9、应用程序调用

把生成好的libJNI_FileSys.so文件放入java工程的libs目录下,就可以使用了


10、应用程序调用

当然在android下读写文件时还需要配置权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

10、运行效果

附上完整源代码: android JNI调用-文件操作

转载请指明出处:http://www.luoxudong.com/?p=1

有7人对 “Android中JNI调用–文件操作”留言了

  1. I enjoy you because of all of the efforts on this site. Kate loves engaging in investigation and it’s really easy to understand why. I know all concerning the dynamic medium you give informative secrets on this web blog and as well as foster participation from others on that content plus our daughter is always discovering a lot. Have fun with the remaining portion of the new year. You are conducting a great job.

  2. I precisely desired to say thanks again. I’m not certain the things I could possibly have accomplished in the absence of the advice contributed by you about my area. Certainly was a alarming case for me, but being able to see a expert approach you dealt with it forced me to cry for contentment. Extremely grateful for the advice and believe you are aware of a powerful job that you’re accomplishing educating many others all through a blog. I’m certain you’ve never come across all of us.

  3. I’m commenting to let you be aware of of the amazing discovery my child developed browsing the blog. She came to find a good number of details, including how it is like to have a great coaching character to get a number of people clearly completely grasp a number of advanced issues. You actually did more than her desires. I appreciate you for churning out such beneficial, dependable, edifying and cool guidance on that topic to Emily.

  4. I’m commenting to let you be aware of of the amazing discovery my child developed browsing the blog. She came to find a good number of details, including how it is like to have a great coaching character to get a number of people clearly completely grasp a number of advanced issues. You actually did more than her desires. I appreciate you for churning out such beneficial, dependable, edifying and cool guidance on that topic to Emily.

  5. Thanks for your own work on this website. My mum takes pleasure in setting aside time for research and it is simple to grasp why. We notice all relating to the powerful way you provide very important tips and hints via your website and therefore strongly encourage response from visitors on this theme while our own simple princess is without a doubt understanding so much. Take pleasure in the rest of the year. Your performing a useful job.

发表评论

电子邮件地址不会被公开。