app-zfz-v1.3-巫师3v1.12 hotfixx.apk

问题补充&&
本页链接:
猜你感兴趣Android(9)
apk+热补丁
参考文献:
硬件环境:macOS
:Android studio
什么是热补丁?
当一个APP发布以后,如果某个类或者模块出现bug需要修复,这时需要重新编译、打包、测试APP,之后再发到各个渠道,提示用户升级,覆盖安装更新版本,而改动却仅仅是一行代码。
这时如果服务器提供一份修复的代码,app在下载到这份代码之后,直接替换掉原来的代码该是件多么温馨的事啊。
热补丁就是完成这样的事。
热补丁原理
该原理简单的来说就是类替换:将有问题的类替换成我们修复的类。
正常的app解压之后只有一个classes.dex文件,该文件是Android Dalvik虚拟机可以执行的文件。如图:
如果你知道Android multidex(也叫Android分包)的话,就会知道一个app里是可以有多个dex文件的。
我们的修复类就在classes2.dex(后面我们会对该文件进行重新打包为途中的path_dex.jar文件,这个操作后续会说)。
我们知道,multidex方案的实现,其实就是把多个dex放进app的classloader之中,从而使得所有dex的类都能被找到。在虚拟机findClass的过程中,如果在两个dex文件中出现了重复的类,参照下面的类加载的实现,是会使用第一个找到的类的。
public Class findClass(String name, List&Throwable& suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexF
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
return null;
该热补丁方案就是从这一点出发,只要把有问题的类修复后,放到一个单独的dex,通过反射插入到dexElements数组的最前面,不就可以让虚拟机加载到打完补丁的class了吗。如果实现了这些,在app跑起来之后你会发现app崩溃。这是因为出现了这个异常:“java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation”。解决方法是通过在所有类(Application除外,当时还没加载自定义类的代码)的构造函数插入一个对在单独的dex的类的引用,就可以解决这个问题。空间使用了javaassist进行编译时字节码插入。
关于这部分内容,请参考。
下面就是具体的实现步骤了。
按照上面的所说,我们首先要实现javassist动态代码注入,在编译时将所有类的构造函数插入一个对单独的dex的类的引用。
该部分内容参考,可以直接使用该项目中得代码。我们按照该项目的说明来制作我们的sample.
1、新建一个项目,MyMultiDex。
2、使用HotFix中得buildSrc(该module是一个Groovy开发的,需要配置Groovy SDK才可以编译成功。)和hackdex两个模块:直接将该项目中得buildSrc和hackdex文件夹复制到我们项目下如图:,修改MyMultiDex中得setting.gradle文件,将这两个module导入进来:,然后点击sync project按钮,重新编译我们的项目即可。(如果没有配置Groovy SDK,会编译失败,至于如何配置,我已经忘记,请自行百度。^_^)。
builderScr的作用是动态代码注入,而hackdex module的作用是注入的单独的dex的类。关于将hackdex module中得AntilazyLoad类打包成dex的方法,HotFix中已经做了说明:
1)、进入到AntilazyLoad类所在的包,通过javac命令编译成class文件:javac dodola/hackdex/AntilazyLoad.java
2)、将class文件打入一个jar包中 jar cvf hack.jar dodola/hackdex/AntilazyLoad.class
3)、将jar包转换成dex的jar包 dx –dex –output=hackdex_dex.jar hack.jar 之后会生成hackdex_dex.jar文件
将hackdex_dex.jar文件放入app项目的assets文件中(dx命令在Android SDK目录下地/build-tools的各个版本中,任选一个即可)。
4)、在app项目中新建MainApplication文件继承Application.在onCreate()方法中添加
File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "hackdex_dex.jar");
Utils.prepareDex(this.getApplicationContext(), dexPath, "hackdex_dex.jar");
HotFix.patch(this, dexPath.getAbsolutePath(), "dodola.hackdex.AntilazyLoad");
this.getClassLoader().loadClass("dodola.hackdex.AntilazyLoad");
} catch (ClassNotFoundException e) {
e.printStackTrace();
该代码将生成的hackdex_dex.jar文件引入到项目中。
3、实现javassist动态代码注入。
在 app 项目的 build.gradle 中插入如下代码。
`task(‘processWithJavassist’) && {
String classPath = file(‘build/intermediates/classes/debug’)//项目编译class所在目录
dodola.patch.PatchClass.process(classPath, project(‘:hackdex’).buildDir
.absolutePath + ‘/intermediates/classes/debug’)//第二个参数是hackdex的class所在目录
applicationVariants.all { variant -&
variant.dex.dependsOn && processWithJavassist //在执行dx命令之前将代码打入到class中
}`,请注意这是两端代码在build.gradle中的位置。
该段的意思是在gradle编译的过程中调用dodola.patch.PatchClass类的process方法来给所有类的构造函数添加dex引用。这里的我们假定app项目中得BugActivity类存在问题。需要将process方法中写成如下所示:
然后build Apk,在Androidstudio Gradle Console窗口会出现如下log:
到此我们实现了项目的准备阶段,下面开始制作修复类的dex文件,并实现替换。
制作补丁文件
这里的补丁文件其实也是dex文件,不过跟之前制作的hackdex_dex.jar的步骤不同,因为在通过javac命令编译成class文件时,AntilazyLoad文件比较简单,没有其他类的引用,但是如果要将一个有其他类的引用的文件单独编译成class文件时会出现异常,所以这里使用google推荐的制作dex的方法(参考:http://blog.csdn.net/zylc369/article/details/)。
我们项目中BugActivity,这个Activity假定为我们要修复的。
代码如下:
import android.support.v7.app.AppCompatA
import android.os.B
import android.widget.B
public class BugActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
btn = (Button)findViewById(R.id.button);
btn.setText("这是一个bug");
这时我们来还原下我们这是的情景:打包这个APP,发到各个渠道后,发现这是一个有bug的app,这时就是我们的热补丁发挥作用的时候了。
1)、修复BugActivity:btn.setText("已经修复这个bug");
2)、项目配置
参考Android官方文档:。
3)、编译APP
4)、使用apktool工具反编译我们的app:
输入命令apktool d app-debug.apk
如图所示:
在反编译出来的文件夹中,创建一个新的文件夹smali_classes2,然后从smali文件夹中剪切出BugActivity.smali到前面的文件夹中(当然,这个类文件也需要完整的包目录,否则回译肯定无法通过)。如图:
5)、重新编译app
输入命令:apktool b app-debug,
在app-debug/dist文件夹里生成然后app,然后解压,会发现里面存在了app里多了classes2.dex文件如图:
6)、通过之前提到的jar或者dx命令将dex文件打包成压缩文件。
至于这里为什么还要打包压缩文件的原因是因为直接使用(如何使用请看下一步)classes2.dex会报错,如图:
在这个异常中,我们也能发现,其实我们需要一个zip包,并且里面要有我们的classes.dex。我们的APK其实就是一个压缩文件,我们原始的classes.dex就是在压缩文件中,这也侧面证明了这个。
进入到classes2.dex文件所在的目录,将文件名修改为classes.dex输入命令:
jar cvf hack_dex_fix.jar classes.dex
生成的hack_dex_fix.jar即使我们需要的补丁文件。
7)、使用补丁文件
将hackfix.jar拷贝到我们项目的assets目录,然后修改MainActivity代码:
import android.content.C
import android.content.I
import android.os.B
import android.support.v7.app.AppCompatA
import android.view.V
import android.widget.B
import android.widget.T
import java.io.F
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
Button fixBt,startBt;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fixBt = (Button)findViewById(R.id.fixBtn);
startBt = (Button)findViewById(R.id.startBA);
fixBt.setOnClickListener(this);
startBt.setOnClickListener(this);
public void onClick(View v) {
switch (v.getId()) {
case R.id.fixBtn:
Toast.makeText(this, "打补丁", Toast.LENGTH_SHORT).show();
File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "hack_dex_fix.jar");
Utils.prepareDex(this.getApplicationContext(), dexPath, "hack_dex_fix.jar");
HotFix.patch(this, dexPath.getAbsolutePath(), ".mymultidex.BugActivity");
case R.id.startBA:
startActivity(new Intent(MainActivity.this,BugActivity.class));
到此,我们运行我们的app。
如果直接启动BugActivity,会发现显示的是”这是一个Bug”。
谨记因为是热补丁,所以补丁要在BugActivity启动之前打上,如果BugActivity已经启动,这是就需要完全退出应用之后才能打补丁操作。
这种方法无法在已经加载好的类中实现动态替换,只能在类加载之前替换掉。就是说,补丁下载下来后,只能等待用户重启应用才能完成补丁效果。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1908次
排名:千里之外
(2)(2)(2)(1)(1)(3)

我要回帖

更多关于 whatsapp apk 的文章

 

随机推荐