Android实践:你还没使用增量更新
来源:互联网 发布:儿童画画板软件 编辑:程序博客网 时间:2024/05/19 13:17
一、增量更新原理
从Android4.1开始,Google Play引入了Smart app update方式更新App,即增量更新。它的原理如下:
1.服务端:生成最新版本apk和手机上已安装版本的apk二进制对比(bsddiff二进制比较工具)的差分包;
2.手机端:发现需要更新时下载差分包,并使用差分包与已安装版本的apk合并成最新版的apk。使用散列算法(MD5或SHA1)校验合成的apk是否完成,如果完成即重新安装;
二、增量更新业务
1.生成差分包
因为当前使用用户可能分布在已发布的不同版本,故需要在服务端生成最新版本和已发布所有版本的差分包,如:已发布版本有1.0、2.0和3.0,当前发布版本4.0。则需要生成以下查分包:
1.0->4.0差分包;
2.0->4.0差分包;
3.0->4.0差分包;
注意:对于版本跨度非常大的用户,未了避免生成大量的差分包,可以对这部分用户进行整包升级;
三、增量更新展示
1.下载bsdiff工具
下载地址:http://download.pokorra.de/coding/bsdiff_win_exe.zip;
bspatch.c:39:6: error: conflicting types for 'errx'
void errx(char a, char* format)
^
bspatch.c:34:6: note: previous definition of 'errx' was here
void errx(char a, char* format, char* param)
处理1:方法的命名冲突,将两个函数的名称修改为不同名字,修改相关方法调用处即可;
2.使用bsdiff工具生成差分patch包(这里我们直接下载bsdiff_win_exe.zip)
D:\bsdiff>bsdiff.exe app-old.apk app-new.apk patch.patch
3.使用bspatch工具和差分patch包合并新版本app-patch.apk
D:\bsdiff>bspatch.exe app-old.apk app-patch.apk patch.patch
4.校验编译打包和合并生成的新版本apk的md5值,md5完全一致,实验成功!
1.NDK环境搭建
该实践环境为:Android Studio2.2.2,Gradle2.2.2;
SDK Tools:安装SDK Tools LLDB、CMake和NDK;
2.下载bsdiff和bzip2源码
bsdiff地址:http://ftp.bestcom.ru/FreeBSD/ports/distfiles/bsdiff-4.1.tar.gz
bzip2地址:http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
注意:Android系统上下载BSD Protection License版本bsdiff-4.1.tar.zz;
3.添加bsdiff和bzip2源码
在项目创建src/main/cpp目录,并将bsdiff和bzip2源码添加如下,删除bzip2中非.c后缀的文件删除:
4.修改bsdiff源码
修改bsdiff.c,bspatch.c源码,将原来的main方法暴露给java层调用,引入bzip2源码。如下:
bsdiff.c
创建DiffUtils.java,PatchUtils.java类,创建c代码的java声明。如下:
DiffUtils.java
cpp/CMakeLists.txt
SmartUpdateActivity.java
8.运行测试
java.lang.NoSuchFieldError: com.qproject.smartupdate.R$id.sample_text
at com.qproject.smartupdate.SmartUpdateActivity.onCreate(SmartUpdateActivity.java:15)
at android.app.Activity.performCreate(Activity.java:5193)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1090)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2189)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2286)
at android.app.ActivityThread.access$600(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1259)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5166)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:768)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:584)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
at dalvik.system.NativeStart.main(Native Method)
处理2:引入的模块与原来的模块,是不是存在R.id的命令冲突;
提示3:
CMake Error at CMakeLists.txt:7 (add_subdirectory):
The source directory
C:/Users/chengxiang.peng.QUNARSERVERS/GitHubSources/QProject/smartupdate/src/main/cpp/bzip2
does not contain a CMakeLists.txt file.
处理3:在bzip2目录下创建CMakeList.txt文件,具体代码如上;
从Android4.1开始,Google Play引入了Smart app update方式更新App,即增量更新。它的原理如下:
1.服务端:生成最新版本apk和手机上已安装版本的apk二进制对比(bsddiff二进制比较工具)的差分包;
2.手机端:发现需要更新时下载差分包,并使用差分包与已安装版本的apk合并成最新版的apk。使用散列算法(MD5或SHA1)校验合成的apk是否完成,如果完成即重新安装;
二、增量更新业务
1.生成差分包
因为当前使用用户可能分布在已发布的不同版本,故需要在服务端生成最新版本和已发布所有版本的差分包,如:已发布版本有1.0、2.0和3.0,当前发布版本4.0。则需要生成以下查分包:
1.0->4.0差分包;
2.0->4.0差分包;
3.0->4.0差分包;
注意:对于版本跨度非常大的用户,未了避免生成大量的差分包,可以对这部分用户进行整包升级;
三、增量更新展示
1.下载bsdiff工具
下载地址:http://download.pokorra.de/coding/bsdiff_win_exe.zip;
注意:
采用gcc编译源码方式,下载源码时注意区分windows版本(bsdiff_win_src.zip)和bsd版本(bsdiff-4.1.tar.gz);
bsdiff依赖于bzip2代码,下载相关源码并修改源代码中的include;
bspatch.c:39:6: error: conflicting types for 'errx'
void errx(char a, char* format)
^
bspatch.c:34:6: note: previous definition of 'errx' was here
void errx(char a, char* format, char* param)
处理1:方法的命名冲突,将两个函数的名称修改为不同名字,修改相关方法调用处即可;
2.使用bsdiff工具生成差分patch包(这里我们直接下载bsdiff_win_exe.zip)
D:\bsdiff>bsdiff.exe app-old.apk app-new.apk patch.patch
3.使用bspatch工具和差分patch包合并新版本app-patch.apk
D:\bsdiff>bspatch.exe app-old.apk app-patch.apk patch.patch
4.校验编译打包和合并生成的新版本apk的md5值,md5完全一致,实验成功!
D:\bsdiff>certutil -hashfile app-new.apk MD5MD5 哈希(文件 app-new.apk):02 75 d0 8e ff ca b9 4b c9 70 84 4e 86 b3 6b 5cCertUtil: -hashfile 命令成功完成。D:\bsdiff>certutil -hashfile app-patch.apk MD5MD5 哈希(文件 app-patch.apk):02 75 d0 8e ff ca b9 4b c9 70 84 4e 86 b3 6b 5cCertUtil: -hashfile 命令成功完成。四、增量更新实践
1.NDK环境搭建
该实践环境为:Android Studio2.2.2,Gradle2.2.2;
SDK Tools:安装SDK Tools LLDB、CMake和NDK;
2.下载bsdiff和bzip2源码
bsdiff地址:http://ftp.bestcom.ru/FreeBSD/ports/distfiles/bsdiff-4.1.tar.gz
bzip2地址:http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
注意:Android系统上下载BSD Protection License版本bsdiff-4.1.tar.zz;
3.添加bsdiff和bzip2源码
在项目创建src/main/cpp目录,并将bsdiff和bzip2源码添加如下,删除bzip2中非.c后缀的文件删除:
4.修改bsdiff源码
修改bsdiff.c,bspatch.c源码,将原来的main方法暴露给java层调用,引入bzip2源码。如下:
bsdiff.c
#include <fcntl.h>... ...//引入bzip2源码#include "bzip2/bzlib.h"#define MIN(x,y) (((x)<(y)) ? (x) : (y))... ...//修改main方法为genpatchint genpatch(int argc,char *argv[]){ int fd; u_char *old,*new; ... ... return 0;}//暴露方法给java层调用JNIEXPORT jint JNICALL Java_com_qunar_home_utils_DiffUtils_diff(JNIEnv *env,jclass cls, jstring old, jstring new, jstring patch){ int argc = 4; char * argv[argc]; argv[0] = "bsdiff"; argv[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0)); argv[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0)); argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0)); //调用原来的main方法生成差分包 int ret = genpatch(argc, argv); (*env)->ReleaseStringUTFChars(env, old, argv[1]); (*env)->ReleaseStringUTFChars(env, new, argv[2]); (*env)->ReleaseStringUTFChars(env, patch, argv[3]); return ret;}bspatch.c
//引入bzip2源码#include "bzip2/bzlib.h".. ...//修改原来main方法名为applypatchint applypatch(int argc,char * argv[]){ FILE * f, * cpf, * dpf, * epf; ... ... return 0;}//将方法暴露java层调用JNIEXPORT jint JNICALL Java_com_qunar_home_utils_PatchUtils_patch(JNIEnv *env,jobject obj, jstring old, jstring new, jstring patch){ char * ch[4]; ch[0] = "bspatch"; ch[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0)); ch[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0)); ch[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0)); //调用原来的main方法 int ret = applypatch(4, ch); (*env)->ReleaseStringUTFChars(env, old, ch[1]); (*env)->ReleaseStringUTFChars(env, new, ch[2]); (*env)->ReleaseStringUTFChars(env, patch, ch[3]); return ret;}5.增加java层c代码调用方法声明;
创建DiffUtils.java,PatchUtils.java类,创建c代码的java声明。如下:
DiffUtils.java
/** * 二进制对比工具类,提供二进制对比生成查分包方法 */public class DiffUtils { /** * native方法声明,将旧文件和新文件进行二进制对比,生成查分文件。这里我们用来生成新就版本apk的查分包 * * @param oldFilePatch 对比旧版本文件路径,这里为旧版本apk * @param newFilePath 对比新版本文件路径,这里为新版本的apk * @param patchPath 对比生成差分文件路径,这里为新旧版本apk的二进制差分文件 * @return 操作返回码,0-代表成功 */ public static native int diff(String oldFilePatch, String newFilePath, String patchPath);}PatchUtils.java
/** * 二进制合并工具类,提供二进制合并方法 */public class PatchUtils { /** * native方法声明,将旧文件和差分文件进行合并,生成新文件。这里我们用来生成合并生成新版本的apk * * @param oldFilePatch 合并旧版本文件路径,这里为旧版本apk * @param newFilePath 合并生成的新版本文件路径,这里为合成生成的新版本的apk * @param patchPath 合并差分文件路径,这里为新老版本apk的二进制差分文件 * @return 操作返回码,0-代表成功 */ public static native int patch(String oldFilePatch, String newFilePath, String patchPath);}5.添加CMakeLists.txt,修改build.gradle构建配置:
cpp/CMakeLists.txt
#CMake最低版本号要求cmake_minimum_required(VERSION 3.4.1)#项目信息project(smartupdate)#添加bzip2子目录add_subdirectory(bzip2)#查找当前目录下的所有源文件,并将名称保存到DIR_SRCS变量中aux_source_directory(. DIR_SRCS)add_library(smartupdate SHARED ${DIR_SRCS})#添加链接库log,bzip2find_library(log-lib log)target_link_libraries(smartupdate ${log-lib})target_link_libraries(smartupdate bzip2)cpp/bzip2/CMakeLists.txt
# 查找当前目录下的所有源文件,并将名称保存到 DIR_LIB_SRCS 变量aux_source_directory(. DIR_LIB_SRCS)# 生成链接库add_library (bzip2 ${DIR_LIB_SRCS})build.gradle
apply plugin: 'com.android.application'... ...android { ... ... externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" } }}... ...6.Java代码调用c代码进行patch包的生成,apk包合并,合成apk的安装
SmartUpdateActivity.java
public class SmartUpdateActivity extends AppCompatActivity { String externalStoragePath = Environment.getExternalStorageDirectory().getPath() + "/qproject/"; String newPath = externalStoragePath + "app-new.apk"; String oldPath = externalStoragePath + "app-old.apk"; String patchPath = externalStoragePath + "patch.patch"; String mergePath = externalStoragePath + "merge.apk"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_smartupdate); } public void generateDiffPatch(View view) { //产生差异patch包 int resultCode = DiffUtils.diff(oldPath, newPath, patchPath); if (resultCode == 0) { Toast.makeText(this, "generateDiffPatch success!", Toast.LENGTH_SHORT).show(); } } public void mergePatchApk(View view) { //合并差异patch包,生成新apk int resultCode = PatchUtils.patch(oldPath, mergePath, patchPath); if (resultCode == 0) { Toast.makeText(this, "mergePatchApk success!", Toast.LENGTH_SHORT).show(); } } public void updatePatchApk(View view) { File mergeFile = new File(mergePath); if (mergeFile.exists()) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(Uri.fromFile(mergeFile), "application/vnd.android.package-archive"); startActivity(intent); } } //加载smartupdate库 static { System.loadLibrary("smartupdate"); }}7.这里我们仅仅是为了演示diff和patch的过程,并没有考虑异步处理,网络包下载,md5校验等等具体业务逻辑,故预先在storage/sdcard0/qproject/目录下,放下新老apk文件;
8.运行测试
四、代码库
QProject:https://github.com/Pengchengxiang/QProject 分支:feature/smartupdate
提示2:java.lang.NoSuchFieldError: com.qproject.smartupdate.R$id.sample_text
at com.qproject.smartupdate.SmartUpdateActivity.onCreate(SmartUpdateActivity.java:15)
at android.app.Activity.performCreate(Activity.java:5193)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1090)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2189)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2286)
at android.app.ActivityThread.access$600(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1259)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5166)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:768)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:584)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
at dalvik.system.NativeStart.main(Native Method)
处理2:引入的模块与原来的模块,是不是存在R.id的命令冲突;
提示3:
CMake Error at CMakeLists.txt:7 (add_subdirectory):
The source directory
C:/Users/chengxiang.peng.QUNARSERVERS/GitHubSources/QProject/smartupdate/src/main/cpp/bzip2
does not contain a CMakeLists.txt file.
处理3:在bzip2目录下创建CMakeList.txt文件,具体代码如上;
新技术,新未来!欢迎大家关注“1024工场”微信服务号,时刻关注我们的最新的技术讯息!(甭客气!尽情的扫描或者长按!)
2 0
- Android实践:你还没使用增量更新
- 手把手带你实现Android增量更新
- 手把手带你实现Android增量更新
- 增量更新实践
- Android-App增量更新的使用姿势
- Android App 增量更新的使用姿势
- android还没入门
- android 增量更新应用
- Android 增量更新实例
- android 增量更新
- android增量更新demo
- android实现增量更新
- android 增量更新
- Android 增量更新实例
- Android增量更新
- Android 增量更新APK
- Android APP增量更新
- Android之增量更新
- 20161113
- 数据库练习
- 洛谷 P1174 打砖块
- 关于数组
- 【福利】小米手机修改MAC地址教程
- Android实践:你还没使用增量更新
- 基于浏览器的HTML5 Geolocation API (地理位置应用程序接口)查找地理位置
- Linux互斥锁
- 学习javascript数据结构(二)——链表
- 数据库制表
- 使用Banner实现图片的轮番显示的效果,替换ViewPager + CirclePagerIndicator
- poj2785(折半枚举)
- JSON必知必会
- java程序运行机制和虚拟机(JVM)