Skip to content

Commit

Permalink
Refactor DefaultFileChecker: add md5 support. and del file if check f…
Browse files Browse the repository at this point in the history
…ailed before install
  • Loading branch information
yjfnypeu committed Dec 14, 2017
1 parent 4c1acb1 commit e21fa53
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public Update parse(String httpResponse) throws Exception {
update.setForced(true);
// 是否显示忽略此次版本更新按钮
update.setIgnore(object.optBoolean("ignore_able",false));
update.setMd5("Hello world");
return update;
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ public final void run() {
try {
sendDownloadStart();
File cacheFile = builder.getFileCreator().create(update);
if (cacheFile != null && cacheFile.exists()
&& builder.getFileChecker().checkForDownload(update, cacheFile.getAbsolutePath())) {
FileChecker checker = builder.getFileChecker();
checker.attach(update, cacheFile);
if (builder.getFileChecker().checkBeforeDownload()) {
// check success: skip download and show install dialog if needed.
sendDownloadComplete(cacheFile);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.lzh.framework.updatepluginlib.impl.DefaultFileChecker;
import org.lzh.framework.updatepluginlib.model.Update;

import java.io.File;

/**
* 用于提供在更新中对apk进行有效性、安全性检查的接口
*
Expand All @@ -29,24 +31,42 @@
*
* @author haoge
*/
public interface FileChecker {
public abstract class FileChecker {

protected Update update;
protected File file;

final void attach(Update update, File file) {
this.update = update;
this.file = file;
}

final boolean checkBeforeDownload(){
if (file == null || !file.exists()) {
return false;
}

try {
return onCheckBeforeDownload();
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 在启动下载任务前。通过对设置的文件下载缓存路径进行验证
* 在启动下载任务前。对{@link FileCreator}创建的缓存文件进行验证。判断是否此文件已被成功下载完成
*
* <p>当验证成功时,则代表此文件在之前已经被下载好了。则将跳过下载任务。
*
* @param update 更新数据实体类
* @param file 被{@link FileCreator}所创建的缓存文件地址
* @return True代表当前
* <p>若下载失败
*/
boolean checkForDownload(Update update, String file);
protected abstract boolean onCheckBeforeDownload() throws Exception;

/**
* 当下载完成后。触发到此。进行文件安全校验检查。当检查成功。即可启动安装任务。安装更新apk
*
* @param update 更新数据实体类
* @param file 被{@link FileCreator}所创建的缓存文件地址
* <p>若检查失败。则可主动抛出一个异常。用于提供给框架捕获并通知给用户。
*/
void checkForInstall(Update update, String file) throws Exception;
protected abstract void onCheckBeforeInstall() throws Exception;
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public final void setFile(File file) {
*/
public final void sendToInstall() {
try {
builder.getFileChecker().checkForInstall(update, file.getAbsolutePath());
builder.getFileChecker().onCheckBeforeInstall();
builder.getInstallStrategy().install(ActivityManager.get().getApplicationContext(), file.getAbsolutePath(), update);
} catch (Exception e) {
builder.getCheckCB().onCheckError(new RuntimeException("check failed after download apk." + e.getMessage(), e));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,43 @@
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;

import org.lzh.framework.updatepluginlib.base.FileChecker;
import org.lzh.framework.updatepluginlib.model.Update;
import org.lzh.framework.updatepluginlib.util.ActivityManager;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;

/**
* 默认的apk文件检查器。
*
* 实现:通过{@link PackageManager}读取并解析对应apk文件的信息。并与{@link Update}数据进行比对。
* <p>实现逻辑:<br>
* - 若{@link Update#getMd5()}为空时。使用{@link PackageManager}读取apk的版本信息并与{@link Update#versionCode}比对<br>
* - 若{@link Update#getMd5()}不为空。则计算apk的md5值并与Update中的md5值进行比对。<br>
* - 若为在{@link #onCheckBeforeInstall()},即下载完成后启动安装任务之前的检查。当此时检查失败。则删除apk文件。
* @author haoge
*/
public class DefaultFileChecker implements FileChecker {
public class DefaultFileChecker extends FileChecker {

private void check(Update update, String file) throws Exception {
private void check() throws Exception {
// TODO: 2017/11/27 添加MD5支持
if (!TextUtils.isEmpty(update.getMd5())) {
// 当md5值不为null时。使用md5进行检查
checkByMD5();
} else {
checkByPM();
}
}

private void checkByPM() {
Context context = ActivityManager.get().getApplicationContext();
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(file, PackageManager.GET_ACTIVITIES);
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(file.getAbsolutePath(), PackageManager.GET_ACTIVITIES);
if (packageInfo.versionCode != update.getVersionCode()) {
throw new IllegalStateException(
String.format("The version code not matched between apk and update entity. apk is %s but update is %s",
Expand All @@ -44,20 +63,81 @@ private void check(Update update, String file) throws Exception {
}
}

private void checkByMD5() throws Exception{
String md5 = getMD5(file);
if (!update.getMd5().equals(md5)) {
throw new RuntimeException(
String.format("The md5 not matched between apk and update entity. apk is %s but update is %s",
md5, update.getMd5())
);
}
}

@Override
public boolean checkForDownload(Update update, String file) {
public boolean onCheckBeforeDownload() {
try {
check(update, file);
check();
return true;
} catch (Exception e) {
return false;
}
}

@Override
public void checkForInstall(Update update, String file) throws Exception {
check(update, file);
public void onCheckBeforeInstall() throws Exception {
try {
check();
} catch (Exception e) {
// 检查失败。删除apk
file.delete();
throw e;
}
}

private String getMD5(File file) throws IOException {
String md5str = "";
InputStream is = null;
try {
// 1 创建一个提供信息摘要算法的对象,初始化为md5算法对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 2 将消息变成byte数组
is = new FileInputStream(file);
byte[] buffer = new byte[8 * 1024];
int length = -1;
while ((length = is.read(buffer)) != -1) {
md.update(buffer, 0, length);
}

// 3 计算后获得字节数组,这就是那128位了
byte[] buff = md.digest();

// 4 把数组每一字节(一个字节占八位)换成16进制连成md5字符串
md5str = bytesToHex(buff);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
is.close();
}
}
return md5str;
}

private String bytesToHex(byte[] bytes) {
StringBuffer md5str = new StringBuffer();
// 把数组每一字节换成16进制连成md5字符串
int digital;
for (int i = 0; i < bytes.length; i++) {
digital = bytes[i];

if (digital < 0) {
digital += 256;
}
if (digital < 16) {
md5str.append("0");
}
md5str.append(Integer.toHexString(digital));
}
return md5str.toString().toUpperCase();
}
}

0 comments on commit e21fa53

Please sign in to comment.