创建工程├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── xyz
│ │ └── hanks
│ │ └── fix
│ │ ├── BugClass.java
│ │ ├── FixApplication.java
│ │ └── MainActivity.java
├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── xyz
│ │ └── hanks
│ │ └── fix
│ │ ├── BugClass.java
│ │ ├── FixApplication.java
│ │ └── MainActivity.java
通过Android Studio创建一个工程. BugClass
类是需要修复的类, MainActivity
是主Activity, FixApplication
是自定义的Application. 初始的MainActivity如下
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.text);
textView.setText(new BugClass().showToast("Happy new year"));
}
}
/**
* Created by hanks on 16-1-2.
*/
public class BugClass {
public String showToast(String content) {
return content;
}
}
现在运行程序,界面的文本显示成文字
Happy new year,
MainActivity
类会被加上
CLASS_ISPREVERIFIED 标志,因为BugClass 和 MainActivity 都属于同一个dex. 如果现在直接加载补丁包中的
BugClass 类,那么就会出现
Class ref in pre-verified class resolved to unexpected implementation
错误.
引用hack.dex,防止类加上CLASS_ISPREVERIFIED
因为我们要修复BugClass
类,而调用是在MainActivity
中,也就是说,当打上补丁包之后,MainActivity
调用的BugClass
将会是补丁包中的BugClass
(也就是来自于其他的dex),那么我就就需要防止MainActivity
被加上CLASS_ISPREVERIFIED 标志. 那么怎么防止呢? 需要在MainActivity
中引用别的dex(hack.dex)中的一个类.那么代码如下:
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.text);
System.out.print(Hack.class); // 引用 hack.dex中的Hack类
textView.setText(new BugClass().showToast("Happy new year"));
}
}
上面代码只是简单的引用了一下 Hack.class
, 这样程序运行起来就不会把MainActivity
加上CLASS_ISPREVERIFIED. 注意现在的代码是编译不过的. 引用我们的程序中没有Hack.class
, 要想编译通过,那么我们就得有 Hack.class
, 于是新建一个library, 然后app这个依赖与这个library, 但是注意不要使用 compile, 使用provided 关键字,这样标示这个library这是提供引用,并不被编译到apk中(不在MainActivity的dex中).这样就解决了编译问题.
现在运行起来程序还是有错误, 因为MainActivity
引用了Hack.class
,虽然编译通过了,但是实际上是没有这个类的,所以这个时候就需要在调用Hack这个类之前,先动态加载进来.
先加载Hack.dex,保证引用不会出错/**
* 加载
* Created by hanks on 16-1-3.
*/
public class FixApplication extends Application {
@Override public void onCreate() {
super.onCreate();
try {
PathClassLoader pathClassLoader = (PathClassLoader) getClassLoader();
String hackFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() +"/hack.dex"; // hack.dex的路径
Object a = combineArray(getDexElements(getPathList(pathClassLoader)), // 原有的 dex
getDexElements(getPathList(new DexClassLoader(hackFilePath, getDir("dex", 0).getAbsolutePath(), hackFilePath, getClassLoader())))); // 将新的dex插入到dexElements数组的前面
Object a2 = getPathList(pathClassLoader);
setField(a2, a2.getClass(), "dexElements", a); // 通过反射修改dexElements数组
pathClassLoader.loadClass("xyz.hanks.Hack");
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 加载
* Created by hanks on 16-1-3.
*/
public class FixApplication extends Application {
@Override public void onCreate() {
super.onCreate();
try {
PathClassLoader pathClassLoader = (PathClassLoader) getClassLoader();
String hackFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() +"/hack.dex"; // hack.dex的路径
Object a = combineArray(getDexElements(getPathList(pathClassLoader)), // 原有的 dex
getDexElements(getPathList(new DexClassLoader(hackFilePath, getDir("dex", 0).getAbsolutePath(), hackFilePath, getClassLoader())))); // 将新的dex插入到dexElements数组的前面
Object a2 = getPathList(pathClassLoader);
setField(a2, a2.getClass(), "dexElements", a); // 通过反射修改dexElements数组
pathClassLoader.loadClass("xyz.hanks.Hack");
} catch (Exception e) {
e.printStackTrace();
}
}
}
这样程序就正常运行起来了.
生成补丁包
现在BugClass
出现bug了. 修改一下, 然后将修改后的BugClass
导出jar包,然后通过dx
工具转换成dex,就叫做patch.dex 吧.然后放入到sdcard目录下.
现在可以加载补丁包了.
/**
* 加载
* Created by hanks on 16-1-3.
*/
public class FixApplication extends Application {
@Override public void onCreate() {
super.onCreate();
try {
PathClassLoader pathClassLoader = (PathClassLoader) getClassLoader();
String hackFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() +"/hack.dex"; // hack.dex的路径
Object a = combineArray(getDexElements(getPathList(pathClassLoader)), // 原有的 dex
getDexElements(getPathList(new DexClassLoader(hackFilePath, getDir("dex", 0).getAbsolutePath(), hackFilePath, getClassLoader())))); // 将新的dex插入到dexElements数组的前面
Object a2 = getPathList(pathClassLoader);
setField(a2, a2.getClass(), "dexElements", a); // 通过反射修改dexElements数组
pathClassLoader.loadClass("xyz.hanks.Hack");
// 加载补丁包
String patchFilePath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/patch.dex";
Object a3 = combineArray(getDexElements(getPathList(pathClassLoader)), // 原有的 dex
getDexElements(getPathList(new DexClassLoader(patchFilePath, getDir("dex", 0).getAbsolutePath(), patchFilePath, getClassLoader()))));
Object a4 = getPathList(pathClassLoader);
setField(a4, a4.getClass(), "dexElements", a3);
pathClassLoader.loadClass("xyz.hanks.fix.BugClass");
} catch (Exception e) {
e.printStackTrace();
}
}
}
每次修改BugClass类之后,生产补丁,放到sdcard,重启程序即可成效(不一定重启程序,目的是要在bugclass被第一次加载之前,加载补丁中的bugclass)