Android NDK開發(fā)之旅17--NDK--案例之文件拆分合并與日志輸出

前言

這里再次啰嗦一下痘番,我們?yōu)槭裁匆獙W習NDK開發(fā)呢捉片?因為很多大公司平痰,為了節(jié)省開發(fā)資源,很多時候核心技術(shù)都是用C/C++去實現(xiàn)的伍纫,一套代碼宗雇,可以給Android、IOS莹规、后端調(diào)用赔蒲,這也是一種跨平臺的實現(xiàn)方案,大大節(jié)省了人力成本良漱,但是對開發(fā)者的要求就提高了舞虱。這也是為什么像QQ、淘寶等眾多大型APP都是采用了很多動態(tài)庫文件的原因母市。所以說很多時候矾兜,大公司的筆試考的都是C/C++。

如果你也有志氣想進大公司患久,那就學習吧椅寺,勇往直前!

文件案例與拆分

先說說為什么要做文件拆分和合并蒋失。舉一些例子返帕,我們做多媒體開發(fā)的時候,尤其是大視頻上傳篙挽,我們就需要把視頻拆分成多個分別上傳闺骚,減輕服務端壓力的同時挂滓,也防止了上傳失敗導致全盤重新上傳毒嫡。而且這次的文章是為了下一次的增量更新做鋪墊谎仲。

上一篇博客介紹了Android Studio2.3的NDK開發(fā)流程,下面我們通過一個實際案例來進一步加強算行、熟悉這個開發(fā)流程梧油。

首先我們創(chuàng)建項目,配置我們的CMake構(gòu)建腳本:

cmake_minimum_required(VERSION 3.4.1)

add_library(
             testJni
             SHARED
             src/main/jni/testJni.c )

find_library( log-lib
              log )

target_link_libraries(
                       testJni
                       ${log-lib} )

由于我們在NDK中不能夠使用printf輸出logcat州邢,所以將會使用NDK自帶的log-lib來進行日志打印儡陨。

其中:

  1. find_library命令是找到log-lib這個庫。
  2. target_link_libraries是把我們自己的庫跟log-lib關(guān)聯(lián)起來量淌。相當于我們的庫依賴log-lib庫骗村。

如果你使用ndk-build的方式來構(gòu)建項目的話,那么請在Android.mk文件中添加LOCAL_LDLIBS配置呀枢,完整的配置如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := testJni
LOCAL_SRC_FILES := testJni.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

其它的就不贅述了胚股,參考上一篇文章,下面我們創(chuàng)建一個類裙秋,專門用于文件的拆分與合并:

package com.nan.testjni;

public class FileUtil {
    //文件拆分
    public static native void diff(String path, String pattern, int count);

    //文件合并
    public static native void patch(String path, String pattern, int count);
}

下面我們在Application里面加載我們的動態(tài)庫琅拌,因為Application是全局的缨伊,一般來說Application只會初始化一次(單進程的情況下)。

public class App extends Application {

    static {
        //加載動態(tài)庫.so文件进宝,注意不用寫lib前綴刻坊,系統(tǒng)會默認添加
        System.loadLibrary("testJni");
    }

    @Override
    public void onCreate() {
        super.onCreate();

    }
}

記得在清單文件中配置一下:

<application
    android:name=".App"
</application>

文件拆分

先舉個例子,比如說我們的文件大小是110個單位(字節(jié))党晋,需要被分成9個小文件:

  1. 如果110能夠被9整除的話谭胚,那么每個文件大小是:110/9
  2. 很顯然,這里不能夠整除未玻,那么我們前面8(9-1)個文件需要大一些灾而,大小為:110/(9-1)。最后一個文件的大小稍微小一些深胳,大小為:110%(9-1)绰疤。

示例代碼如下:

JNIEXPORT void JNICALL
Java_com_nan_testjni_FileUtil_diff(JNIEnv *env, jclass type, jstring path_, jstring pattern_,
                                   jint count) {

    //需要分割的文件路徑
    const char *path = (*env)->GetStringUTFChars(env, path_, NULL);
    //分割之后的小文件命名法則
    const char *pattern = (*env)->GetStringUTFChars(env, pattern_, NULL);

    //得到分割之后的小文件的命名列表(字符串數(shù)組)铜犬,分配內(nèi)存
    char **patches = malloc(sizeof(char *) * count);
    int i = 0;
    for (; i < count; i++) {
        //為字符數(shù)組的每一個條目分配內(nèi)存
        patches[i] = malloc(sizeof(char) * 100);
        sprintf(patches[i], pattern, i);

        //打印日志
        //__android_log_print(ANDROID_LOG_DEBUG, "TAG_NDK", "File Name : %s\n", patches[i]);
        LOGE("name:%s\n", patches[i]);
    }

    FILE *fTotal = fopen(path, "rb");
    int fileSize = getFileSize(path);

    if (fileSize / count == 0) {
        //可以整除
        int partSize = fileSize / count;
        i = 0;
        for (; i < count; i++) {
            FILE *fPart = fopen(patches[i], "wb");
            int j = 0;
            for (; j < partSize; j++) {
                fputc(fgetc(fTotal), fPart);
            }
            fclose(fPart);
        }
    } else {
        //不可以整除
        int partSize = fileSize / (count - 1);
        i = 0;
        for (; i < count - 1; i++) {
            FILE *fPart = fopen(patches[i], "wb");
            int j = 0;
            for (; j < partSize; j++) {
                fputc(fgetc(fTotal), fPart);
            }
            fclose(fPart);
        }
        FILE *fLast = fopen(patches[count - 1], "wb");
        i = 0;
        for (; i < fileSize % (count - 1); i++) {
            fputc(fgetc(fTotal), fLast);
        }
        fclose(fLast);
    }

    //釋放資源
    fclose(fTotal);
    i = 0;
    for (; i < count; i++) {
        free(patches[i]);
    }
    free(patches);

    (*env)->ReleaseStringUTFChars(env, path_, path);
    (*env)->ReleaseStringUTFChars(env, pattern_, pattern);
}

