最近在計算一大批視頻的播放時長,於是使用了Android的videoview庫。然後Android studio 用的3.0,順便就用了Android的ndk來操作了一下日誌。雖然Java原生也能實現,這裏我不知爲啥就沒用原生的。。。。。。=。=。。。
功能點:
在Android Java文件中調用c++中的方法,達到文件的新建,日誌的記錄,日誌的追加,日誌文件的刪除。
知識點:
我想了想這個項目雖然很簡單,但是包含內容還是比較完整的,包括c++ 基礎 jni函數的兩種註冊,cmake的節點含義, native與Java相互調用。
如何使用:
step1:
在項目中的build文件中的
allprojects節點下面添加
maven { url "https://raw.githubusercontent.com/NDKFile/NDK/master" }
step2:
在app中的build文件中添加
implementation 'com.ndklog:log_lib:1.0.0'
use :
FileUtils .getInstance.FileWrite("","");
FileUtils .getInstance.FileDelete("");
所有源代碼如下:
native_lib.c++ 代碼:
此文件主要是動態註冊了文件操作的函數,以及靜態註冊一個原生的方法。
#include <jni.h>
#include <string>
#include "fileutil.h"
// 靜態註冊的 函數
extern "C" JNIEXPORT
jstring
JNICALL
Java_com_example_administrator_ndkfile_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
jint File_write(JNIEnv* env,jobject thiz,jstring content,jstring filepath){
fileutil* fileutil1 = new fileutil();
jint result = fileutil1->Write_file(env,thiz,content,filepath);
delete(fileutil1);
fileutil1=NULL;
return result;
}
jint File_Delete(JNIEnv* env,jobject thiz, jstring filepath){
fileutil* fileutil1 = new fileutil();
jint result = fileutil1->Delete_file(env,thiz,filepath);
delete(fileutil1);
fileutil1=NULL;
return result;
}
// jni 函數列表
static JNINativeMethod getMethods[] = {
// 括號內代表傳入參數的簽名符號,爲空可以不寫,括號外代表返回參數的簽名符號,爲空填寫 V,對應關係入下表
{"FileWrite", "(Ljava/lang/String;Ljava/lang/String;)I",(void*)File_write},
{"FileDelete", "(Ljava/lang/String;)I",(void*)File_Delete},
};
//此函數通過調用RegisterNatives方法來註冊我們的函數
static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* getMethods,int methodsNum){
jclass clazz;
//找到聲明native方法的類
clazz = env->FindClass(className);
if(clazz == NULL){
return JNI_FALSE;
}
//註冊函數 參數:java類 所要註冊的函數數組 註冊函數的個數
if(env->RegisterNatives(clazz,getMethods,methodsNum) < 0){
return JNI_FALSE;
}
return JNI_TRUE;
}
static int registerNatives(JNIEnv* env){
//指定類的路徑,通過FindClass 方法來找到對應的類
const char* className = "com/example/administrator/ndkfile/FileUtils";
return registerNativeMethods(env,className,getMethods, sizeof(getMethods)/ sizeof(getMethods[0]));
}
//回調函數 在加載JNI 的時候去註冊函數
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved ){
JNIEnv* env = NULL;
//獲取JNIEnv
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
__glibcxx_assert(env != NULL);
//註冊函數 registerNatives ->registerNativeMethods ->env->RegisterNatives
if(!registerNatives(env)){
return -1;
}
//返回jni 的版本
return JNI_VERSION_1_6;
}
fileutil.h 頭文件
定義了文件操作的方法以及,構造函數和析構函數
//
// Created by Administrator on 2019/11/28 0028.
//
#ifndef NDKFILE_FILEUTIL_H
#define NDKFILE_FILEUTIL_H
#include <jni.h>
class fileutil {
public:
fileutil();
~ fileutil();
jint Delete_file(JNIEnv* env,jobject thiz,jstring filepath);
jint Write_file(JNIEnv* env,jobject thiz,jstring content,jstring filepath);
};
#endif //NDKFILE_FILEUTIL_H
fileutil.cpp 文件
文件類的實現:
//
// Created by Administrator on 2019/11/28 0028.
//
#include "fileutil.h"
#include <iostream>
#include <android/log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <cstdlib>
#include <unistd.h>
// 定義info信息
#define LOG_TAG "[CHENZHU_JNILOG]"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
using namespace std;
fileutil::fileutil(){
LOGI("構造函數");
};
/**
* File_write--操作文件目錄
* 返回0--成功
*/
jint fileutil::Write_file(JNIEnv* env,jobject thiz,jstring content,jstring filepath){
FILE * file;
const char * file_path = env->GetStringUTFChars( filepath, JNI_FALSE);
const char * write_content = env->GetStringUTFChars( content, JNI_FALSE);
LOGI("go to file write:%s\n", file_path);
file = fopen(file_path,"a+");//如果文件存在就添加內容,如果文件不存在就創建新文件
if (file== NULL){
LOGI("open file faile%s\n","");
}else{
LOGI("open file success%s\n","");
}
fputs(write_content,file);//寫入操作
fclose(file);//關閉文件流
//free(filepath);//釋放動態內存,防止內存泄露
//在這裏再回調一下 Java 告訴他寫入完成
jclass j_class = env->FindClass("com/example/administrator/ndkfile/FileUtils");
jmethodID method = env->GetStaticMethodID(j_class, "callByJNI","(I)V");
// 回調靜態方法
env->CallStaticVoidMethod(j_class, method, 666);
return 0;//正確 執行返回
}
jint fileutil::Delete_file(JNIEnv* env,jobject thiz,jstring filepath) {
const char* dirName = env->GetStringUTFChars(filepath,JNI_FALSE);
if(0 == access(dirName,0)) {//目錄存在
LOGI("目錄存在 需要刪除\n");
int issuccess= remove(dirName);
if (issuccess==0){
LOGI("目錄存在 刪除成功\n");
return 0;
} else{
LOGI("目錄存在 刪除失敗\n");
return 1;
}
} else{
LOGI("目錄不存在 不用刪除\n");
return 0;
}
}
fileutil::~fileutil() {
LOGI("析構函數");
}
對應的Java文件 Fileutil.Java
package com.example.administrator.ndkfile;
public class FileUtils {
//類初始化時,不初始化這個對象(延時加載,真正用的時候再創建)
private static FileUtils instance;
//構造器私有化
private FileUtils(){}
//方法同步,調用效率低
public static synchronized FileUtils getInstance(){
if(instance==null){
instance=new FileUtils();
}
return instance;
}
public native int FileWrite(String conten,String path);
public native int FileDelete(String path);
public static void callByJNI(int data){
System.out.print("JNI 使用了回調:"+data);
}
}
在MainActivity中調用
adb 連接手機查看
cd 到對應目錄下 。cat查看信息,如果要copy 到電腦上的話
// 要推出 adb shell然後 adb pull /sdcard/data.txt data.txt
// adb copy 到電腦的C:\Users\Administrator\Desktop 上
後面如果有同學需要繼續完善 上傳這些日誌到服務器的話給我留言 我把後面的繼續完善。
github源碼地址 https://github.com/chenzhu005774/NDKFile