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

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

  1. You are so awesome! I don’t suppose I’ve read something like this before.
    So nice to discover someone with unique thoughts on this issue.
    Seriously.. thanks for starting this up. This site
    is one thing that’s needed on the internet, someone with a little originality!

  2. Have you ever considered about including a little
    bit more than just your articles? I mean, what you say is valuable and everything.
    Nevertheless think of if you added some great graphics or video
    clips to give your posts more, “pop”! Your content is excellent but with pics and videos,
    this blog could definitely be one of the very best in its niche.
    Very good blog!

  3. Just desire to say your article is as astonishing.
    The clarity on your submit is simply spectacular and that i could think you are
    knowledgeable on this subject. Fine together with your permission allow me to snatch your feed to keep up to date with drawing close post.

    Thanks 1,000,000 and please keep up the rewarding work.

发表评论

邮箱地址不会被公开。