Android 面试题汇总
Android 面试题汇总
文章目录
- Android 面试题汇总
- 快手一面
- 同程旅行一面
- 快手二面
- 虎牙二面
- 蚂蚁一面
很多八股文的差不多,这里只汇总一些我不会的知识点
快手一面
- tcp三次握手,最后一次失败,网络会怎么样?
- 如果第三次握手失败的时候,服务器会定时发送SYN+ACK,重传次数根据/proc/sys/net/ipv4/tcp_synack_retries来指定,默认是5次,如果重传次数到了之后。任然未收到ACK应答,那么一段时间后,server会自动关闭这个链接,但是client会认为已经建立了这个连接,如果client端向server写数据,server端将以RST包回应。进入CLOSED状态。这样做的目的是为了防止SYN洪泛攻击。
- **RST包:TCP协议中重置,复位链接的标志位,用来关闭异常链接
**发送RST包关闭链接时,不等缓冲区的包发送完成,丢弃缓冲区中的包,直接发送RST,同样接受端收到RST包后,也不必发送ACK确认包
同程旅行一面
- impelementation与api的区别
- 有工程A、B、C,让A依赖于B
- 若B implementation C A不能调用C的方法
- 若B api C A可以调用C的方法
- RecycleView的四级缓存结构
:::info
- ListView有两级缓存,在ListView里面有一个内部类 RecycleBin,RecycleBin有两个对象Active View和Scrap View来管理缓存,Active View是第一级,Scrap View是第二级。缓存的对象是ItemView;一级缓存Active View是负责屏幕内的ItemView快速复用,而Scrap View是缓存屏幕外的数据,当该数据从屏幕外滑动到屏幕内的时候需要走一遍getView()方法。
- RecyclerView有四级缓存,分别是Scrap、Cache、ViewCacheExtension和RecycledViewPool,缓存的对象是ViewHolder。Scrap对应ListView的Active View,就是屏幕内的缓存数据,就是相当于换了一个名字,可以直接拿来复用,Cache是刚刚移出屏幕的缓存数据,默认大小是2个,会根据先进的缓存数据移出并放到下一级缓存中然后把新 的数据添加进来,Cache里面数据是干净的,也就是携带, 原来的ViewHolder的所有数据信息,数据 信息可以直接拿来服用的,Scrap和Cache分别是通过position去找ViewHolder可以直接复用;ViewCacheExtension自定义缓存,目前来说应用场景比较少却需慎用;RecycledViewPool通过type来获取ViewHolder,获取的ViewHolder是个全新,需要重新绑定数据。这个时候ViewHolder还是空的话就会去就会create view了,创建一个新的ViewHolder
推荐阅读:让你彻底掌握RecyclerView的缓存机制
:::
- git冲突解决以及回滚
:::info
- 如果有冲突,可以在合并的任意时刻使用git status命令来查看那些因包含合并冲突而处于未合并(unmerged)状态文件
- 所有合并中冲突而待解决的文件,都会以未合并状态标识出来,Git会在有冲突的文件中加入标准的冲突解决标记,打开这些包含冲突的文件然后手动解决冲突。
:::
<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> dev/xx:index.html
:::info
3. 上述的冲突解决方案仅保留了其中一个分支的修改,并且<<<<<<< , ======= , 和 >>>>>>> 这些行需要被完全删除。
4. 修改完再提交
回滚:
- git reset
git reset命令是回滚某次提交,被回滚的提交将不会出现在提交记录中
:::
git reset --mixed/--hard/--soft c27894c06a2cc23e4097a93013cf640cc4fd527d
git reset –mixed HEAD~1
回退一个版本,且会将暂存区的内容和本地已提交的内容全部恢复到未暂存的状态,不影响原来本地文件(未提交的也 不受影响) ,也就是恢复到add之前 ,是reset的默认参数
git reset –soft HEAD~1
回退一个版本,不清空暂存区,将已提交的内容恢复到暂存区,不影响原来本地的文件(未提交的也不受影响),也就是恢复到commit之前
git reset –hard HEAD~1
回退一个版本,清空暂存区,将已提交的内容的版本恢复到本地,本地的文件也将被回退的版本替换,也就是恢复到没开发之前
:::info
2. git revert
git revert命令是创建一个新的提交来达到撤销的目的,被撤销的提交和撤销的提交都会出现在提交记录中。
3. 推荐阅读:
Git使用小技巧之回滚和撤销 - 掘金
Git提交规范流程和解决冲突实际使用 - 掘金
:::
- Okhttp的自定义DNS,网络监控的拦截器怎么实现
- 对于异步请求我们可以更改默认的加入正在执行队列的Call的条件判断, Dispatcher的setMaxRequestsPerHost()方法被调用 且 Dispatcher的setMaxRequestsPerHost()方法被调用
- OkHttp 其实本身已经暴露了一个 Dns 接口,默认的实现是使用系统的 InetAddress 类,发送 UDP 请求进行 DNS 解析。我们只需要实现 OkHttp 的 Dns 接口,在我们实现的 Dns 接口实现类中,解析 DNS 的方式,换成 自定义解析方式,将解析结果返回。在 OkHttp.build() 时,通过 dns() 方法配置。
class HttpDns : Dns {
override fun lookup(hostname: String): List<InetAddress> {
val ip = HttpDnsHelper.getIpByHost(hostname)
if (!TextUtils.isEmpty(ip)) {
//返回自己解析的地址列表
return InetAddress.getAllByName(ip).toList()
} else {
// 解析失败,使用系统解析
return Dns.SYSTEM.lookup(hostname)
}
}
}
使用
mOkHttpClient = httpBuilder
.dns(HttpDns())
.build();
3. okhttp要自定义网络拦截器的话,只需要继承Interceptor类,在里面做自己的操作,然后返回 chain.proceed(request); 添加拦截器:`builder.addNetworkInterceptor(<font style="color:#cc7832;">new </font>NetworkInterceptor())<font style="color:#cc7832;">;</font>`
4. 推荐阅读:
Android 网络优化,使用 HTTPDNS 优化 DNS,从原理到 OkHttp 集成 - 掘金
一文搞懂如何自定义 OkHttp 拦截器 - 掘金
OKHttp源码解析(一)–初阶 - 腾讯云开发者社区-腾讯云
OkHttp源码深度解析 - 掘金
- 动态代理实现原理,android的那些地方使用了动态代理
1.继承invocationhandler类
2.通过
Proxy.newProxyInstacnce(ClassLoader loader,Class<?> interfaces,InvocationHandler h);
反射生成代理对象
3.调用动态代理对象方法会回调
h.invoke(thisObject proxy,Method method,Object[] args);
4.最终通过调用
method.invoke(Object object,Object... args)
调用目标对象的方
:::info
动态代理在 Android 中的应用:
1. <font style="color:rgb(51, 51, 51);">Android跨进程通信中的使用了动态代理
比如Activity启动过程,其实就是隐藏了远程代理的使用
2. Retrofit中的create()方法通过动态代理获取接口对象
:::
- 为什么不用SurfaceView代替Activity进行子线程更新
:::info
- SurfaceView与View的区别:
- View的绘图效率不高,主要是用于动画变化较少的程序,适用于主动更新
- SurfaceView绘图效率较高,用于界面更新频繁的程序,适用于被动刷新
- View是在主线程进行刷新,SurfaceView是在子线程进行刷新
- View没有使用双缓冲机制,SurfaceView在底层实现的机制中已经实现了双缓冲机制
- SurfaceView的双缓冲机制
- SurfaceView在更新视图时用到了两张 Canvas,一张 frontCanvas 和一张 backCanvas ,每次实际显示的是 frontCanvas ,backCanvas 存储的是上一次更改前的视图。当你在播放这一帧的时候,它已经提前帮你加载好后面一帧了,所以播放起视频很流畅。 当使用lockCanvas()获取画布时,得到的实际上是backCanvas 而不是正在显示的 frontCanvas ,之后你在获取到的 backCanvas 上绘制新视图,再 unlockCanvasAndPost(canvas)此视图,那么上传的这张 canvas 将替换原来的 frontCanvas 作为新的frontCanvas ,原来的 frontCanvas 将切换到后台作为 backCanvas
- SurfaceView,SurfaceHolder和Surface简单介绍
- 这三个是最典型的MVC模式,其中SurfaceView对应的就是View层,SurfaceHolder就是controler接口,而Surface就是对应的Model层,它里面持有Canvas,保存着绘制的数据。
- SurfaceHolder中的接口可以分为2类,一类是Callback接口,也就是我们上面模版代码中实现的3个接口方法,这类接口主要是用于监听SurfaceView的状态,以便我们进行相应的处理,比如创建绘制子线程,停止绘制等。另一类方法主要用于和Surface以及SurfaceView交互,比如lockCanvas方法和unlockCanvasAndPost方法用于获取Canvas以及提交绘制结果等。
- SurfaceView继承自View,拥有自己独立的绘图表面,我们调用SurfaceHolder的lockCanvas方法实际上调用的是Surface的lockCanvas方法,返回的是Surface中的Canvas。并且调用过程加了一个可重入锁mSurfaceLock。所以绘制过程中只能绘制完一帧内容并提交更改以后才会释放Canvas,也就是才能继续下一帧的绘制操作
- Surface实现了Parcelable接口,因为它需要在进程间以及本地方法间传输。Surface中创建了Canvas对象,用于执行具体的绘制操作
- SurfaceView的使用
- SurfaceView必须实现SurfaceHolder的Callback接口,主要是3个方法,分别是surfaceCreated、surfaceChanged、surfaceDestroyed。从名字就可以看出来这个是监听SurfaceView状态的,跟Activity的生命周期有点像。
- 在子线程中,我们通过SurfaceHolder的lockCanvas方法获取Canvas对象来进行具体的绘制操作,此时Canvas对象被当前线程锁定,绘制完成后通过SurfaceHolder的unlockCanvasAndPost方法提交绘制结果并释放Canvas对象。
- 用于控制子线程绘制的标记参数,如上面代码中的isDrawing变量,需要用volatile关键字修饰,以保证多线程安全。
- 推荐阅读:Android自定义View之双缓冲机制和SurfaceView
:::
快手二面
- okhttp怎么发起http和https请求
:::info
- 如果这个https站点,是经过了权威证书颁发机构CA的认证,就可以像访问普通的http那样来访问https
- 如果这个https站点,是没有经过CA认证,那么有两种方式来访问
- 一是让okhttpClient信任所有的https,就是让
**<font style="color:rgb(153, 0, 0);">HostnameVerifier</font>**
的**<font style="color:rgb(153, 0, 0);">verify</font>**
方法,直接返回true,也就是信任所有的主机。**<font style="color:rgb(153, 0, 0);">X509TrustManager</font>**
类的**<font style="color:rgb(153, 0, 0);background-color:rgb(248, 248, 248);">checkServerTrusted 和 checkClientTrusted</font>**
默认返回true,表示接受任意的客户端与服务端的证书。这样写的话相当于是使用了一个没用的TrustManager,这样还不如不加密,不推荐使用。 - 下载该https站点的证书文件放置到工程内部,将文件的inputStream设置到okhttpClient的某个参数对象中,从而让okHttpClient对象 支持该https站点。
- 一是让okhttpClient信任所有的https,就是让
- 推荐阅读:OkHttp配置HTTPS访问+服务器部署 - 掘金
Android Https相关完全解析 当OkHttp遇到Https_鸿洋_的博客-CSDN博客
:::
- okhttp连接复用的规则
ConnectInterceptor
的主要工作就是负责建立TCP
连接,建立TCP
连接需要经历三次握手四次挥手等操作,如果每个HTTP
请求都要新建一个TCP
消耗资源比较多
而Http1.1
已经支持keep-alive
,即多个Http
请求复用一个TCP
连接,OKHttp
也做了相应的优化,下面我们来看下OKHttp
是怎么复用TCP
连接的
ConnectInterceptor
中查找连接的代码会最终会调用到ExchangeFinder.findConnection
方法,具体如下:
# ExchangeFinder
//为承载新的数据流 寻找 连接。寻找顺序是 已分配的连接、连接池、新建连接
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
synchronized (connectionPool) {
// 1.尝试使用 已给数据流分配的连接.(例如重定向请求时,可以复用上次请求的连接)
releasedConnection = transmitter.connection;
result = transmitter.connection;
if (result == null) {
// 2. 没有已分配的可用连接,就尝试从连接池获取。(连接池稍后详细讲解)
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {
result = transmitter.connection;
}
}
}
synchronized (connectionPool) {
if (newRouteSelection) {
//3. 现在有了IP地址,再次尝试从连接池获取。可能会因为连接合并而匹配。(这里传入了routes,上面的传的null)
routes = routeSelection.getAll();
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, false)) {
foundPooledConnection = true;
result = transmitter.connection;
}
}
// 4.第二次没成功,就把新建的连接,进行TCP + TLS 握手,与服务端建立连接. 是阻塞操作
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
synchronized (connectionPool) {
// 5. 最后一次尝试从连接池获取,注意最后一个参数为true,即要求 多路复用(http2.0)
//意思是,如果本次是http2.0,那么为了保证 多路复用性,(因为上面的握手操作不是线程安全)会再次确认连接池中此时是否已有同样连接
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, true)) {
// 如果获取到,就关闭我们创建里的连接,返回获取的连接
result = transmitter.connection;
} else {
//最后一次尝试也没有的话,就把刚刚新建的连接存入连接池
connectionPool.put(result);
}
}
return result;
}
上面精简了部分代码,可以看出,连接拦截器使用了5种方法查找连接
1. 首先会尝试使用 已给请求分配的连接。(已分配连接的情况例如重定向时的再次请求,说明上次已经有了连接)
2. 若没有 已分配的可用连接,就尝试从连接池中 匹配获取。因为此时没有路由信息,所以匹配条件:`address`一致——`host`、`port`、代理等一致,且匹配的连接可以接受新的请求。
3. 若从连接池没有获取到,则传入`routes`再次尝试获取,这主要是针对`Http2.0`的一个操作,`Http2.0`可以复用`square.com`与`square.ca`的连接
4. 若第二次也没有获取到,就创建`RealConnection`实例,进行`TCP + TLS`握手,与服务端建立连接。
5. 此时为了确保`Http2.0`连接的多路复用性,会第三次从连接池匹配。因为新建立的连接的握手过程是非线程安全的,所以此时可能连接池新存入了相同的连接。
6. 第三次若匹配到,就使用已有连接,释放刚刚新建的连接;若未匹配到,则把新连接存入连接池并返回。
- okhttp中TLS握手代码是怎么实现的
:::info
- 创建 TLS 套接字
- 配置 Socket 的加密算法,TLS版本和扩展
- 强行进行一次 TLS 握手
- 建立 SSL 会话
- 校验证书
- 证书锁定校验
- 如果成功连接,保存握手和 ALPN 的协议
深入OKHttp之TLS - 腾讯云开发者社区-腾讯云
:::
- git fetch 与 git pull的区别
:::info
- git fetch 是从远程获取最新版本到本地分支,但是不会自动merge
- git pull = git fetch + git merge 会从远程获取最新版本到本地分支并进行合并到本地分支
:::
虎牙二面
- 一串数组里面有n个数据,里面有k个数据是乱序的,k<<n,选择合适的排序算法
插入排序
- 社区有各种年龄段的人,给各种年龄段的人进行排序,选择合适的排序算法。
快速排序
蚂蚁一面
- sqlite与mysql与room等关系?
:::info
- SQLite:
- SQLite功能简约,小型化,追求最大的磁盘效率,适用单机用户,数据量不是很大,需要方便移植或者需要频繁读写磁盘文件
- 一个自包含,基于文件的数据,整个数据库都包含在磁盘上的一个文件中,SQLite提供了出色的工具集,可以处理所有类型的数据,是一个“简化版”数据库
- 没有用户管理,SQLite产生的目的和本身性质没有多用户并发的高层设计,SQLite 就不支持使用各种技巧来进行额外的性能优化
- 所有需要迁移性,不需要扩展的应用,例如,单用户的本地应用,移动应用和游戏。代替磁盘访问:在很多情况下,需要频繁直接读/写磁盘文件的应用,都很适合转为使用 SQLite
- MySQL:
- MySQL功能全面,综合化,追求最大并发效率,适用满足多用户同时访问,或者是网站访问量比较大
- 容易使用,功能丰富,MySQL 支持大部分关系型数据库应该有的 SQL 功能,高安全性,MYSQL 有很多安全特性,其中有些相当高级。灵活而强大,快速,放弃支持某些标准,让 MySQL 效率更高并能使用捷径,因此带来速度的提升。
- 有一定的局限性,MySQL本身就没打算做到全知全能,因此有一些功能的局限性
- 适合分布式操作,把MySQL包括进你的部署栈,就像任何一个独立的数据库服务器,它会带来大量的操作自由性和一些先进的功能。
- Room
- Room是一个持久性数据库,Room持久性数据库提供了SQLite的抽象层,以便在充分利用SQLite的同时允许流畅的数据库访问。Room底层还是使用SQLite
- Database: 用这个组件创建一个数据库。注解定义了一系列entities,并且类中提供一系列Dao的抽象方法,也是下层主要连接的访问点。注解的类应该是一个继承 RoomDatabase 的抽象类。在运行时,你能通过调用Room.databaseBuilder()或者 Room.inMemoryDatabaseBuilder()获得一个实例
- Entity: 用这个组件创建表,Database类中的entities数组通过引用这些entity类创建数据库表。每个entity中的字段都会被持久化到数据库中,除非用@Ignore注解
- DAO: 这个组件代表了一个用来操作表增删改查的dao。Dao 是Room中的主要组件,负责定义访问数据库的方法。被注解@Database的类必须包含一个没有参数的且返回注解为@Dao的类的抽象方法。在编译时,Room创建一个这个类的实现。
:::
- https一定可以保证安全吗?数字证书解决什么问题?只有服务端数字证书可以保证安全吗?客户端的数字证书?数字证书的格式是什么?内容是什么?
:::info
- HTTPS协议本身到目前为止没有漏洞的,即使你成功进行中间人攻击,本质上是利用客户端的漏洞,(用户点击继续访问或者被恶意导入的根证书),并不是HTTPS不够安全。
- 数字证书的格式普遍采用的是X.509V3国际标准,一个标准的X.509数字证书包含以下一些内容:1、证书的版本信息;2、证书的序列号,每个证书都有一个唯一的证书序列号;3、证书所使用的签名算法;4、证书的发行机构名称,命名规则一般采用X.500格式;5、证书的有效期,通用的证书一般采用UTC时间格式;6、证书所有人的名称,命名规则一般采用X.500格式;7、证书所有人的公开密钥;8、证书发行者对证书的签名。
- 推荐阅读:数字证书、签名到底是什么?这篇文章讲得太好了_Wang_AI的博客-CSDN博客
:::
- retrofit与okhttp的关系?
- 两者之间的联系就是Retrofit是基于Okhttp的,它底层的所有请求默认走的都是Okhttp。它在Okhttp的基础上进一步封装,使用注解方式让我们使用简单方便且看代码一目了然。Retrofit是基于APP发起请求的封装,也就是面向的是应用层(比如响应数据的处理和错误处理等)。而Okhttp是对底层网络请求的封装与优化(socket优化,数据压缩,buffer缓存等)。
- onSaveInstanceState方法会在什么时候被执行
onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据- 当用户按下HOME键时。
- 这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则
- 长按HOME键,选择运行其他的程序时。
- 按下电源按键(关闭屏幕显示)时。
- 从activity A中启动一个新的activity时。
- 屏幕方向切换时,例如从竖屏切换到横屏时。
- onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行。
- 当用户按下HOME键时。