http://blog.csdn.net/watermusicyes/article/details/50529461
项目以飞快的速度迭代,2周进行一次迭代升级。每次开发完功能跑通,在现有机型上测试没问题的话,就提交市场。
在以极快的速度迭代的时候,避免不了出现各种问题,传说中的bug
或,重大bug,需要紧急修复
或,可以下次迭代修复的bug
或,影响用户体验的行为
出现bug后,我们的一贯做法:
发布紧急版本,等待用户下载更新。
可是,并不是所有的用户都会下载更新。那该怎么办?
强制更新,这是不太友好的行为,如果人现在用的是流量,强制别人更新,会耗费别人流量,还是挺贵的!这确实有点说不过去。
有没有一种方法,在用户不下载新App的前提下修复这些bug。当然有,以下是我找到的两种解决方案。
有两种解决方案:
- Dexposed
- AndFix
Dexposed
Dexposed是基于开源Xposed框架实现的一个Android平台上功能强大的无侵入式运行时AOP框架。
Dexposed的AOP实现是完全无侵入式的,没有使用任何注解处理器,编织器或者字节码重写器。只需要在应用初始化阶段加载一个很小的JNI库就可以完成Dexposed框架的集成。
Dexposed不仅可以hook应用中的自定义函数,也可以hook你的应用进程中调用的Android框架的函数。这个特性对于Android开发者来说有很多好处,因为我们严重依赖于Android SDK的版本碎片化。
借助动态类加载技术,运行中的app可以加载一小段经过编译的Java AOP代码,在不需要重启app的前提下实现修改目标app的行为。
典型的应用场景
AOP编程
插桩(例如测试,性能监控等)
在线热更新,修复严重的,紧急的或者安全性的bug
SDK hooking以提供更好的开发体验
在这里,我只关心Dexposed在Android平台上的在线热更新Hotfix
的使用:
在Android平台,Dexposed支持函数级别的在线热更新,例如对已经发布在应用市场上的APK,当我们从crash统计平台上发现某个函数调用有bug,导致经常性crash,而这个时候离下次迭代的预定发布日期还有一段时间。这种情况下,可以在本地开发一个补丁APK,并发布到服务器中,应用市场上的APK下载这个补丁APK并集成后,就可以修复这个crash。
上述涉及到两个角色:
- 宿主apk——已经发布在应用市场上,有bug并且需要修复的apk;
- 补丁apk——在本地开发的,修复了bug的apk;
HotFix是什么
HotFix用来修复线上严重的,紧急的或者安全性的bug,这里会涉及到两个apk文件:一个我们称为宿主apk,也就是发布到应用市场的apk,一个称为补丁apk。宿主apk出现bug时,通过在线下载的方式从服务器下载补丁apk,使用补丁apk中的函数替换原来的函数,从而实现在线修复bug的功能。
如何借助Dexposed实现HotFix
- 需要再引入一个名为patchloader的jar包,这个函数库实现了一个热更新框架,宿主apk在发布时会将这个jar包一起打包进apk中
- 补丁apk只是在编译时需要这个jar包,但打包成apk时不包含这个jar包,以免补丁apk集成到宿主apk中时发生冲突
- 补丁apk将会以provided的形式依赖dexposedbridge.jar和patchloader.jar,补丁apk的build.gradle文件中依赖部分脚本如下所示
<code class="hljs livecodeserver has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">dependencies {
provided <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">files</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'libs/dexposedbridge.jar'</span>)
provided <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">files</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'libs/patchloader.jar'</span>)
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>
支持状态:
Dexposed支持从Android2.3到4.4(除了3.0)的所有dalvid运行时arm架构的设备,稳定性已经经过实践检验。
缺点:
我的设备是5.0 或者更高系统的使用起来就有点问题了。
因此,我并没打算仔细的学习Dexposed,只是做一个简要的了解。
具体使用可以参见这个博客,写的很棒:
使用示例:Android平台免Root无侵入AOP框架Dexposed使用详解
AndFix
AndFix is What ?
经过综合对比,发现AndFix可用性比较强,因此决定学习AndFix的使用,从官方文档入手。
AndFix是一个Android App的在线热补丁框架。使用此框架,我们能够在不重复发版的情况下,在线修改App中的Bug。AndFix就是 “Android Hot-Fix”的缩写。
它支持Android 2.3到6.0版本,并且支持arm与X86系统架构的设备。完美支持Dalvik与ART的Runtime。AndFix 的补丁文件是以 .apatch 结尾的文件。它从你的服务器分发到你的客户端来修复你App的bug 。
原理:
AndFix执行的原理是实现方法体的替换,如下图所示(该图片来自AndFix的github介绍):
方法替换:
AndFix通过Java的自定义注解来判断一个方法是否应该被替换,如果可以就会hook该方法并进行替换。AndFix在ART架构上的Native方法是art_replaceMethod
、在X86架构上的Native方法是dalvik_replaceMethod
。他们的实现方式是不同的。对于Dalvik,它将改变目标方法的类型为Native
同时hook方法的实现至AndFix自己的Native
方法,这个方法称为 dalvik_dispatcher
,这个方法将会唤醒已经注册的回调,这就是我们通常说的hooked
(挂钩)。对于ART来说,我们仅仅改变目标方法的属性来替代它。
更多信息,见这里:AndFix/jni/
Fix 流程,大致如下:
- 发现bug(友盟、TestIn 、用户反馈)
- 分析bug产生原因
- 创建并发布补丁
- App打补丁
在App中引入AndFix
如何来获得AndFix ?
将AndFix aar 作为library编译入你的工程:
<code class="hljs xml has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">dependency</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">groupId</span>></span>com.alipay.euler<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">groupId</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">artifactId</span>></span>andfix<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">artifactId</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">version</span>></span>0.3.1<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">version</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">type</span>></span>aar<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">type</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">dependency</span>></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>
<code class="hljs nginx has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">dependencies</span> {
<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">compile</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'com.alipay.euler:andfix:0.3.1<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">@aar</span>'</span>
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
如何使用AndFix ?
-
初始化 PatchManager
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> patchManager = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> PatchManager(context);
patchManager.init(appversion);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//current version</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
-
Load patch 加载补丁
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> patchManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.loadPatch</span>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
你应该尽可能早的加载补丁,通常都是在Application的onCreate()方法中进行初始化。
-
Add patch 添加补丁
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> patchManager.addPatch(path);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//path:补丁文件下载到本地的路径。</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
当一个新的补丁文件被下载后,调用addPatch(path)就会立即生效。
开发工具
AndFix 提供了一个补丁创建工具 apkpatch.
如何使用这个工具
1.准备两个应用apk: 一个是线上的apk,另一个是修复了bug的apk.
2.通过提供的两个apk生成补丁文件.
运行如下命令:
<code class="hljs haml has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">usage: apkpatch -f <new> -t <old> -o <output> -k <keystore> -p <***> -a <alias> -e <***>
-<span class="ruby" style="box-sizing: border-box;">a,--<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">alias</span> <<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">alias</span>> keystore entry <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">alias</span>.
</span> -<span class="ruby" style="box-sizing: border-box;">e,--epassword <***> keystore entry password.
</span> -<span class="ruby" style="box-sizing: border-box;">f,--from <loc> new <span class="hljs-constant" style="box-sizing: border-box;">Apk</span> file path.
</span> -<span class="ruby" style="box-sizing: border-box;">k,--keystore <loc> keystore path.
</span> -<span class="ruby" style="box-sizing: border-box;">n,--name <name> patch name.
</span> -<span class="ruby" style="box-sizing: border-box;">o,--out <dir> output dir.
</span> -<span class="ruby" style="box-sizing: border-box;">p,--kpassword <***> keystore password.
</span> -<span class="ruby" style="box-sizing: border-box;">t,--to <loc> old <span class="hljs-constant" style="box-sizing: border-box;">Apk</span> file path.</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>
这个时候,你就有了你的应用的救世主——补丁文件。接下来你就可以通过某种方式将该补丁文件分发给你的客户端。
- 或,adb push 到sdk
- 或,发布到服务器,客户端下载该apatch
有时候,你的团队成员可能会fix各自的bug,这个时候就会有不止一个补丁文件.apatch
。这种情况下,你可以用这个工具merge这些.apatch
文件:
<code class="hljs haml has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">usage: apkpatch -m <apatch_path...> -o <output> -k <keystore> -p <***> -a <alias> -e <***>
-<span class="ruby" style="box-sizing: border-box;">a,--<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">alias</span> <<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">alias</span>> keystore entry <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">alias</span>.
</span> -<span class="ruby" style="box-sizing: border-box;">e,--epassword <***> keystore entry password.
</span> -<span class="ruby" style="box-sizing: border-box;">k,--keystore <loc> keystore path.
</span> -<span class="ruby" style="box-sizing: border-box;">m,--merge <loc...> path of .apatch files.
</span> -<span class="ruby" style="box-sizing: border-box;">n,--name <name> patch name.
</span> -<span class="ruby" style="box-sizing: border-box;">o,--out <dir> output dir.
</span> -<span class="ruby" style="box-sizing: border-box;">p,--kpassword <***> keystore password.</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>
运行示例
- 导入samplesI/AndFixDemo进你的IDE,给AndFixDemo添加AndFix(library project 或者 aar)的依赖。
- build 工程,保存应用为1.apk,然后将该apk安装到手机或者模拟器上。
- 修改com.euler.test.A,作为Fix后的包。
- build 工程,保存应用为2.apk
- Use apkpatch tool to make a patch. 使用apkpatch 工具制作一个补丁文件。
- Rename the patch file to out.apatch, and then copy it to sdcard.重命名补丁文件为out.apatch ,然后将它拷贝到sdcard上。
- 运行1.apk然后查看log
注意
混淆
如果你使用混淆,你要保存mapping.txt,这样的话你在新版本的构建是就可以借助 “”-applymapping” 来使用它了。
混淆时需要保留下面的内容:
- Native方法 ,比如:
com.alipay.euler.andfix.AndFix
- Annotation,比如:
com.alipay.euler.andfix.annotation.MethodReplace
为了确保这些类在运行了ProGuard后,可以被找见,在ProGuard配置中添加如下代码:
<code class="hljs scala has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">-keep <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> * <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">java</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">lang</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">annotation</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Annotation</span></span>
-keepclasseswithmembernames <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> * {</span>
native <methods>;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>
局限性
如果你使用了,软件加固比如 Bangcle,为了生成补丁文件,你最好使用为经过加固的apk.使用这些加固,很可能使热补丁失效。
安全性
验证apatch文件的签名是否就是在使用apkpatch工具时使用的签名(如果不验证那么任何人都可以制作自己的apatch文件来对你的APP进行修改)
验证optimize file的指纹(防止有人替换掉本地保存的补丁文件,所以要验证MD5码)
以下是我在用demo时碰到的问题:
-
运行apkpatch报nullPointException
关于该问题的github issue : 运行apkpatch报nullPointException
解决方法:
运行命令行时 alias 填错了,应该填alias名字,我填成了alias路径。修改后,解决了问题。
<code class="hljs haml has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">zhanggeng:apkpatch-1.0.3$
./apkpatch.sh
-<span class="ruby" style="box-sizing: border-box;">f /<span class="hljs-constant" style="box-sizing: border-box;">Users</span>/zhanggeng/<span class="hljs-constant" style="box-sizing: border-box;">Desktop</span>/after/app-release.apk
</span>-<span class="ruby" style="box-sizing: border-box;">t /<span class="hljs-constant" style="box-sizing: border-box;">Users</span>/zhanggeng/<span class="hljs-constant" style="box-sizing: border-box;">Desktop</span>/before/app-release.apk
</span>-<span class="ruby" style="box-sizing: border-box;">o /<span class="hljs-constant" style="box-sizing: border-box;">Users</span>/zhanggeng/<span class="hljs-constant" style="box-sizing: border-box;">Desktop</span>/apatch/
</span>-<span class="ruby" style="box-sizing: border-box;">k /<span class="hljs-constant" style="box-sizing: border-box;">Users</span>/zhanggeng/<span class="hljs-constant" style="box-sizing: border-box;">Desktop</span>/fixdemo.jks
</span>-<span class="ruby" style="box-sizing: border-box;">p <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">123456</span>
</span>-<span class="ruby" style="box-sizing: border-box;">a fixdemo
</span>-<span class="ruby" style="box-sizing: border-box;">e <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">123456</span></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>
-
运行程序,检测是否可以使用时,抛出异常:java.util.zip.ZipException: File too short to be a zip file: 0
关于该问题的github issue: java.util.zip.ZipException: File too short to be a zip file: 0
解决方法:
这个问题是,上面那个问题造成的。只要补丁文件生成正确,是不会有这个问题的。
参考文章:
Demo下载地址:AndFixDemo