<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                接續上篇[NDK開發基礎①使用Android Studio編寫NDK](http://www.jianshu.com/p/f1b8b97d2ef8) ### 前情提要 隨著Android Studio的越來越完善 , 我們編寫NDK就會越來越方便,使用Android Studio 2.2 RC2 , 編寫NDK的時候 , 不需要使用`javah`命令來生成頭文件 , 創建一個`native`方法 , 使用`alt + enter`會提示要你創建一個JNI函數 , C/C++語法提示也相對比較完善了, 減少了一些重復代碼的編寫,相信使用Android Studio 2.2編寫NDK, 不會讓你失望 。 ###Part 1 , -- 文件的加密解密 在[C語言基礎系列](http://www.jianshu.com/p/4701cd1e1914)第八章[文件IO](http://www.jianshu.com/p/eb5d58800a63),介紹了文件的加密解密 , 開發工具使用的是VS , 在上篇[NDK開發基礎①使用Android Studio編寫NDK](http://www.jianshu.com/p/f1b8b97d2ef8)簡單介紹了Android Studio來搭建NDK開發環境,今天我們使用Android Studio來玩一把 。 > 界面 與Project目錄結構 ![UI](http://upload-images.jianshu.io/upload_images/643851-788b88eea0671dba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 就幾個按鈕 , 布局文件就不貼代碼了 , 讀者也可以整個輸入框,可以輸入加密的密碼 。 ![project directory](http://upload-images.jianshu.io/upload_images/643851-d44584946c363713.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 從目錄上可以看出`ndk`包下面的 , 就是一個`native`方法的類, `cpp`目錄下的就是JNI `C`文件 , CMakeLists.txt配置我們的編譯流程,以及生成庫與添加庫的配置。 > Java native code ```java /** * 帶密碼的文件加密 * @param normalFilePath 要加密的文件路徑 * @param encryptFilePath 加密之后的文件路徑 * @param password 加密密碼 */ public static native void fileEncrypt(String normalFilePath,String encryptFilePath,String password) ; /** * 帶密碼的文件解密 * @param encryptFilePath 要解密的文件路徑 * @param encryptFilePath 解密之后的文件路徑 * @param password 加密密碼 */ public static native void fileDecrypt(String encryptFilePath,String decryptFilePath,String password) ; ``` 簡要提示:文件的加密解密 , 實質上在復制文件的時候進行`^`運算,在計算機中 , 所有的文件都是以二進制存儲的 , 所有可以進行運算來進行文件的加密解密 , 當然 , 密碼的算法是可逆的 , 也就是可解密的 。 > use java native code ```java // SD_CARD 根路徑 private static final String SD_CARD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() ; /*文件加密*/ private void fileEncrypt() { String normalFilePath = SD_CARD_PATH+ File.separatorChar+"neck.jpg" ; String encryptFilePath = SD_CARD_PATH+File.separatorChar+"neck_encrypt.jpg" ; Log.e(TAG, "fileEncrypt: normalFilePath = "+ normalFilePath + " encryptFilePath = "+encryptFilePath); try{ HelloNDK.fileEncrypt(normalFilePath,encryptFilePath,"xj"); }catch (Exception e) { e.printStackTrace(); } } /*文件解密*/ private void fileDecrypt() { String encryptFilePath = SD_CARD_PATH+File.separatorChar+"neck_encrypt.jpg" ; String decryptFilePath = SD_CARD_PATH+File.separatorChar+"neck_decrypt.jpg" ; try{ HelloNDK.fileDecrypt(encryptFilePath,decryptFilePath,"xj"); }catch (Exception e) { e.printStackTrace(); } } ``` > 文件加密解密實現 ```c /*加密文件*/ JNIEXPORT void JNICALL Java_com_zeno_encryptanddecrypt_ndk_HelloNDK_fileEncrypt(JNIEnv *env, jclass type, jstring normalFilePath_, jstring encryptFilePath_, jstring password_) { const char *normalFilePath = (*env)->GetStringUTFChars(env, normalFilePath_, 0); const char *encryptFilePath = (*env)->GetStringUTFChars(env, encryptFilePath_, 0); const char *password = (*env)->GetStringUTFChars(env, password_, 0); int passwordLen = strlen(password); LOGE("MainActivity","path1 == %s , path2 == %s",normalFilePath , encryptFilePath) ; // 讀文件指針 FILE* frp = fopen(normalFilePath,"rb") ; // 寫文件指針 FILE* fwp = fopen(encryptFilePath,"wb") ; if(frp == NULL) { LOGE("MainActivity","%s","文件不存在") ; return; } if(fwp == NULL) { LOGE("MainActivity","%s","沒有寫權限") ; } // 邊讀邊寫邊加密 int buffer ; int index = 0 ; while((buffer = fgetc(frp)) != EOF) { // write fputc(buffer ^ *(password+(index % passwordLen)),fwp) ; index++ ; } // 關閉文件流 fclose(fwp); fclose(frp); LOGE("MainActivity","%s","文件加密成功") ; (*env)->ReleaseStringUTFChars(env, normalFilePath_, normalFilePath); (*env)->ReleaseStringUTFChars(env, encryptFilePath_, encryptFilePath); (*env)->ReleaseStringUTFChars(env, password_, password); } ``` 簡要提示:加密解密算法,在[C語言:文件IO](http://www.jianshu.com/p/eb5d58800a63)里面就介紹了 , 這里就不加贅述了,加密與解密共用的是一套算法, 解密的就不貼代碼了 。在使用`*password`指針的時候需要注意,`password+1`可以得到下一個字符,但是`password+0`的時候不會得到第一個字符,會輸出整個字符指針,所有在使用字符指針的時候一個一個取字符 , 需要使用`*(password+0) ,*(password+1) `取第一個第二字符 。如果您對C語言以及JNI開發都不了解的話 , 可以關注的我的專題[Android NDK開發之旅](http://www.jianshu.com/collection/a25bf14495d7) ### Part 2 , 文件分割與合并 __使用場景:__大文件上傳 , 如視頻文件 , 進行文件拆分 , 分批上傳 。 > Java native code ```java /** * 文件分割 * @param splitFilePath 要分割文件的路徑 * @param suffix 分割文件拓展名 * @param fileNum 分割文件的數量 */ public static native void fileSplit(String splitFilePath,String suffix,int fileNum); /** * 文件合并 * @param splitFilePath 分割文件的路徑 * @param splitSuffix 分割文件拓展名 * @param mergeSuffix 合并文件的拓展名 * @param fileNum 分割文件的數量 */ public static native void fileMerge(String splitFilePath,String splitSuffix,String mergeSuffix,int fileNum); ``` > use java native code ```java /*分割文件*/ private void fileSplit() { String splitFilePath = SD_CARD_PATH+File.separatorChar+"neck.jpg" ; String suffix = ".b"; try{ HelloNDK.fileSplit(splitFilePath,suffix,3); }catch (Exception e) { e.printStackTrace(); } } /*文件合并*/ private void fileMerge() { String splitFilePath = SD_CARD_PATH+File.separatorChar+"neck.jpg" ; String splitSuffix = ".b"; String mergeSuffix = ".jpeg"; try{ HelloNDK.fileMerge(splitFilePath,splitSuffix,mergeSuffix,3); }catch (Exception e) { e.printStackTrace(); } } ``` > 文件分割實現 算法分析圖: ![file split](http://upload-images.jianshu.io/upload_images/643851-3348bb0d653afe62.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 偽代碼: ``` // 平均分配文件大小 if(fileSize % fileNum) { int i= 0 ; for(; i < fileNum ; i++) { // 創建分割文件 int j= 0 ; for(;j < partFileSize ; j++) { // 讀寫數據 } } } // 不可平均分配大小 else { // 前面的文件平分文件大小 , 將剩下的大小全部給最后一個文件 if(fileSize % (fileNum-1)){ int i= 0 ; for(; i < (fileNum-1) ; i++) { // 創建分割文件 int j= 0 ; for(;j < partFileSize ; j++) { // 讀寫數據 } } }else { // 將剩余大小fileSize % (fileNum-1)寫入到最后一個文件中 int j = 0 ; for(;j < fileSize % (fileNum-1)){ // 讀寫數據 } } } ``` 文件分割算法就分析到這里 , 下面我們來看看具體的實現: > java native code ```java /*分割文件*/ private void fileSplit() { String splitFilePath = SD_CARD_PATH+File.separatorChar+"neck.jpg" ; String suffix = ".b"; try{ HelloNDK.fileSplit(splitFilePath,suffix,3); }catch (Exception e) { e.printStackTrace(); } } /*文件合并*/ private void fileMerge() { String splitFilePath = SD_CARD_PATH+File.separatorChar+"neck.jpg" ; String splitSuffix = ".b"; String mergeSuffix = ".jpeg"; try{ HelloNDK.fileMerge(splitFilePath,splitSuffix,mergeSuffix,3); }catch (Exception e) { e.printStackTrace(); } } ``` > 具體實現: ```c /*文件分割*/ JNIEXPORT void JNICALL Java_com_zeno_encryptanddecrypt_ndk_HelloNDK_fileSplit(JNIEnv *env, jclass type, jstring splitFilePath_, jstring suffix_, jint fileNum) { const char *splitFilePath = (*env)->GetStringUTFChars(env, splitFilePath_, 0); const char *suffix = (*env)->GetStringUTFChars(env, suffix_, 0); // 要分割文件 , 首先要得到分割文件的路徑列表 ,申請動態內存存儲路徑列表 char** split_path_list = (char**)malloc(sizeof(char*) * fileNum); // 得到文件大小 long file_size = getFileSize(splitFilePath); // 得到路徑字符長度 int file_path_str_len = strlen(splitFilePath); // 組合路徑 char file_path[file_path_str_len + 5] ; strcpy(file_path,splitFilePath); strtok(file_path,"."); strcat(file_path,"_%d"); strcat(file_path,suffix); int i=0 ; for (; i < fileNum; ++i) { // 申請單個文件的路徑動態內存存儲 split_path_list[i] = (char*)malloc(sizeof(char) * 128); // 組合分割的單個文件路徑 sprintf(split_path_list[i],file_path,(i+1)) ; LOGE("MainActivity","%s",split_path_list[i]); } LOGE("MainActivity","文件大小 == %ld",file_size); LOGE("MainActivity","文件路徑 == %s",splitFilePath); // 讀文件 FILE* fp = fopen(splitFilePath,"rb"); if(fp == NULL) { LOGE("MainActivity","%s","文件不存在,或文件不可讀"); return; } // 整除 , 說明各個文件劃分大小一致 if (file_size % fileNum) { // 單個文件大小 int part_file_size = file_size/fileNum ; LOGE("MainActivity","單個文件大小 == %d",part_file_size); int i = 0 ; // 分割多少個文件就分段讀多少次 for (; i < fileNum; i++) { // 寫文件 FILE* fwp = fopen(split_path_list[i],"wb"); if(fwp == NULL) { LOGE("MainActivity","%s","沒有文件寫入權限"); return; } int j = 0 ; // 單個文件有多大 , 就讀寫多少次 for (; j < part_file_size; j++) { // 邊讀邊寫 fputc(fgetc(fp),fwp) ; } // 關閉文件流 fclose(fwp); } } /*文件大小不整除*/ else{ // 不整除 int part_file_size = file_size / (fileNum -1 ) ; LOGE("MainActivity","單個文件大小 == %d",part_file_size); int i = 0 ; for (; i < (fileNum - 1); i++) { // 寫文件 FILE* fwp = fopen(split_path_list[i],"wb"); if(fwp == NULL) { LOGE("MainActivity","%s","沒有文件寫入權限") ; return; } int j = 0 ; for (; j < part_file_size; j++) { // 邊讀邊寫 fputc(fgetc(fp),fwp); } // 關閉流 fclose(fwp); } // 剩余部分 FILE* last_fwp = fopen(split_path_list[fileNum - 1],"wb") ; i= 0 ; for (; i < file_size % (fileNum -1); i++) { fputc(fgetc(fp),last_fwp) ; } // 關閉流 fclose(last_fwp); } // 關閉文件流 fclose(fp); // 釋放動態內存 i= 0 ; for (; i < fileNum ; i++) { free(split_path_list[i]) ; } free(split_path_list); (*env)->ReleaseStringUTFChars(env, splitFilePath_, splitFilePath); (*env)->ReleaseStringUTFChars(env, suffix_, suffix); } ``` 簡要提示:組合路徑的時候,使用了`strtok`來進行文件路徑的分割,將`.jpg`去掉,用`strcat`拼接了`_%d`,最后拼接了傳入的文件拓展名,這樣設計是為了讓分割的文件 , 不那么見名知意 。 `LOGE`定義的一個預處理函數,在C語言基礎系列[C語言基礎及指針⑩預編譯及jni.h分析](http://www.jianshu.com/p/569f968bcdce)有著詳細說明。 ```c #define LOGE(TAG,FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,TAG,FORMAT,__VA_ARGS__) ``` > 文件合并 文件的分割 , 是件一個文件分割成多個子文件 。文件的合并 , 則是將多個子文件合并成一個文件 。 算法分析圖: ![merge file](http://upload-images.jianshu.io/upload_images/643851-fa3ebc68a92c3958.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) > 具體實現 ```c /*合并文件*/ JNIEXPORT void JNICALL Java_com_zeno_encryptanddecrypt_ndk_HelloNDK_fileMerge(JNIEnv *env, jclass type, jstring splitFilePath_, jstring splitSuffix_, jstring mergeSuffix_, jint fileNum) { const char *splitFilePath = (*env)->GetStringUTFChars(env, splitFilePath_, 0); const char *splitSuffix = (*env)->GetStringUTFChars(env, splitSuffix_, 0); const char *mergeSuffix = (*env)->GetStringUTFChars(env, mergeSuffix_, 0); // 1. 申請split文件路徑列表動態內存 char** split_path_list = (char**)malloc(sizeof(char*) * fileNum) ; // 2. 組裝split文件路徑 int split_file_path_len = strlen(splitFilePath) ; int split_file_path_suffix_len = strlen(splitSuffix); char split_file_path[split_file_path_len + split_file_path_suffix_len] ; strcpy(split_file_path,splitFilePath); strtok(split_file_path,"."); strcat(split_file_path,"_%d"); strcat(split_file_path,splitSuffix); // 3. 組裝merge文件路徑 int merge_file_path_len = strlen(mergeSuffix); char merge_file_path[split_file_path_len + merge_file_path_len] ; strcpy(merge_file_path,splitFilePath); strtok(merge_file_path,"."); strcat(merge_file_path,mergeSuffix); LOGE("MainActivity","merge 文件路徑 = %s",merge_file_path) ; // 4. 循環得到split文件路徑列表 int file_path_str_len = strlen(split_file_path); int i= 0; for (; i < fileNum; i++) { split_path_list[i] = (char*)malloc(sizeof(char) * file_path_str_len) ; sprintf(split_path_list[i],split_file_path,(i+1)) ; LOGE("MainActivity","split文件路徑列表 = %s",split_path_list[i]) ; } // 5. 創建并打開 merge file FILE* merge_fwp = fopen(merge_file_path,"wb") ; // 6. 邊讀邊寫 , 讀多個文件,寫入一個文件 i = 0 ; for (; i < fileNum ; i++) { FILE* split_frp = fopen(split_path_list[i],"rb") ; if(split_frp == NULL) { LOGE("MainActivity","%s","文件不存在,或沒有讀文件權限"); return; } long part_split_file_size = getFileSize(split_path_list[i]); int j = 0 ; for (; j < part_split_file_size; j++) { fputc(fgetc(split_frp),merge_fwp); } // 關閉流 fclose(split_frp) ; // 每合并一個文件 ,就刪除它 remove(split_path_list[i]) ; } // 關閉文件流 fclose(merge_fwp); // 釋放動態內存 i = 0 ; for (; i < fileNum; i++) { free(split_path_list[i]) ; } free(split_path_list); LOGE("MainActivity","%s","文件合并完成") ; (*env)->ReleaseStringUTFChars(env, splitFilePath_, splitFilePath); (*env)->ReleaseStringUTFChars(env, splitSuffix_, splitSuffix); (*env)->ReleaseStringUTFChars(env, mergeSuffix_, mergeSuffix); } ``` 簡要提示:在編寫文件分割與合并的時候 , 文件的路徑千萬不要弄混了,筆者有有時候也會因為變量的提示沒細看 , 而將分割的子文件路徑與目標文件路徑弄混 ,在命名的時候 , 盡量規范 , 見名知意 。在發生錯誤的時候 , 一步一步的調試 , 首先猜測哪里發生異常的可能性比較大 , 再進行log輸出查看 。 ### 結語 文件的加密解密 , 可以引申為對數據的加密解密 , 可以使用C/C++進行加密解密, 這樣對反編譯APK增加了難度 , 也多了一層防范 , 因為java實在是太容易反編譯了, 即使不反編譯成java代碼, 通過smail文件也可以看個大概 , 還是很危險的 。 文件的分割與合并 , 對文件數據傳輸有很大益處 。 > 如果想了解更多關于Android NDK的知識 , 歡迎關注我的專題[Android NDK 開發之旅](http://www.jianshu.com/collection/a25bf14495d7) , 從零開始 , 帶你一步一步進入NDK開發的世界 。 ### 參考 [Standard C 語言標準函數庫速查](http://ganquan.info/standard-c/function/tmpnam) > 項目地址 : [https://github.com/zhuyongit/EncryptAndDecrypt](https://github.com/zhuyongit/EncryptAndDecrypt) __贊助__ 本文及本系列,由老司機學院[動腦學院](http://www.dongnaoedu.com/)特約贊助。 > 做一家受人尊敬的企業,做一位令人尊敬的老師
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看