之前寫過一篇博客是:手把手教你在Android中使用bsdiff實作檔案增量更新 ,
由于Android Studio自帶NDK的環境,所以實作JNI是比較簡單的,
但是在博客中也說到了,檔案差分的功能肯定是要在服務端進行的,而服務端運行的環境可能是在window,mac或者linux等,所以,我們需要把bsdiff的原始碼編譯成對應環境需要的 native 庫檔案,以便于給Java呼叫,
由于大多數專案基本都是在linux上運行的,這里我就只演示下如何把bsdiff原始碼編譯成so檔案,然后在java代碼中呼叫,
廢話不多說,開始干!
Clion搭建編譯環境
這里我使用的Jetbrains全家桶中的Clion. Jetbrains的IDE不用多說了,懂得都懂,
首先是需要準備一個Linux的系統,Windows的話本地除錯推薦直接用WSL安裝ubuntu,安裝方式也比較簡單,按照官方檔案走即可,WSL安裝檔案:
如果是MAC的話,本地除錯推薦使用docker去安裝linux,在使用Docker作為Clion的編譯環境時,一定要安裝那種已經配置過cmkae和gcc等編譯環境的鏡像,否則Clion識別不出來,我之前是先安裝的ubuntu,然后再安裝cmake,gdb等環境,結果Clion死活找不到,一直提示未找到安裝包,在這個地方卡了快一天,給我整的都懷疑人生了,后來還是按照官方檔案的方式才成功的,官方檔案:Using Docker with CLion
jetbrains在github上也提供了幾個寫好的Dockerfile,自己根據需要選擇即可,clion-remote
另外一種是比較推薦的方式,去阿里云購買一個云主機,直接遠端編譯,然后把寫好的代碼發布到遠端進行測驗,windows和mac都能用,比較方便,
安裝完linux環境準備好之后需要安裝cmake,gdb等,這里就不多BB了,安裝后去Clion配置下工具鏈,根據自己需要配置,如下,

然后是配置cmake

至此環境就配置好了
在Linux上將C編譯成so
在之前Android使用bsdiff的博客中,我們已經對bsdiff做了處理了,也能在Android上打出so,
那在java web專案中也是一樣的步驟,準備java宣告的native代碼,更改cpp代碼,配置cmake即可,
需要注意的是Android專案中的NDK可以直接引入jni.h,在Clion中我們要把需要手動把jdk中的jni.h和jni_md.h 拷貝到專案里,
jni.h在jdk中的includ目錄里,jni_md.h在win32的目錄里,

代碼的結構如下

然后就可以去編譯so檔案了,點擊build->Rebuild Project即可,

等待專案構建完成,如下

可以看到完成了,然后去上面紅框給定的目錄看一下檔案,
可以看到,linux上是有原始碼的,相當于Clion把你本地的代碼上傳到linux中,然后通過linux的環境來進行編譯了,

切到指定的最終目錄看下

上面紅框的so檔案就是我們需要的了,
至此,linux上編譯出so檔案就搞定了,
Java Web Jni實作
然后就簡單了,就是正常的JNi實作,先貼一下專案目錄,

