一文带你手动实现最简单的Android热修复,android面试试题

Toast.makeText(context, “结果” + a / b, Toast.LENGTH_SHORT).show();
}
}

然后我们在布局文件中添加二个按钮,一个按钮点击调用caculate方法,触发Bug,另一个按钮点击修复Bug,需要注意的是,千万不要忘记了权限的申请,因为整个过程涉及到文件的读取和写入,而6.0以上需要动态获取权限,所以要在清单文件中加入下列两行代码。

MainActivity代码如下

public class MainActivity extends AppCompatActivity {
private Button btn, btn_fix;
public static final int REQUEST_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.btn);
btn_fix = findViewById(R.id.btn_fix);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
TestCaculate testCaculate = new TestCaculate();
testCaculate.caculate(MainActivity.this);
}
});
btn_fix.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
fix();
}
});
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
}
private void fix() {
try {
String dexPath = Environment.getExternalStorageDirectory() + “/classes2.dex”;
HotFix.patch(this, dexPath, “com.aiiage.testhotfix.TestCaculate”);
Toast.makeText(this, “修复成功”, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(this, “修复失败” + e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE: {
if (grantResults.length > 0) {
// permission was granted
} else {
// permission denied
}
return;
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}

然后就是我们热修复的工具类,怎么使用,在MainActivity中已经有使用的代码了,工具类中用到了反射的知识,但是不是本文的重点,有需要的小伙伴自行学习相关知识,这个工具类中,最重要的两个类就是DexClassLoader和PathClassLoader,看名字就知道这是两个类加载器,用来加载类的,想知道具体实现的,下面就是源码

public final class HotFix {
/**

  • 修复指定的类
  • @param context 上下文对象
  • @param patchDexFile dex文件
  • @param patchClassName 被修复类名
    /
    public static void patch(Context context, String patchDexFile, String patchClassName) {
    if (patchDexFile != null && new File(patchDexFile).exists()) {
    try {
    if (hasLexClassLoader()) {
    injectInAliyunOs(context, patchDexFile, patchClassName);
    } else if (hasDexClassLoader()) {
    injectAboveEqualApiLevel14(context, patchDexFile, patchClassName);
    } else {
    injectBelowApiLevel14(context, patchDexFile, patchClassName);
    }
    } catch (Throwable th) {
    }
    }
    }
    private static boolean hasLexClassLoader() {
    try {
    Class.forName(“dalvik.system.LexClassLoader”);
    return true;
    } catch (ClassNotFoundException e) {
    return false;
    }
    }
    private static boolean hasDexClassLoader() {
    try {
    Class.forName(“dalvik.system.BaseDexClassLoader”);
    return true;
    } catch (ClassNotFoundException e) {
    return false;
    }
    }
    private static void injectInAliyunOs(Context context, String patchDexFile, String patchClassName)
    throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    InstantiationException, NoSuchFieldException {
    PathClassLoader obj = (PathClassLoader) context.getClassLoader();
    String replaceAll = new File(patchDexFile).getName().replaceAll(“\.[a-zA-Z0-9]+”, “.lex”);
    Class cls = Class.forName(“dalvik.system.LexClassLoader”);
    Object newInstance =
    cls.getConstructor(new Class[]{String.class, String.class, String.class, ClassLoader.class}).newInstance(
    new Object[]{context.getDir(“dex”, 0).getAbsolutePath() + File.separator + replaceAll,
    context.getDir(“dex”, 0).getAbsolutePath(), patchDexFile, obj});
    cls.getMethod(“loadClass”, new Class[]{String.class}).invoke(newInstance, new Object[]{patchClassName});
    setField(obj, PathClassLoader.class, “mPaths”,
    appendArray(getField(obj, PathClassLoader.class, “mPaths”), getField(newInstance, cls, “mRawDexPath”)));
    setField(obj, PathClassLoader.class, “mFiles”,
    combineArray(getField(obj, PathClassLoader.class, “mFiles”), getField(newInstance, cls, “mFiles”)));
    setField(obj, PathClassLoader.class, “mZips”,
    combineArray(getField(obj, PathClassLoader.class, “mZips”), getField(newInstance, cls, “mZips”)));
    setField(obj, PathClassLoader.class, “mLexs”,
    combineArray(getField(obj, PathClassLoader.class, “mLexs”), getField(newInstance, cls, “mDexs”)));
    }
    @TargetApi(14)
    private static void injectBelowApiLevel14(Context context, String str, String str2)
    throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
    PathClassLoader obj = (PathClassLoader) context.getClassLoader();
    DexClassLoader dexClassLoader =
    new DexClassLoader(str, context.getDir(“dex”, 0).getAbsolutePath(), str, context.getClassLoader());
    dexClassLoader.loadClass(str2);
    setField(obj, PathClassLoader.class, “mPaths”,
    appendArray(getField(obj, PathClassLoader.class, “mPaths”), getField(dexClassLoader, DexClassLoader.class,
    “mRawDexPath”)
    ));
    setField(obj, PathClassLoader.class, “mFiles”,
    combineArray(getField(obj, PathClassLoader.class, “mFiles”), getField(dexClassLoader, DexClassLoader.class,
    “mFiles”)
    ));
    setField(obj, PathClassLoader.class, “mZips”,
    combineArray(getField(obj, PathClassLoader.class, “mZips”), getField(dexClassLoader, DexClassLoader.class,
    “mZips”)));
    setField(obj, PathClassLoader.class, “mDexs”,
    combineArray(getField(obj, PathClassLoader.class, “mDexs”), getField(dexClassLoader, DexClassLoader.class,
    “mDexs”)));
    obj.loadClass(str2);
    }
    private static void injectAboveEqualApiLevel14(Context context, String str, String str2)
    throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
    PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
    Object a = combineArray(getDexElements(getPathList(pathClassLoader)),
    getDexElements(getPathList(
    new DexClassLoader(str, context.getDir(“dex”, 0).getAbsolutePath(), str, context.getClassLoader()))));
    Object a2 = getPathList(pathClassLoader);
    //新的dexElements对象重新设置回去
    setField(a2, a2.getClass(), “dexElements”, a);
    pathClassLoader.loadClass(str2);
    }
    /
    *
  • 通过反射先获取到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)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

img
img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
img

最后

都说三年是程序员的一个坎,能否晋升或者提高自己的核心竞争力,这几年就十分关键。

技术发展的这么快,从哪些方面开始学习,才能达到高级工程师水平,最后进阶到Android架构师/技术专家?我总结了这 5大块;

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

g-62vsYeFo-1711596719375)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值