Android+JNI調用–文件操作

開發環境:Windows xp sp3 +MyEclipse 8.6+android2.3.3+jdk1.6+android-ndk-r6b

JNI概述:

JNI 是 Java Native Interface 的縮寫,譯爲 Java 本地接口。它允許 Java 代碼和其他語言編寫的代碼進行交互。在android 中提供 JNI 的方式,讓 Java 程序可以調用 C/C++語言程序。 android 中很多 Java 類都具有 native 接口,這些接口由本地實現,然後註冊到系統中。在android系統中實現JNI庫需要連接.so共享庫,如:lib<文件名>.so。

注意權限

Android NDK概述:
Android NDK是一個工具集,讓你的Android應用程序裏可以內嵌使用本地代碼(C/C++)的組件。
Android應用程序運行在Dalvik虛擬機中。NDK可以讓你使用C/C++這樣的本地代碼語言來實現你的應用程序中某些部分。這對某類程序是有幫助的,比如需要重用已有的C代碼,或者爲了提高運行速度。

NDK 提供:
1). 編譯文件和工具集,用來將你的C/C++源文件編譯成本地庫。
2). 提供一種方式,將對應的本地庫內嵌到應用程序包文件(.apk)中,最終發佈到Android設備中。
3). 本地系統頭文件和庫,這些頭文件和庫從Android1.5開始往後都是被支持的。但使用本地活動(native activity)的程序只能運行在Android 2.3或更高的系統中。
4). 文檔、示例、指南。
本例中JNI調用大概流程圖如下:


1.編寫Android JNI模塊java調用類

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);
}
2.實現本地方法調用接口
爲了方便我們可以使用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對象,類型對應表如下:

Java類型

本地類型

字節(bit)

boolean

jboolean

8, unsigned

byte

jbyte

8

char

jchar

16, unsigned

short

jshort

16

int

jint

32

long

jlong

64

float

jfloat

32

double

jdouble

64

void

void

n/a

3.本地接口實現:
根據對應的.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);
jclass m_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)。

4.生成共享庫
把編寫好的各種相關聯代碼放在一個文件夾中,編寫android.mk文件,使用ndk生成libJNI_FileSys.so文件。


會在libs\armeabi目錄下生成.so文件


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


6.當然在android下讀寫文件時還需要配置權限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

7.運行效果:


轉載請指明出處:http://blog.csdn.net/rohsuton/article/details/6865628

附上完整源代碼 android JNI調用-文件操作


發佈了24 篇原創文章 · 獲贊 50 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章