注意要加一下java代碼訪問resources目錄中so檔案的配置
下面是一些關鍵代碼,這里只是demo代碼,以實作功能為主,在生產環境下要注意下代碼健壯性,
package com.yzq.bsdiffserver.utils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
/**
* @description: 用來load so檔案,先將專案的so拷貝到linux中,然后加載
* @author : yuzhiqiang (zhiqiang.yu.xeon@gmail.com)
* @date : 2021/12/18
* @time : 14:33
*/
public class LibLoader {
public static void loadLib(String libName, String resourcePath) {
System.out.println("libName = " + libName);
System.out.println("resourcePath = " + resourcePath);
/*獲取當前專案所在的linux路徑 示例:/home/admin/webapp */
final String projectPath = System.getProperty("user.dir");
System.out.println("projectPath = " + projectPath);
/*創建一個目錄 用來放so*/
String nativeLibPath = projectPath + File.separator + "tmp" + File.separator + "bsdiffdemo" + File.separator + "lib" + File.separator;
File nativeLibFolder = new File(nativeLibPath);
if (!nativeLibFolder.exists()) {
nativeLibFolder.mkdirs();
}
/*用來存放臨時檔案的目錄*/
String filePath = projectPath + File.separator + "tmp" + File.separator + "bsdiffdemo" + File.separator + "file" + File.separator;
File fileFolder = new File(filePath);
if (!fileFolder.exists()) {
fileFolder.mkdirs();
}
File libFile = new File(nativeLibFolder, libName);
System.out.println("libFile.getAbsolutePath() = " + libFile.getAbsolutePath());
if (libFile.exists()) {
System.out.println("libFile 檔案存在 libFile.getAbsolutePath() = " + libFile.getAbsolutePath());
System.load(libFile.getAbsolutePath());
} else {
try {
final InputStream inputStream = new ClassPathResource(resourcePath).getInputStream();
// InputStream in = LibLoader.class.getResourceAsStream(resourcePath);
final FileOutputStream fileOutputStream = new FileOutputStream(libFile);
/*把檔案存到臨時目錄里*/
final int copy = FileCopyUtils.copy(inputStream, fileOutputStream);
System.out.println("copy result= " + copy);
inputStream.close();
fileOutputStream.close();
System.out.println("libFile.getAbsolutePath() = " + libFile.getAbsolutePath());
System.load(libFile.getPath());
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Failed to load required lib", e);
}
}
}
}
工具類代碼,跟Android上類似,
package com.yzq.bsdiffserver.utils;
import java.io.File;
/**
* @author : yuzhiqiang (zhiqiang.yu.xeon@gmail.com)
* @description: BsDiffUtil
* @date : 2021/12/18
* @time : 14:33
*/
public class BsDiffUtil {
static {
String systemType = System.getProperty("os.name");
System.out.println("systemType = " + systemType);
try {
/*獲取當前專案所在的linux路徑 示例:/home/admin/webapp */
// final String projectPath = System.getProperty("user.dir");
String libPath = "lib" + File.separator + "libxeon_bsdiff.so";
String libName = "libxeon_bsdiff.so";
/*mac上使用dylib*/
// String libPath = "lib" + File.separator + "libxeon_bsdiff.dylib";
// String libName = "libxeon_bsdiff.dylib";
LibLoader.loadLib(libName, libPath);
System.out.println("load so success");
} catch (Exception e) {
System.out.println("e = " + e.getMessage());
e.printStackTrace();
}
}
/**
* 生成補丁檔案
*
* @param newFilePath
* @param oldFilePath
* @param patchFilePath
* @return
*/
public static native int fileDiff(String newFilePath, String oldFilePath, String patchFilePath);
/**
* 合并檔案
*
* @param oldFilePath
* @param patchFilePath
* @param combineFilePath
* @return
*/
public static native int filePatch(String oldFilePath, String patchFilePath, String combineFilePath);
}
測驗方法
package com.yzq.bsdiffserver.service;
import com.yzq.bsdiffserver.utils.BsDiffUtil;
import java.io.File;
/**
* @description: 測驗類
* @author : yuzhiqiang (zhiqiang.yu.xeon@gmail.com)
* @date : 2021/12/18
* @time : 14:55
*/
public class BsDiffService {
public static void testBsDiff() {
try {
/*測驗load so*/
// BsDiffUtil.test();
final String projectPath = System.getProperty("user.dir");
String filePath = projectPath + File.separator + "tmp" + File.separator + "bsdiffdemo" + File.separator + "file" + File.separator;
/*舊檔案路徑*/
String oldFilePath = filePath + "old.txt";
/*新檔案*/
String newFilePath = filePath + "new.txt";
/*補丁檔案*/
String patchFilePath = filePath + "patch.txt";
/*合并后的檔案*/
String combineFilePath = filePath + "combine.txt";
System.out.println("oldFilePath = " + oldFilePath);
System.out.println("newFilePath = " + newFilePath);
System.out.println("patchFilePath = " + patchFilePath);
System.out.println("combineFilePath = " + combineFilePath);
/*生成補丁檔案*/
final int diffResult = BsDiffUtil.fileDiff(newFilePath, oldFilePath, patchFilePath);
System.out.println("diffResult = " + diffResult);
/*合并補丁檔案*/
final int patchResult = BsDiffUtil.filePatch(oldFilePath, patchFilePath, combineFilePath);
System.out.println("patchResult = " + patchResult);
} catch (Exception e) {
e.printStackTrace();
}
}
}
java代碼準備好之后直接部署到遠端服務器測驗即可,
這里就直接放出測驗的結果了

存放so檔案的目錄

測驗合并差分檔案的目錄

可以看到,合并后的檔案和新檔案大小是一致的,內容我看了也是完全一致的,
到這里,從Android到后端的基于bsdiff增量更新功能技術方面就打通了,具體應用的話根據你們的需求來定,
大冬天的碼字不易,手都凍僵了,覺得有幫助的點個贊支持一下…

如果你覺得本文對你有幫助,麻煩動動手指頂一下,可以幫助到更多的開發者,如果文中有什么錯誤的地方,還望指正,轉載請注明轉自喻志強的博客 ,謝謝!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/386639.html
標籤:其他
