patch so 生成
1、BsDiffDecoder.java 对so进行patch
@Override
public boolean patch(File oldFile, File newFile) throws IOException, TinkerPatchException {
//first of all, we should check input files
if (newFile == null || !newFile.exists()) {
return false;
}
//new add file
String newMd5 = MD5.getMD5(newFile);
File bsDiffFile = getOutputPath(newFile).toFile();
if (oldFile == null || !oldFile.exists()) {
FileOperation.copyFileUsingStream(newFile, bsDiffFile);
writeLogFiles(newFile, null, null, newMd5);
return true;
}
//both file length is 0
if (oldFile.length() == 0 && newFile.length() == 0) {
return false;
}
if (oldFile.length() == 0 || newFile.length() == 0) {
FileOperation.copyFileUsingStream(newFile, bsDiffFile);
writeLogFiles(newFile, null, null, newMd5);
return true;
}
//new add file
String oldMd5 = MD5.getMD5(oldFile);
if (oldMd5.equals(newMd5)) {
return false;
}
if (!bsDiffFile.getParentFile().exists()) {
bsDiffFile.getParentFile().mkdirs();
}
BSDiff.bsdiff(oldFile, newFile, bsDiffFile);
if (Utils.checkBsDiffFileSize(bsDiffFile, newFile)) {
writeLogFiles(newFile, oldFile, bsDiffFile, newMd5);
} else {
FileOperation.copyFileUsingStream(newFile, bsDiffFile);
writeLogFiles(newFile, null, null, newMd5);
}
return true;
}
2、BSDiff.java 对so进行patch
public static void bsdiff(File oldFile, File newFile, File diffFile) throws IOException {
InputStream oldInputStream = new BufferedInputStream(new FileInputStream(oldFile));
InputStream newInputStream = new BufferedInputStream(new FileInputStream(newFile));
OutputStream diffOutputStream = new FileOutputStream(diffFile);
try {
byte[] diffBytes = bsdiff(oldInputStream, (int) oldFile.length(), newInputStream, (int) newFile.length());
diffOutputStream.write(diffBytes);
} finally {
diffOutputStream.close();
}
}
patch so 合成
1、TinkerPatchService.java doApplyPatch
private static void doApplyPatch(Context context, Intent intent) {
// Since we may retry with IntentService, we should prevent
// racing here again.
if (!sIsPatchApplying.compareAndSet(false, true)) {
TinkerLog.w(TAG, "TinkerPatchService doApplyPatch is running by another runner.");
return;
}
Tinker tinker = Tinker.with(context);
tinker.getPatchReporter().onPatchServiceStart(intent);
if (intent == null) {
TinkerLog.e(TAG, "TinkerPatchService received a null intent, ignoring.");
return;
}
String path = getPatchPathExtra(intent);
if (path == null) {
TinkerLog.e(TAG, "TinkerPatchService can't get the path extra, ignoring.");
return;
}
File patchFile = new File(path);
long begin = SystemClock.elapsedRealtime();
boolean result;
long cost;
Throwable e = null;
PatchResult patchResult = new PatchResult();
try {
if (upgradePatchProcessor == null) {
throw new TinkerRuntimeException("upgradePatchProcessor is null.");
}
result = upgradePatchProcessor.tryPatch(context, path, patchResult);
} catch (Throwable throwable) {
e = throwable;
result = false;
tinker.getPatchReporter().onPatchException(patchFile, e);
}
2、UpgradePatch.java tryPatch分别对dex、so、res patch加载
if (!DexDiffPatchInternal.tryRecoverDexFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch dex failed");
return false;
}
if (!BsDiffPatchInternal.tryRecoverLibraryFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch library failed");
return false;
}
if (!ResDiffPatchInternal.tryRecoverResourceFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch resource failed");
return false;
}
3、BsDiffDecoder.java 对so进行patch
protected static boolean tryRecoverLibraryFiles(Tinker manager, ShareSecurityCheck checker, Context context,
String patchVersionDirectory, File patchFile) {
if (!manager.isEnabledForNativeLib()) {
TinkerLog.w(TAG, "patch recover, library is not enabled");
return true;
}
String libMeta = checker.getMetaContentMap().get(SO_META_FILE);
if (libMeta == null) {
TinkerLog.w(TAG, "patch recover, library is not contained");
return true;
}
long begin = SystemClock.elapsedRealtime();
boolean result = patchLibraryExtractViaBsDiff(context, patchVersionDirectory, libMeta, patchFile);
long cost = SystemClock.elapsedRealtime() - begin;
TinkerLog.i(TAG, "recover lib result:%b, cost:%d", result, cost);
return result;
}
private static boolean patchLibraryExtractViaBsDiff(Context context, String patchVersionDirectory, String meta, File patchFile) {
String dir = patchVersionDirectory + "/" + SO_PATH + "/";
return extractBsDiffInternals(context, dir, meta, patchFile, TYPE_LIBRARY);
}
4、BsDiffPatchInternal.java extractBsDiffInternals 进入BSPatch
InputStream oldStream = null;
InputStream newStream = null;
try {
oldStream = apk.getInputStream(rawApkFileEntry);
newStream = patch.getInputStream(patchFileEntry);
BSPatch.patchFast(oldStream, newStream, extractedFile);
} finally {
StreamUtil.closeQuietly(oldStream);
StreamUtil.closeQuietly(newStream);
}
5、BSPatch.java 进行patch
public static int patchFast(InputStream oldInputStream, InputStream diffInputStream, File newFile) throws IOException {
if (oldInputStream == null) {
return RETURN_OLD_FILE_ERR;
}
if (newFile == null) {
return RETURN_NEW_FILE_ERR;
}
if (diffInputStream == null) {
return RETURN_DIFF_FILE_ERR;
}
byte[] oldBytes = BSUtil.inputStreamToByte(oldInputStream);
byte[] diffBytes = BSUtil.inputStreamToByte(diffInputStream);
byte[] newBytes = patchFast(oldBytes, oldBytes.length, diffBytes, diffBytes.length, 0);
OutputStream newOutputStream = new FileOutputStream(newFile);
try {
newOutputStream.write(newBytes);
} finally {
newOutputStream.close();
}
return RETURN_SUCCESS;
}