1.MainActivity
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.xinrui.hotfix.utils.FixBug;
import com.xinrui.hotfix.utils.HotFix;
public class MainActivity extends Activity implements View.OnClickListener{
private TextView fix_txt,show_txt;
private FixBug mFixMe;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
public void init(){
fix_txt=(TextView)findViewById(R.id.fixtxt);
show_txt=(TextView)findViewById(R.id.show);
fix_txt.setOnClickListener(this);
show_txt.setOnClickListener(this);
mFixMe = new FixBug(MainActivity.this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.fixtxt:
checkFix();
break;
case R.id.show:
mFixMe.showWhat();
break;
}
}
private void checkFix(){
try {
String dexPath = Environment.getExternalStorageDirectory() + "/classes2.dex";
HotFix.fixDexFile(MainActivity.this, dexPath);
Toast.makeText(MainActivity.this, "修复成功", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(MainActivity.this, "修复失败" + e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
}
2.activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/fixtxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:text="修复"
android:layout_marginTop="100px"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"/>
<TextView
android:id="@+id/notgril"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Not Gril!"
android:layout_centerInParent="true"
android:visibility="gone"/>
<ImageView
android:id="@+id/gril"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"/>
<TextView
android:id="@+id/show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="显示"
android:textSize="25sp"
android:layout_marginBottom="100px"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
3.HotFix类
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import android.content.Context;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
/**
* 作者:created by yufenfen on 2019/3/21:12:13
* 邮箱: [email protected]
*/
public class HotFix {
/**
* 修复指定的类
*
* @param context 上下文对象
* @param fixDexFilePath 修复的dex文件路径
*/
public static void fixDexFile(Context context, String fixDexFilePath) {
if (fixDexFilePath != null && new File(fixDexFilePath).exists()) {
try {
injectDexToClassLoader(context, fixDexFilePath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* @param context
* @param fixDexFilePath 修复文件的路径
* @throws ClassNotFoundException
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private static void injectDexToClassLoader(Context context, String fixDexFilePath)
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
//读取 baseElements
PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
Object basePathList = getPathList(pathClassLoader);
Object baseElements = getDexElements(basePathList);
//读取 fixElements
String baseDexAbsolutePath = context.getDir("dex", 0).getAbsolutePath();
DexClassLoader fixDexClassLoader = new DexClassLoader(
fixDexFilePath, baseDexAbsolutePath, fixDexFilePath, context.getClassLoader());
Object fixPathList = getPathList(fixDexClassLoader);
Object fixElements = getDexElements(fixPathList);
//合并两份Elements
Object newElements = combineArray(baseElements, fixElements);
//一定要重新获取,不要用basePathList,会报错
Object basePathList2 = getPathList(pathClassLoader);
//新的dexElements对象重新设置回去
setField(basePathList2, basePathList2.getClass(), "dexElements", newElements);
}
/**
* 通过反射先获取到pathList对象
*
* @param obj
* @return
* @throws ClassNotFoundException
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private static Object getPathList(Object obj) throws ClassNotFoundException, NoSuchFieldException,
IllegalAccessException {
return getField(obj, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
}
/**
* 从上面获取到的PathList对象中,进一步反射获得dexElements对象
*
* @param obj
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private static Object getDexElements(Object obj) throws NoSuchFieldException, IllegalAccessException {
return getField(obj, obj.getClass(), "dexElements");
}
private static Object getField(Object obj, Class cls, String str)
throws NoSuchFieldException, IllegalAccessException {
Field declaredField = cls.getDeclaredField(str);
declaredField.setAccessible(true);//设置为可访问
return declaredField.get(obj);
}
private static void setField(Object obj, Class cls, String str, Object obj2)
throws NoSuchFieldException, IllegalAccessException {
Field declaredField = cls.getDeclaredField(str);
declaredField.setAccessible(true);//设置为可访问
declaredField.set(obj, obj2);
}
/**
* 合拼dexElements ,并确保 fixElements 在 baseElements 之前
*
* @param baseElements
* @param fixElements
* @return
*/
private static Object combineArray(Object baseElements, Object fixElements) {
Class componentType = fixElements.getClass().getComponentType();
int length = Array.getLength(fixElements);
int length2 = Array.getLength(baseElements) + length;
Object newInstance = Array.newInstance(componentType, length2);
for (int i = 0; i < length2; i++) {
if (i < length) {
Array.set(newInstance, i, Array.get(fixElements, i));
} else {
Array.set(newInstance, i, Array.get(baseElements, i - length));
}
}
return newInstance;
}
}
4.FixBug 类
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.xinrui.hotfix.R;
public class FixBug {
private final String TAG = "FixBug";
private ImageView mBelle;
private TextView mNotShow;
private Context mContext;
//false: bug, true: fix
private boolean fix = false;
public FixBug(Activity context) {
mContext = context;
mBelle = (ImageView) context.findViewById(R.id.gril);
mNotShow = (TextView) context.findViewById(R.id.notgril);
}
public void showWhat() {
if (fix) {
fixBug();
Log.d(TAG, "fix bug!");
} else {
mBelle.setVisibility(View.GONE);
mNotShow.setVisibility(View.VISIBLE);
Log.d(TAG, "this is a bug!");
}
}
private void fixBug() {
try {
mBelle.setVisibility(View.VISIBLE);
mNotShow.setVisibility(View.GONE);
mBelle.setBackgroundResource(R.drawable.timg);
} catch (Exception e) {
e.printStackTrace();
}
}
}