解釋一下上面的代碼:

  1. 首先我們需要一個分割之后的字符串數(shù)組(也就是char二級指針)舞终,用于存放分割之后的文件路徑。
  2. 然后我分整除和不整除兩種情況來分別處理癣猾,不斷讀取被分割的文件敛劝,循環(huán)寫入每個子文件中。
  3. 釋放資源纷宇,尤其注意的是夸盟,malloc分配的堆內(nèi)存需要及時釋放。

這里我們封裝了一個獲取文件大小的函數(shù)像捶,之前的C語音的文件有介紹上陕,不在贅述:

//獲取文件大小
long get_file_size(char *path){
    FILE *fp = fopen(path,"rb");
    fseek(fp,0,SEEK_END);
    return ftell(fp);
}

NDK日志輸出

在上面配置了我們的CMake構(gòu)建腳本之后(否則找不到打印日志函數(shù)),我們可以使用__android_log_print來輸出日志拓春,參數(shù)分別為日志級別释簿、TAG,剩下的兩個參數(shù)跟printf一樣硼莽。使用的例子如下:

先通過include包含文件:

#include <android/log.h>

然后調(diào)用__android_log_print輸出日志:

__android_log_print(ANDROID_LOG_DEBUG, "TAG_NDK", "File Name : %s\n", patches[i]);

這里為了簡化庶溶,我們通過一些宏定義來簡化操作,在C語音的預編譯的那篇文章有介紹懂鸵,這里不再贅述:

#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO, "huannan",FORMAT,__VA_ARGS__)
#define LOGD(FORMAT, ...) __android_log_print(ANDROID_LOG_DEBUG,"huannan",FORMAT,__VA_ARGS__)
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"huannan",FORMAT,__VA_ARGS__)

文件合并

下面來看看文件合并的代碼偏螺,相對來說比較簡單,就是通過一個循環(huán)匆光,不斷讀取每個子文件套像,然后合并到總文件中。代碼如下:

JNIEXPORT void JNICALL
Java_com_nan_testjni_FileUtil_patch(JNIEnv *env, jclass type, jstring path_, jstring pattern_,
                                    jint count) {
    //合并之后的文件路徑
    const char *path = (*env)->GetStringUTFChars(env, path_, NULL);
    //分割之后的小文件命名法則
    const char *pattern = (*env)->GetStringUTFChars(env, pattern_, NULL);

    //得到被分割的小文件的命名列表(字符串數(shù)組)终息,分配內(nèi)存
    char **patches = malloc(sizeof(char *) * count);
    int i = 0;
    for (; i < count; i++) {
        //為字符數(shù)組的每一個條目分配內(nèi)存
        patches[i] = malloc(sizeof(char) * 100);
        sprintf(patches[i], pattern, i);

        //打印日志
        //__android_log_print(ANDROID_LOG_DEBUG, "TAG_NDK", "File Name : %s\n", patches[i]);
        LOGE("name:%s\n", patches[i]);
    }

    //合并的文件路徑
    FILE *fTotal = fopen(path, "wb");
    i = 0;
    for (; i < count; i++) {
        int patchSize = getFileSize(patches[i]);
        FILE *fPart = fopen(patches[i], "rb");
        int j = 0;
        for (; j < patchSize; j++) {
            fputc(fgetc(fPart), fTotal);
        }
        fclose(fPart);
    }

    //釋放資源
    fclose(fTotal);
    i = 0;
    for (; i < count; i++) {
        free(patches[i]);
    }
    free(patches);

    (*env)->ReleaseStringUTFChars(env, path_, path);
    (*env)->ReleaseStringUTFChars(env, pattern_, pattern);
}

如果覺得我的文字對你有所幫助的話夺巩,歡迎關(guān)注我的公眾號:

公眾號:Android開發(fā)進階

我的群歡迎大家進來探討各種技術(shù)與非技術(shù)的話題货葬,有興趣的朋友們加我私人微信huannan88,我拉你進群交(♂)流(♀)劲够。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末震桶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子征绎,更是在濱河造成了極大的恐慌蹲姐,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件人柿,死亡現(xiàn)場離奇詭異柴墩,居然都是意外死亡,警方通過查閱死者的電腦和手機凫岖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門江咳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人哥放,你說我怎么就攤上這事歼指。” “怎么了甥雕?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵踩身,是天一觀的道長。 經(jīng)常有香客問我社露,道長挟阻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任峭弟,我火速辦了婚禮附鸽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瞒瘸。我一直安慰自己坷备,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布挨务。 她就那樣靜靜地躺著击你,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谎柄。 梳的紋絲不亂的頭發(fā)上丁侄,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音朝巫,去河邊找鬼鸿摇。 笑死,一個胖子當著我的面吹牛劈猿,可吹牛的內(nèi)容都是我干的拙吉。 我是一名探鬼主播潮孽,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼筷黔!你這毒婦竟也來了往史?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤佛舱,失蹤者是張志新(化名)和其女友劉穎椎例,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體请祖,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡订歪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肆捕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刷晋。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖慎陵,靈堂內(nèi)的尸體忽然破棺而出眼虱,到底是詐尸還是另有隱情,我是刑警寧澤荆姆,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布蒙幻,位于F島的核電站,受9級特大地震影響胆筒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诈豌,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一仆救、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧矫渔,春花似錦彤蔽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至油够,卻和暖如春蚁袭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背石咬。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工揩悄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鬼悠。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓删性,卻偏偏與公主長得像亏娜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蹬挺,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容