所谓增量更新就是仅仅更新更改过的部分,这样当用户更新自己的apk的时候不用将整个apk重新下载一遍只需要下载差量文件(也就是久版本和新版本产生的一个差异文件,下文有介绍)。这样可以为客户省去了宝贵的流量,同时看起来更吊一些。
下文提到的增量更新有一个缺点:比如当前的apk版本有1.0 2.0 3.0 4.0 5.0 这个时候我的差量文件就需要有1 + 2 + 3 + 4个。为什么这样呢?1.0到2.0需要一个差量文件、1.0到3.0需要一个差量文件、1.0到4.0需要一个差量文件、1.0到5.0需要一个差量文件这样就需要4个,其它类似。
打补丁的过程如果放在客户端进行,那么客户需要下载整个apk和本地的进行对比,这样就又违初衷了。各位大神如果有更好的办法不妨告知在下。
闲话少扯进入正题。
一、差量文件的制作
环境:Linux
所谓的差量文件就是通过bsdiff工具对比两个文件产生的一个补丁文件。有了这个差量文件之后就可以将该文件当做补丁打到久版本的文件上,如果打补丁成功的话该久文件就和新的文件一个样子了。
这个差量文件需要在linux系统下进行制作,我在编译bsdiff工具的时候总是出现各种各样的错误,实在郁闷。最后就索性不自己编译了,直接在linux系统上安装一个bsdiff工具。
安装的命令:sudo apt-get install bsdiff
等待安装完成即可愉快的使用bsdiff的命令了。
我们将会用到两个命令:
1.生成拆分包:
命令:bsdiff old.file new.file add.patch ,即old.file是旧的文件,new.file是新更改变化的文件,add.patch是这两个文件的差异文件.
2.旧文件和差分包合成新文件:
命令:bspatch old.file createNew.file add.patch 其中createNew.file是合并后的新文件
二、 合成新的apk
环境:Eclipse
这里合并新的apk代码实在C中实现的,因此要用到jni。
首先来看java代码的描述:
public class PatchNative {
static{
System.loadLibrary("bsdiff");
}
/*java程序通过调用这个本地方法实现文件的合并
参数说明:
@oldApk:当前apk的绝对路径
@newApk:新生成的apk存放的绝对路径
@patch:增量文件的绝对路径
*/
public native int bspatch(String oldApk, String newApk, String patch);
}
在java层只需要调用这个本地方法即可。
当成功合并apk之后可以调用一个方法进行安装:
public class ApkExtract {
private final static String TAG = "ApkExtract";
//主程序可以调用该方法得到当前apk的绝对路径
public static String extract(Context context) {
context = context.getApplicationContext();
ApplicationInfo applicationInfo = context.getApplicationInfo();
String apkPath = applicationInfo.sourceDir;
Log.i(TAG, apkPath);
return apkPath;
}
/*
主程序中可以调用该方法进行安装新的apk,apkPath新的apk的路径
*/
public static void install(Context context, String apkPath) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setDataAndType(Uri.fromFile(new File(apkPath)),
"application/vnd.android.package-archive");
context.startActivity(i);
android.os.Process.killProcess(android.os.Process.myPid());
}
}
其次再看C代码的实现:
//如果是C++的代码调用C的函数这里需要加上extern "C"
extern "C"
{
const char* TAG = "Native";
/*
这个函数是具体实现合并的,封装在C代码中。这个是高手写好的可以去到
https://github.com/hymanAndroid/tools/tree/master/bsdiff-4.3这里下载bsdiff.c bspatch.c
*/
extern int patchMethod(int argc,char * argv[]);
//bspatch多对应的本地方法
int Native_bspatch(JNIEnv* env, jobject obj,jstring old,jstring dest,jstring myPatch)
{
int argc = 4;
char * argv[argc];
argv[0] = "bspatch";
//这里我们需要将jstring类型转成我们需要的char*
argv[1] = (char*)env->GetStringUTFChars(old,0);
argv[2] = (char*)env->GetStringUTFChars(dest,0);
argv[3] = (char*)env->GetStringUTFChars(myPatch,0);
int ret = patchMethod(argc, argv);
env->ReleaseStringUTFChars(old,argv[1]);
env->ReleaseStringUTFChars(dest,argv[2]);
env->ReleaseStringUTFChars(myPatch,argv[3]);
return ret;
}
static JNINativeMethod gMethods[] =
{
{ "bspatch", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", (void*) Native_bspatch}
};
void ThrowExceptionToJava(JNIEnv* env, string strNote)
{
jclass clazzException = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(clazzException, strNote.c_str());
}
/* 加载JNI库 */
jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
__android_log_write(ANDROID_LOG_DEBUG, "Native",
"JNI_OnUnload");
JNIEnv* env = NULL;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
return JNI_ERR;
jclass clazz = env->FindClass("com/gn/hellop/PatchNative");
if (NULL == clazz)
{
__android_log_write(ANDROID_LOG_DEBUG, "DiagMain",
"can't find com/gn/hellop/PatchNative");
ThrowExceptionToJava(env, "Unable to find class com/gn/hellop/PatchNative");
}
env->RegisterNatives(clazz, gMethods,
(int)(sizeof(gMethods) / sizeof(gMethods[0])));
return JNI_VERSION_1_4;
}
/* 卸载JNI库 */
void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
{
__android_log_write(ANDROID_LOG_DEBUG, "Native",
"JNI_OnUnload");
}
}
这里还需要文件bzlib的支持可以去到这里下载:
http://www.bzip.org/downloads.html http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
将下载后的.c 文件和 .h文件拿到工程中进行编译。
在编译的过程中如果遇到重复定义main函数,不用担心将其屏蔽掉即可。
欢迎各路大神给予指正。
参考文章:http://www.2cto.com/kf/201610/555426.html
源代码下载地址:http://download.csdn.net/detail/chengbaojin/9680735