总结
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
作者:小马快跑
我们知道在使用ARouter时,需要在build.config里配置:
annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'
并且知道annotationProcessor用来声明注解解析器,arouter-compiler用来解析ARouter中的各个注解并自动生成class类,那么我们就来看一下到底生成了哪些类:
其生成的5个索引文件有4大类(Group类有2个文件,按Group区分开了),他们都实现了ARouter中的接口:
至于他们都代表什么,我们后面一一分析。
ARouter初始化
在自定义Application中进行初始化:
// 尽可能早,推荐在Application中初始化
ARouter.init(Application.this);
点进去看一下:
//ARouter.java
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
哦,ARouter又调用了_ARouter.init(application)去初始化,再点进去:
//_ARouter.java
private volatile static ThreadPoolExecutor executor = DefaultPoolExecutor.getInstance();
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
哦,_ARouter
在init
初始化方法里除了初始化一些变量和一个handler
,又调用了LogisticsCenter.init(mContext, executor)
, 其中executor
是一个线程池, 继续跟到LogisticsCenter
里去:
/**
* LogisticsCenter contains all of the map.
* 1\. Creates instance when it is first used.
* 2\. Handler Multi-Module relationship map(*)
* 3\. Complex logic to solve duplicate group definition
*/
//LogisticsCenter.java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
Set<String> routerMap;
//1、遍历“com.alibaba.android.arouter.routes”路径下的类并把其加入到set中
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// These class was generated by arouter-compiler.
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
// Save new version name when router map update finishes.
PackageUtils.updateVersion(context);
} else {
logger.info(TAG, "Load router map from cache.");
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
//2、遍历set,将root、group、provider分类并填充到Warehouse路由表中
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
}
LogisticsCenter.init
方法比较长,上面只保留了核心代码,ARouter
优先使用arouter-auto-register
插件去解析并填充Warehouse
路由表,忽略这种方式。我们来看上面这种加载方式,PackageUtils.isNewVersion(context)
中判断SharedPreferences(后面简称sp)里面是否有存储versionName
及versionCode
,如果没有或者他们有更新的时候,需要重新加载一次com.alibaba.android.arouter.routes
这个路径下的类名并填充到Set中,否则直接从sp中取数据并赋值到Set中去。接着就开始遍历这个Set,并通过Class.forName(className)
这种反射方式去实例化类并调用类中的loadInto
方法将注解对应的索引信息添加到Warehouse
路由表中。画个图来总结一下:
ARouter跳转
ARouter跳转时,直接使用ARouter.getInstance().build("xxx/xxx").navigation()
即可完成跳转,那我们就来看一下源码,看看里面都做了什么,首先是build
方法:
/**
* Build postcard by path and default group
*/
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path));
}
}
/**
* Build postcard by uri
*/
protected Postcard build(Uri uri) {
if (null == uri || TextUtils.isEmpty(uri.toString())) {
throw new HandlerException(Consts.TAG + "Parameter invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
uri = pService.forUri(uri);
}
return new Postcard(uri.getPath(), extractGroup(uri.getPath()), uri, null);
}
}
/**
* Build postcard by path and group
*/
protected Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return new Postcard(path, group);
}
}
三个方法中都有,PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
那么这个PathReplaceService是干啥的,点进去看看:
/**
* Preprocess your path
*/
public interface PathReplaceService extends IProvider {
/**
* For normal path.
*
* @param path raw path
*/
String forString(String path);
/**
* For uri type.
*
* @param uri raw uri
*/
Uri forUri(Uri uri);
}
看它的介绍就知道了,原来这个类是用来预处理path和uri的,调用方需要实现PathReplaceService就可以做预处理,如果不实现,默认pService==null,那么直接走下面的去初始化Postcard实体类。
接着来看navigation
方法,因为build
方法返回的是PostCard
类,所以调用的是PostCard
类的navigation
方法,经过一系列跳转,最终来到_ARouter.getInstance().navigation(mContext, postcard, requestCode, callback)
:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
if (!postcard.isGreenChannel()) {
// It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
} else {
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
去除了部分无关代码,只保留了核心代码,首先调用了LogisticsCenter.completion
方法,我们追进去看看:
//LogisticsCenter.java
/**
* Completion the postcard by route metas
*
* @param postcard Incomplete postcard, should complete by this method.
## 结尾
好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。
> 这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套**进阶学习的视频及面试专题资料包**,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~
![](https://img-blog.csdnimg.cn/img_convert/33a604a5be327b9bfebf2bbc0d1f283d.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/672c402ec73ddf06a57074dc36e80096.webp?x-oss-process=image/format,png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
可以帮助到大家~
[外链图片转存中...(img-Cm5WqzhQ-1715586845785)]
[外链图片转存中...(img-fN2oxF2S-1715586845785)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**