总结
开发是面向对象。我们找工作应该更多是面向面试。哪怕进大厂真的只是去宁螺丝,但你要进去得先学会面试的时候造飞机不是么?
作者13年java转Android开发,在小厂待过,也去过华为,OPPO等,去年四月份进了阿里一直到现在。等大厂待过也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
作者:贾里
什么是热修复
定义:在我们应用上线后出现bug需要及时修复时,不用再发新的安装包,只需要发布补丁包,在客户无感知下修复掉bug。
怎么进行热修复
服务端:补丁包管理
用户端:执行热修复
开发端:生成补丁包
热修复需要解决的问题
开发端
-
补丁包是什么?
-
如何生成补丁包?
-
开启混淆后呢?
-
对比改动自动生成补丁包(gradle)?
用户端
-
什么时候执行热修复?
-
怎么执行热修复(使用补丁包)?
-
Android版本兼容问题?
热修复解决方案
热补丁方案有很多,其中比较出名的有腾讯Tinker、阿里的AndFix、美团的Robust以及QZone的超级补丁方案。
AndFix
在native动态替换java层的方法,通过native层hook java层的代码。
Robust
对每个函数都在编译打包阶段自动的插入了一段代码。类似于代理,将方法执行的代码重定向到其他方法中。
Tinker
Tinker通过计算对比指定的Base Apk中的dex与修改后的Apk中的dex的区别,补丁包中的内容即为两者差分的描述。
运行时将Base Apk中的dex与补丁包进行合成,重启后加载全新的合成后的dex文件。
Qzone
双亲委托机制
某个类加载器在加载类时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务或者没有父类加载器时,才自己去加载。
1、避免重复加载,当父加载器已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
2、安全性考虑,防止核心API库被随意篡改。
在线源码阅读:www.androidos.net.cn/或http://and…
类查找流程
类加载实现热修复
类是怎么被加载的? 怎么使用补丁包中的类? 已经被加载过的类还能够替换修复吗? 怎样保证补丁包中正确class 的dex先加载?
加载修复后的类
EnjoyFix.installPatch(this, new File(“/sdcard/patch.jar”));
EnjoyFix.class
public class EnjoyFix {
private static final String TAG = “EnjoyFix”;
private static File initHack(Context context) {
File hackFile = new File(context.getExternalFilesDir(“”), “hack.dex”);
FileOutputStream fos = null;
InputStream is = null;
try {
fos = new FileOutputStream(hackFile);
is = context.getAssets().open(“hack.dex”);
int len;
byte[] buffer = new byte[2048];
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return hackFile;
}
/**
-
1、获取程序的PathClassLoader对象
-
2、反射获得PathClassLoader父类BaseDexClassLoader的pathList对象
-
3、反射获取pathList的dexElements对象 (oldElement)
-
4、把补丁包变成Element数组:patchElement(反射执行makePathElements)
-
5、合并patchElement+oldElement = newElement (Array.newInstance)
-
6、反射把oldElement赋值成newElement
-
@param application
-
@param patch
*/
public static void installPatch(Application application, File patch) {
File hackDex = initHack(application);
List patchs = new ArrayList<>();
patchs.add(hackDex);
if (patch.exists()) {
patchs.add(patch);
}
//1、获取程序的PathClassLoader对象
ClassLoader classLoader = application.getClassLoader();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
try {
ClassLoaderInjector.inject(application, classLoader, patchs);
} catch (Throwable throwable) {
}
return;
}
//2、反射获得PathClassLoader父类BaseDexClassLoader的pathList对象
try {
Field pathListField = ShareReflectUtil.findField(classLoader, “pathList”);
Object pathList = pathListField.get(classLoader);
//3、反射获取pathList的dexElements对象 (oldElement)
Field dexElementsField = ShareReflectUtil.findField(pathList, “dexElements”);
Object[] oldElements = (Object[]) dexElementsField.get(pathList);
//4、把补丁包变成Element数组:patchElement(反射执行makePathElements)
Object[] patchElements = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Method makePathElements = ShareReflectUtil.findMethod(pathList, “makePathElements”,
List.class, File.class,
List.class);
ArrayList ioExceptions = new ArrayList<>();
patchElements = (Object[])
makePathElements.invoke(pathList, patchs, application.getCacheDir(), ioExceptions);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Method makePathElements = ShareReflectUtil.findMethod(pathList, “makeDexElements”,
ArrayList.class, File.class, ArrayList.class);
ArrayList ioExceptions = new ArrayList<>();
patchElements = (Object[])
makePathElements.invoke(pathList, patchs, application.getCacheDir(), ioExceptions);
}
//5、合并patchElement+oldElement = newElement (Array.newInstance)
//创建一个新数组,大小 oldElements+patchElements
// int[].class.getComponentType() ==int.class
Object[] newElements = (Object[]) Array.newInstance(oldElements.getClass().getComponentType(),
oldElements.length + patchElements.length);
System.arraycopy(patchElements, 0, newElements, 0, patchElements.length);
System.arraycopy(oldElements, 0, newElements, patchElements.length, oldElements.length);
//6、反射把oldElement赋值成newElement
dexElementsField.set(pathList, newElements);
} catch (Exception e) {
e.printStackTrace();
}
}
学习分享
①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
学习分享
①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包
[外链图片转存中…(img-fNZMl9fk-1715707120093)]
[外链图片转存中…(img-8cjiVKxL-1715707120094)]
[外链图片转存中…(img-UY4IcLDx-1715707120094)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!