需要先通过插件的 ClassLoader 加载该类再通过反 射调用其方法。插件化框架一般会通过统一的入口
去管理对各个插件中类的访问,并且做一定的限制。
-
若使用单 ClassLoader 机制,主工程则可以直接通过类名去访问插件中的类。该方式有个弊端,若两个不同的插件工程引用了一个库的不同版本,则程序可能会出错。
-
资源加载
-
原理在于通过反射将插件 apk 的路径加入AssetManager 中并创建 Resource 对象加载资源,有两种处理方式:
-
合并式:addAssetPath 时加入所有插件和主工程的路径;由于 AssetManager 中加入了所
有插件和主工程的路径,因此生成的Resource 可以同时访问插件和主工程的资
源。但是由于主工程和各个插件都是独立编译的,生成的资源 id 会存在相同的情况,在
访问时会产生资源冲突。
- 独立式:各个插件只添加自己 apk 路径,各个插件的资源是互相隔离的,不过如果想要实现资源的共享,必须拿到对应的 Resource对象。
6、组件化原理
-
参考回答:
-
引入组件化的原因:项目随着需求的增加规模变得越来越大,规模的增大导致了各种业务错中复杂的交织在一起,每个业务模块之间,代码没有约束,带来了代码边界的模糊,代码冲突时有发生, 更改一个小问题可能引起一些新的问题, 牵一发而动全身,增加一个新需求,需要熟悉相关的代码逻辑,增加开发时间
-
避免重复造轮子,可以节省开发和维护的成本。
-
可以通过组件和模块为业务基准合理地安排人力,提高开发效率。
-
不同的项目可以共用一个组件或模块,确保整体技术方案的统一性。
-
为未来插件化共用同一套底层模型做准备。
-
组件化开发流程就是把一个功能完整的 App 或模块拆分成多个子模块(Module),每个子模块可以独立编译运行,也可以任意组合成另一个新的 App 或模块,每个模块即不相互依赖但又可以相互交互,但是最终发布的时候是将这些组件合并统一成一个 apk,遇到某些特殊情况甚至可以升级或者降级
-
举个简单的模型例子
App 是主 application,ModuleA 和 ModuleB 是两个业务模块(相对独立,互不影响),Library 是基础模块,包含所有模块需要的依赖库,以及一些工具类:如网络访问、时间工具等
- 注意:提供给各业务模块的基础组件,需要根据具体情况拆分成 aar 或者 library,像登录,基础网络层这样较为稳定的组件,一般直接打包成 aar,减少编译耗时。而像自定义 View 组件,由于随着版本迭代会有较多变化,就直接以源码形式抽离成 Library
7、跨组件通信
-
参考回答:
-
跨组件通信场景:
-
第一种是组件之间的页面跳转 (Activity 到 Activity, Fragment 到 Fragment, Activity 到 Fragment, Fragment 到 Activity) 以及跳转时的数据传递 (基础数据类型和可序列化的自定义类类
型)。
-
第二种是组件之间的自定义类和自定义方法的调用(组件向外提供服务)。
-
跨组件通信方案分析:
-
第一种组件之间的页面跳转实现简单,跳转时想传递不同类型的数据提供有相应的 API 即可。
-
第二种组件之间的自定义类和自定义方法的调用要稍微复杂点,需要 ARouter 配合架构中的 公共服务(CommonService) 实现:
-
提供服务的业务模块:
-
在公共服务(CommonService) 中声明 Service接口 (含有需要被调用的自定义方法), 然后在自己的模块中实现这个 Service 接口, 再通过 ARouter API 暴露实现类。
-
使用服务的业务模块:
-
通过 ARouter 的 API 拿到这个Service 接口(多态持有, 实际持有实现类), 即可调用 Service 接口中声明的自定义方法, 这样就可以达到模块之间的交互。
-
此外,可以使用 AndroidEventBus 其独有的Tag, 可以在开发时更容易定位发送事件和接受事件的代码, 如果以组件名来作为 Tag 的前缀进行分组, 也可以更好的统一管理和查看每个组件的事件, 当然也不建议大家过多使用 EventBus。
-
如何管理过多的路由表?
-
RouterHub 存在于基础库, 可以被看作是所有组件都需要遵守的通讯协议, 里面不仅可以放路由地址常量, 还可以放跨组件传递数据时命名的各种 Key 值,再配以适当注释, 任何组件开发人员不需要事先沟通只要依赖了这个协议, 就知道了各自该怎样协同工作, 既提高了效率又降低了出错风险, 约定的东西自然要比口头上说强。
-
Tips: 如果您觉得把每个路由地址都写在基础库的RouterHub 中, 太麻烦了, 也可以在每个组件内部建立一个私有 RouterHub, 将不需要跨组件的路由地址放入私有 RouterHub 中管理, 只将需要跨组件的路由地址放入基础库的公有 RouterHub 中管理, 如果您不需要集中管理所有路由地址的话, 这也是比较推荐的一种方式。
-
ARouter 路由原理:
-
ARouter 维护了一个路由表 Warehouse,其中保存着全部的模块跳转关系,ARouter 路由跳转实际上还是调用了 startActivity 的跳转,使用了原生的Framework 机制,只是通过 apt 注解的形式制造出跳转规则,并人为地拦截跳转和设置跳转条件。
-
常见的组件化方案如下
8、组件化中路由、埋点的实现
-
参考回答:
-
因为在组件化中,各个业务模块之间是各自独立的, 并不会存在相互依赖的关系, 所以一个业务模块是访问不了其他业务模块的代码的, 如果想从 A 业务模块的 A 页面跳转到 B 业务模块的 B 页面, 光靠模块自身是不能实现的,这就需要一种跨组件通信方案—— 路由(Router)
-
路由主要有以下两种场景:
-
第一种是组件之间的页面跳转 (Activity 到 Activity, Fragment 到 Fragment, Activity 到 Fragment, Fragment 到 Activity) 以及跳转时的数据传递 (基础数据类型和可序列化的自定义类类型)
-
第二种是组件之间的自定义类和自定义方法的调用(组件向外提供服务)
-
其原理在于将分布在不同组件 module 中的某些类按照一定规则生成映射表(数据结构通常是 Map,Key 为一个字符串,Value 为类或对象),然后在需要用到的时候从映射表中根据字符串从映射表中取出类或对象,本质上是类的查找
-
埋点则是在应用中特定的流程收集一些信息,用来跟踪应
用使用的状况
-
代码埋点:在某个事件发生时调用 SDK 里面相应的接口发送埋点数据,百度统计、友盟、TalkingData、Sensors Analytics 等第三方数据统计服务商大都采用这种方案
-
全埋点:全埋点指的是将 Web 页面/App 内产生的所有的、满足某个条件的行为,全部上报到后台服务器
-
可视化埋点:通过可视化工具(例如 Mixpanel)配置采集节点,在 Android 端自动解析配置并上报埋点数据,从而实现所谓的自动埋点
-
无埋点:它并不是真正的不需要埋点,而是Android 端自动采集全部事件并上报埋点数据,在后端数据计算时过滤出有用数据
9、Hook 以及插桩技术
-
参考回答:
-
Hook 是一种用于改变 API 执行结果的技术,能够将系统的API 函数执行重定向(应用的触发事件和后台逻辑处理是根据事件流程一步步地向下执行。而 Hook 的意思,就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件,例如逆向破解 App)
-
Android 中的 Hook 机制,大致有两个方式:
-
要 root 权限,直接 Hook 系统,可以干掉所有的App。
-
无 root 权限,但是只能 Hook 自身 app,对系统其它 App 无能为力。
-
插桩是以静态的方式修改第三方的代码,也就是从编译阶段,对源代码(中间代码)进行编译,而后重新打包,是静态的篡改; 而 Hook 则不需要再编译阶段修改第三方的源码或中间代码,是在运行时通过反射的方式修改调用,是一种动态的篡改
10、Android 的签名机制?
-
参考回答:
-
Android 的签名机制包含有消息摘要、数字签名和数字证书
-
消息摘要:在消息数据上,执行一个单向的 Hash 函数,生成一个固定长度的 Hash 值
-
数字签名:一种以电子形式存储消息签名的方法,一个完整的数字签名方案应该由两部分组成:签名算法和验证算法
-
数字证书:一个经证书授权(Certificate Authentication)中心数字签名的包含公钥拥有者信息以及公钥的文件
11、v3 签名 key 和 v2 还有 v1 有什么区别
-
参考回答:
-
在 v1 版本的签名中,签名以文件的形式存在于 apk 包中,这个版本的 apk 包就是一个标准的 zip 包,V2 和 V1 的差别是 V2 是对整个 zip 包进行签名,而且在 zip 包中增加了一个 apk signature block,里面保存签名信息。
-
v2 版本签名块(APK Signing Block)本身又主要分成三部分:
-
SignerData(签名者数据):主要包括签名者的证书,整个 APK 完整性校验 hash,以及一些必要信息
-
Signature(签名):开发者对 SignerData 部分数据的签名数据
-
PublicKey(公钥):用于验签的公钥数据
-
v3 版本签名块也分成同样的三部分,与 v2 不同的是在SignerData 部分,v3 新增了 attr 块,其中是由更小的level 块组成。每个 level 块中可以存储一个证书信息。前一个 level 块证书验证下一个 level 证书,以此类推。最后一个 level 块的证书,要符合 SignerData 中本身的证书,即用来签名整个 APK 的公钥所属于的证书
12、Android5.0~10.0 之间大的变化
-
参考回答:
-
Android5.0 新特性
-
MaterialDesign 设计风格
-
支持 64 位 ART 虚拟机(5.0 推出的 ART 虚拟机,在5.0 之前都是 Dalvik。他们的区别是:Dalvik,每次运行,字节码都需要通过即时编译器转换成机器码(JIT)。 ART,第一次安装应用的时候,字节码就会预先编译成机器码(AOT)) 通知详情可以用户自己设计
-
Android6.0 新特性
-
动态权限管理
-
支持快速充电的切换
-
支持文件夹拖拽应用
-
相机新增专业模式
-
Android7.0 新特性
-
多窗口支持
-
V2 签名
-
增强的 Java8 语言模式
-
夜间模式
-
Android8.0(O)新特性
-
优化通知:通知渠道 (Notification Channel) 通知标志 休眠 通知超时 通知设置 通知清除
-
画中画模式:清单中 Activity 设置android:supportsPictureInPicture
-
后台限制
-
自动填充框架
-
系统优化
-
等等优化很多
-
Android9.0(P)新特性
-
室内 WIFI 定位
-
“刘海”屏幕支持
-
安全增强
-
等等优化很多
-
Android10.0(Q)目前曝光的新特性
-
夜间模式:包括手机上的所有应用都可以为其设置暗黑模式。
-
*桌面模式:提供类似于 PC 的体验,但是远远不能代替 PC。
-
屏幕录制:通过长按“电源”菜单中的"屏幕快照"来开启。
13、说下 Measurepec 这个类
-
参考回答:
-
作用:通过宽测量值 widthMeasureSpec 和高测量值heightMeasureSpec 决定 View 的大小
-
组成:一个 32 位 int 值,高 2 位代表 SpecMode(测量模式),低 30 位代表 SpecSize( 某种测量模式下的规格大小)。
-
三种模式:
-
UNSPECIFIED:父容器不对 View 有任何限制,要多大有多大。常用于系统内部。
-
EXACTLY(精确模式):父视图为子视图指定一个确切的尺寸 SpecSize。对应 LyaoutParams 中的match_parent 或具体数值。
-
AT_MOST(最大模式):父容器为子视图指定一个最大尺寸 SpecSize,View 的大小不能大于这个值。对应LayoutParams 中的 wrap_content。
-
决定因素:值由子 View 的布局参数 LayoutParams 和父容器的 MeasureSpec 值共同决定。具体规则见下图:
14、请例举 Android 中常用布局类型,并简述其用法以及排版效率
-
参考回答:
-
Android 中常用布局分为传统布局和新型布局
-
传统布局(编写 XML 代码、代码生成):
-
框架布局(FrameLayout);
-
线性布局(LinearLayout);
-
绝对布局(AbsoluteLayout);
-
相对布局(RelativeLayout);
-
表格布局(TableLayout);
-
新型布局(可视化拖拽控件、编写 XML 代码、代码生成):
-
约束布局(ConstrainLayout):
-
对于嵌套多层 View 而言,其排版效率:LinearLayout = FrameLayout >> RelativeLayout
15、区别 Animation 和 Animator 的用法,概述其原理
-
参考回答:
-
动画的种类:前者只有透明度,旋转,平移,伸缩 4 种属性,而对于后者,只要是该控件的属性,且有 setter 该属性的方法就都可以对该属性执行一种动态变化的效果。
-
可操作的对象:前者只能对 UI 组件执行动画,但属性动画几乎可以对任何对象执行动画(不管它是否显示在屏幕上)。
-
动画播放顺序:在 Animator 中,AnimatorSet 正是通过playTogether()、playSequentially()、animSet.play().with()、before()、after()这些方法来控制多个动画协同工作,从而做到对动画播放顺序的精确控制
16、使用过什么图片加载库?Glide 的源码设计哪里很微妙?
-
参考回答:
-
图片加载库:Fresco、Glide、Picasso 等 o Glide 的设计微妙在于:
-
Glide 的生命周期绑定:可以控制图片的加载状态与当前页面的生命周期同步,使整个加载过程随着页面的状态而启动/恢复,停止,销毁
-
Glide 的缓存设计:通过(三级缓存,Lru 算法,Bitmap 复用)对 Resource 进行缓存设计
-
Glide 的完整加载过程:采用 Engine 引擎类暴露了一系列方法供 Request 操作
17、如何绕过 9.0 限制?
- 参考回答:
18、用过哪些网络加载库?OkHttp、Retrofit 实现原理?
-
参考回答:
-
网络加载库:OkHttp、Retrofit、xUtils、Volley 等
19、对于应用更新这块是如何做的? (灰度,强制更新、分区域更新)
-
参考回答:
-
内部更新:
-
通过接口获取线上版本号,versionCode
-
比较线上的 versionCode 和本地的 versionCode,弹出更新窗口
-
下载 APK 文件(文件下载)
-
安装 APK
-
灰度更新:
-
找单一渠道投放特别版本。
-
做升级平台的改造,允许针对部分用户推送升级通知甚至版本强制升级。
-
开放单独的下载入口。
-
是两个版本的代码都打到 app 包里,然后在 app 端植入测试框架,用来控制显示哪个版本。测试框架负责与服务器端 api 通信,由服务器端控制 app 上A/B 版本的分布,可以实现指定的一组用户看到 A版本,其它用户看到 B 版本。服务端会有相应的报表来显示 A/B 版本的数量和效果对比。最后可以由服务端的后台来控制,全部用户在线切换到 A 或者B 版本~
-
无论哪种方法都需要做好版本管理工作,分配特别的版本号以示区别。 当然,既然是做灰度,数据监控(常规数据、新特性数据、主要业务数据)还是要做到位,该打的数据桩要打。 还有,灰度版最好有收回的能力,一般就是强制升级下一个正式版。
-
强制更新:
-
一般的处理就是进入应用就弹窗通知用户有版本更新,弹窗可以没有取消按钮并不能取消。这样用户就只能选择更新或者关闭应用了,当然也可以添加取消按钮,但是如果用户选择取消则直接退出应用。
-
**增量更新: **
-
二进制差分工具 bsdiff 是相应的补丁合成工具,根据两个不同版本的二进制文件,生成补丁文件.patch 文件。通过 bspatch 使旧的 apk 文件与不定文件合成新的 apk。 注意通过 apk 文件的 md5 值进行区分版本。
20、会用 Kotlin、Fultter 吗? 谈谈你的理解
最后
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
上面分享的百度、腾讯、网易、字节跳动、阿里等公司2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
【Android思维脑图(技能树)】
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
【Android高级架构视频学习资源】
**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
【Android思维脑图(技能树)】
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
[外链图片转存中…(img-OWmBHC6D-1723781332009)]
【Android高级架构视频学习资源】
**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!