Android的NDK開發(5)————Android JNI層實現檔案的read、write與seek操作
/********************************************************************************************
* author:conowen@大鐘
* E-mail:[email protected]
*
* 注:本文為原創,僅作為學習交流使用,轉載請標明作者及出處。
********************************************************************************************/
1、
在JNI層實現檔案的讀寫操作的話,就要使用到linux的讀寫函數了。
2、開啟檔案
int open( const char *pathname,int flags, int mode);
返回值:為一個檔案控制代碼(fd),供read、write等操作。
引數:
pathname: 開啟的檔案所在路徑字串。如
String filename = "/sdcard/test.txt";
flags: 檔案開啟的方式
flag之間可以作“與”運算,如
open(filename, O_CREAT | O_RDWR,mode);
常用flags:
O_RDONLY 以只讀方式開啟檔案
O_WRONLY 以只寫方式開啟檔案
O_RDWR 以可讀寫方式開啟檔案。上述三種旗標是互斥的,也就是不可同時使用,但可與下列的旗標利用OR(|)運算子組合。
O_CREAT 若欲開啟的檔案不存在則自動建立該檔案。
O_TRUNC 若檔案存在並且以可寫的方式開啟時,此標誌位會令檔案長度重新清為0,也就是說檔案內容清空。
O_APPEND 當讀寫檔案時會從檔案尾開始移動,也就是所寫入的資料會以附加的方式加入到檔案後面。
O_NONBLOCK 以不可阻斷的方式開啟檔案,也就是無論有無資料讀取或等待,都會立即返回程序之中。
O_SYNC 以同步的方式開啟檔案。
O_NOFOLLOW 如果引數pathname所指的檔案為一符號連線,則會令開啟檔案失敗。
O_DIRECTORY 如果引數pathname所指的檔案並非為一目錄,則會令開啟檔案失敗。
mode: 檔案儲存許可權
S_IRWXU00700 許可權,代表該檔案所有者具有可讀、可寫及可執行的許可權。
S_IRUSR 或S_IREAD,00400許可權,代表該檔案所有者具有可讀取的許可權。
S_IWUSR 或S_IWRITE,00200 許可權,代表該檔案所有者具有可寫入的許可權。
S_IXUSR 或S_IEXEC,00100 許可權,代表該檔案所有者具有可執行的許可權。
S_IRWXG 00070許可權,代表該檔案使用者組具有可讀、可寫及可執行的許可權。
S_IRGRP 00040 許可權,代表該檔案使用者組具有可讀的許可權。
S_IWGRP 00020許可權,代表該檔案使用者組具有可寫入的許可權。
S_IXGRP 00010 許可權,代表該檔案使用者組具有可執行的許可權。
S_IRWXO 00007許可權,代表其他使用者具有可讀、可寫及可執行的許可權。
S_IROTH 00004 許可權,代表其他使用者具有可讀的許可權
S_IWOTH 00002許可權,代表其他使用者具有可寫入的許可權。
S_IXOTH 00001 許可權,代表其他使用者具有可執行的許可權。
3、檔案的讀(read)操作
int read(int fd, unsigned char *buf, int size);
返回值:返回實際讀取到的位元組數,如果返回0,表示已到達檔案尾或是無可讀取的資料,此外檔案讀寫位置會隨讀取到的位元組移動。
引數:
fd:表示檔案控制代碼,是由open函式得到
buf:read()函式會把fd 所指的檔案傳送count個位元組到buf指標所指的記憶體中
size:要讀取的位元組數
4、寫入操作
int write (int fd, const unsigned char *buf, int size);
返回值 :如果成功write(),就會返回實際寫入的位元組數。當有錯誤發生時則返回-1
引數:
fd:同上
buf:將要寫入到檔案裡面的內容。
size:要寫入的位元組數
5、跳轉操作
int64_t seek(int fd, int64_t pos, int whence)
返回值:成功時則返回目前的讀寫位置,也就是距離檔案開頭多少個位元組,若有錯誤則返回-1。
引數:
fd:同上
pos:跳轉的相對量,可正可負,表示相對位置的前後關係
whence:跳轉的方向,whence取值如下所示
int SEEK_SET = 0;//將讀寫位置指向檔案頭後再增加offset個位移量。
int SEEK_CUR = 1;//以目前的讀寫位置往後增加offset個位移量。
int EEK_END = 2;//將讀寫位置指向檔案尾後再增加offset個位移量。
注:當size引數=0;whence = SEEK_END;時返回值即為檔案大小。
6、關閉操作
int close(int fd)
7、簡單示例
效果圖:
7.1、JNI程式碼:(有JNI_onLoad函式)
//fs.c
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <stdlib.h>
#include <fcntl.h>
int file_open(const char *filename, int flags)
{
int fd;
fd = open(filename, flags, 0666);
if (fd == -1)
return -1;
return fd;
}
int file_read(int fd, unsigned char *buf, int size)
{
return read(fd, buf, size);
}
int file_write(int fd, const unsigned char *buf, int size)
{
return write(fd, buf, size);
}
int64_t file_seek(int fd, int64_t pos, int whence)
{
if (whence == 0x10000) {
struct stat st;
int ret = fstat(fd, &st);
return ret < 0 ? -1 : st.st_size;
}
return lseek(fd, pos, whence);
}
int file_close(int fd)
{
return close(fd);
}
//jni.c
#define TAG "fs_jni"
#include <android/log.h>
#include "jniUtils.h"
static const char* const kClassPathName = "com/conowen/fs/FsActivity";
jint
Java_com_conowen_fs_FsActivity_NativeFileOpen( JNIEnv* env, jobject thiz,jstring filename,jint flags ){
const char *filename_char = (*env)->GetStringUTFChars(env,filename, NULL);
return file_open(filename_char, flags);
}
jint
Java_com_conowen_fs_FsActivity_NativeFileRead(JNIEnv* env, jobject thiz,int fd,jbyteArray buf,jint size){
unsigned char *buf_char = (char*)((*env)->GetByteArrayElements(env,buf, NULL));
return file_read(fd, buf_char, size);
}
jint
Java_com_conowen_fs_FsActivity_NativeFileWrite(JNIEnv* env, jobject thiz,int fd,jbyteArray buf,jint size){
unsigned char *buf_char = (char*)((*env)->GetByteArrayElements(env,buf, NULL));
return file_write(fd, buf_char, size);
}
jlong
Java_com_conowen_fs_FsActivity_NativeFileSeek(JNIEnv* env, jobject thiz,int fd,jlong Offset,jint whence){
return file_seek(fd, Offset, whence);
}
jint
Java_com_conowen_fs_FsActivity_NativeFileClose(JNIEnv* env, jobject thiz,int fd){
return file_close(fd);
}
/******************************JNI registration.************************************/
static JNINativeMethod gMethods[] = {
{"NativeFileOpen", "(Ljava/lang/String;I)I", (void *)Java_com_conowen_fs_FsActivity_NativeFileOpen},
{"NativeFileRead", "(I[BI)I", (void *)Java_com_conowen_fs_FsActivity_NativeFileRead},
{"NativeFileWrite", "(I[BI)I", (void *)Java_com_conowen_fs_FsActivity_NativeFileWrite},
{"NativeFileSeek", "(IJI)J", (void *)Java_com_conowen_fs_FsActivity_NativeFileSeek},
{"NativeFileClose", "(I)I", (void *)Java_com_conowen_fs_FsActivity_NativeFileClose},
};
int register_com_conowen_fs_FsActivity(JNIEnv *env) {
return jniRegisterNativeMethods(env, kClassPathName, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));
}
//jniUtils.h
#ifndef _JNI_UTILS_H_
#define _JNI_UTILS_H_
#include <stdlib.h>
#include <jni.h>
#ifdef __cplusplus
extern "C"
{
#endif
int jniThrowException(JNIEnv* env, const char* className, const char* msg);
JNIEnv* getJNIEnv();
int jniRegisterNativeMethods(JNIEnv* env,
const char* className,
const JNINativeMethod* gMethods,
int numMethods);
#ifdef __cplusplus
}
#endif
#endif /* _JNI_UTILS_H_ */
//onLoad.cpp
#define TAG "fs_onLoad"
#include <android/log.h>
#include "jniUtils.h"
extern "C" {
extern int register_com_conowen_fs_FsActivity(JNIEnv *env);
}
static JavaVM *sVm;
/*
* Throw an exception with the specified class and an optional message.
*/
int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
jclass exceptionClass = env->FindClass(className);
if (exceptionClass == NULL) {
__android_log_print(ANDROID_LOG_ERROR,
TAG,
"Unable to find exception class %s",
className);
return -1;
}
if (env->ThrowNew(exceptionClass, msg) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR,
TAG,
"Failed throwing '%s' '%s'",
className, msg);
}
return 0;
}
JNIEnv* getJNIEnv() {
JNIEnv* env = NULL;
if (sVm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR,
TAG,
"Failed to obtain JNIEnv");
return NULL;
}
return env;
}
/*
* Register native JNI-callable methods.
*
* "className" looks like "java/lang/String".
*/
int jniRegisterNativeMethods(JNIEnv* env,
const char* className,
const JNINativeMethod* gMethods,
int numMethods)
{
jclass clazz;
__android_log_print(ANDROID_LOG_INFO, TAG, "Registering %s natives\n", className);
clazz = env->FindClass(className);
if (clazz == NULL) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "Native registration unable to find class '%s'\n", className);
return -1;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "RegisterNatives failed for '%s'\n", className);
return -1;
}
return 0;
}
//Dalvik虛擬機器載入C庫時,第一件事是呼叫JNI_OnLoad()函式
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = JNI_ERR;
sVm = vm;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "GetEnv failed!");
return result;
}
__android_log_print(ANDROID_LOG_INFO, TAG, "loading . . .");
if(register_com_conowen_fs_FsActivity(env) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load register_com_conowen_fs_FsActivity");
goto end;
}
__android_log_print(ANDROID_LOG_INFO, TAG, "loaded");
result = JNI_VERSION_1_4;
end:
return result;
}
7.2、Android.mk檔案
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := fs
LOCAL_SRC_FILES := fs.c jni.c onLoad.cpp
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
7.3、java層程式碼
/* author:conowen
* data:2012.5.1
* e-mail:[email protected]
*/
package com.conowen.fs;
import java.io.UnsupportedEncodingException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class FsActivity extends Activity {
String filename = "/sdcard/test.txt";
EditText writestrET;
Button writeBT;
Button readBT;
Button seekBT;
TextView readTV;
String writeStr;
byte[] buf_write;
byte[] buf_read;
int fd;
int O_ACCMODE = 0003;
int O_RDONLY = 00;
int O_WRONLY = 01;
int O_RDWR = 02;
int O_CREAT = 0100; /* not fcntl */
int O_EXCL = 0200; /* not fcntl */
int O_NOCTTY = 0400; /* not fcntl */
int O_TRUNC = 01000; /* not fcntl */
int O_APPEND = 02000;
int O_NONBLOCK = 04000;
int O_NDELAY = O_NONBLOCK;
int O_SYNC = 010000;
int O_FSYNC = O_SYNC;
int O_ASYNC = 020000;
int SEEK_SET = 0;//將讀寫位置指向檔案頭後再增加offset個位移量。
int SEEK_CUR = 1;//以目前的讀寫位置往後增加offset個位移量。
int EEK_END = 2;//將讀寫位置指向檔案尾後再增加offset個位移量。
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
writestrET = (EditText) findViewById(R.id.writeET);
writeBT = (Button) findViewById(R.id.writeBT);
readBT = (Button) findViewById(R.id.readBT);
seekBT = (Button) findViewById(R.id.seekBT);
readTV = (TextView) findViewById(R.id.readTV);
writeBT.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
fd = NativeFileOpen(filename, O_CREAT | O_RDWR);
System.out.println("fd_write---->" + fd);
writeStr = writestrET.getText().toString();
buf_write = writeStr.getBytes();
int ret_write = NativeFileWrite(fd, buf_write, buf_write.length);
System.out.println("寫入返回結果" + ret_write);
NativeFileClose(fd);
}
});
readBT.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
fd = NativeFileOpen(filename, O_CREAT | O_RDWR);
System.out.println("fd_read---->" + fd);
buf_read = new byte[buf_write.length];
int ret_read = NativeFileRead(fd, buf_read, buf_write.length);
System.out.println("讀出返回結果" + ret_read);
try {
readTV.setText( new String(buf_read, "GB2312") + "");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
NativeFileClose(fd);
}
});
seekBT.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
fd = NativeFileOpen(filename, O_CREAT | O_RDWR);
long Offset=20;
long ret_seek =NativeFileSeek(fd, Offset, SEEK_CUR);
System.out.println("seek返回結果" + ret_seek);
NativeFileClose(fd);
/* 1) 欲將讀寫位置移到檔案開頭時:
lseek(int fildes,0,SEEK_SET);
2) 欲將讀寫位置移到檔案尾時:
lseek(int fildes,0,SEEK_END);
3) 想要取得目前檔案位置時:
lseek(int fildes,0,SEEK_CUR);
返回值:當呼叫成功時則返回目前的讀寫位置,也就是距離檔案開頭多少個位元組。若有錯誤則返回-1,errno 會存放錯誤程式碼。
* */
}
});
}
public native int NativeFileOpen(String filename, int flags);
public native int NativeFileRead(int fd, byte[] buf, int sizes);
public native int NativeFileWrite(int fd, byte[] buf, int sizes);
public native long NativeFileSeek(int fd, long Offset, int whence);
//Offset:偏移量,每一讀寫操作所需要移動的距離,單位是位元組的數量,可正可負(向前移,向後移)。
public native int NativeFileClose(int fd);
static {
System.loadLibrary("fs");
}
}
最後記得在manifest.xml裡面加上SD卡操作許可權
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>