自学内容网 自学内容网

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

  1. ListView有两级缓存,在ListView里面有一个内部类 RecycleBin,RecycleBin有两个对象Active View和Scrap View来管理缓存,Active View是第一级,Scrap View是第二级。缓存的对象是ItemView;一级缓存Active View是负责屏幕内的ItemView快速复用,而Scrap View是缓存屏幕外的数据,当该数据从屏幕外滑动到屏幕内的时候需要走一遍getView()方法。
  2. 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

  1. 如果有冲突,可以在合并的任意时刻使用git status命令来查看那些因包含合并冲突而处于未合并(unmerged)状态文件
  2. 所有合并中冲突而待解决的文件,都会以未合并状态标识出来,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. 修改完再提交

回滚:

  1. 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,网络监控的拦截器怎么实现
    1. 对于异步请求我们可以更改默认的加入正在执行队列的Call的条件判断, Dispatcher的setMaxRequestsPerHost()方法被调用 且 Dispatcher的setMaxRequestsPerHost()方法被调用
    2. 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

  1. SurfaceView与View的区别:
    1. View的绘图效率不高,主要是用于动画变化较少的程序,适用于主动更新
    2. SurfaceView绘图效率较高,用于界面更新频繁的程序,适用于被动刷新
    3. View是在主线程进行刷新,SurfaceView是在子线程进行刷新
    4. View没有使用双缓冲机制,SurfaceView在底层实现的机制中已经实现了双缓冲机制
  2. SurfaceView的双缓冲机制
    1. SurfaceView在更新视图时用到了两张 Canvas,一张 frontCanvas 和一张 backCanvas ,每次实际显示的是 frontCanvas ,backCanvas 存储的是上一次更改前的视图。当你在播放这一帧的时候,它已经提前帮你加载好后面一帧了,所以播放起视频很流畅。 当使用lockCanvas()获取画布时,得到的实际上是backCanvas 而不是正在显示的 frontCanvas ,之后你在获取到的 backCanvas 上绘制新视图,再 unlockCanvasAndPost(canvas)此视图,那么上传的这张 canvas 将替换原来的 frontCanvas 作为新的frontCanvas ,原来的 frontCanvas 将切换到后台作为 backCanvas
  3. SurfaceView,SurfaceHolder和Surface简单介绍
    1. 这三个是最典型的MVC模式,其中SurfaceView对应的就是View层,SurfaceHolder就是controler接口,而Surface就是对应的Model层,它里面持有Canvas,保存着绘制的数据。
    2. SurfaceHolder中的接口可以分为2类,一类是Callback接口,也就是我们上面模版代码中实现的3个接口方法,这类接口主要是用于监听SurfaceView的状态,以便我们进行相应的处理,比如创建绘制子线程,停止绘制等。另一类方法主要用于和Surface以及SurfaceView交互,比如lockCanvas方法和unlockCanvasAndPost方法用于获取Canvas以及提交绘制结果等。
    3. SurfaceView继承自View,拥有自己独立的绘图表面,我们调用SurfaceHolder的lockCanvas方法实际上调用的是Surface的lockCanvas方法,返回的是Surface中的Canvas。并且调用过程加了一个可重入锁mSurfaceLock。所以绘制过程中只能绘制完一帧内容并提交更改以后才会释放Canvas,也就是才能继续下一帧的绘制操作
    4. Surface实现了Parcelable接口,因为它需要在进程间以及本地方法间传输。Surface中创建了Canvas对象,用于执行具体的绘制操作
  4. SurfaceView的使用
    1. SurfaceView必须实现SurfaceHolder的Callback接口,主要是3个方法,分别是surfaceCreated、surfaceChanged、surfaceDestroyed。从名字就可以看出来这个是监听SurfaceView状态的,跟Activity的生命周期有点像。
    2. 在子线程中,我们通过SurfaceHolder的lockCanvas方法获取Canvas对象来进行具体的绘制操作,此时Canvas对象被当前线程锁定,绘制完成后通过SurfaceHolder的unlockCanvasAndPost方法提交绘制结果并释放Canvas对象。
    3. 用于控制子线程绘制的标记参数,如上面代码中的isDrawing变量,需要用volatile关键字修饰,以保证多线程安全。
  5. 推荐阅读:Android自定义View之双缓冲机制和SurfaceView

:::

快手二面

  1. okhttp怎么发起http和https请求

:::info

  1. 如果这个https站点,是经过了权威证书颁发机构CA的认证,就可以像访问普通的http那样来访问https
  2. 如果这个https站点,是没有经过CA认证,那么有两种方式来访问
    1. 一是让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,这样还不如不加密,不推荐使用。
    2. 下载该https站点的证书文件放置到工程内部,将文件的inputStream设置到okhttpClient的某个参数对象中,从而让okHttpClient对象 支持该https站点。
  3. 推荐阅读:OkHttp配置HTTPS访问+服务器部署 - 掘金
    Android Https相关完全解析 当OkHttp遇到Https_鸿洋_的博客-CSDN博客

:::

  1. 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. 第三次若匹配到,就使用已有连接,释放刚刚新建的连接;若未匹配到,则把新连接存入连接池并返回。
  1. okhttp中TLS握手代码是怎么实现的

:::info

  1. 创建 TLS 套接字
  2. 配置 Socket 的加密算法,TLS版本和扩展
  3. 强行进行一次 TLS 握手
  4. 建立 SSL 会话
  5. 校验证书
  6. 证书锁定校验
  7. 如果成功连接,保存握手和 ALPN 的协议

深入OKHttp之TLS - 腾讯云开发者社区-腾讯云

:::

  1. git fetch 与 git pull的区别

:::info

  1. git fetch 是从远程获取最新版本到本地分支,但是不会自动merge
  2. git pull = git fetch + git merge 会从远程获取最新版本到本地分支并进行合并到本地分支

:::

虎牙二面

  1. 一串数组里面有n个数据,里面有k个数据是乱序的,k<<n,选择合适的排序算法

插入排序

  1. 社区有各种年龄段的人,给各种年龄段的人进行排序,选择合适的排序算法。

快速排序

蚂蚁一面

  1. sqlite与mysql与room等关系?

:::info

  1. SQLite:
    1. SQLite功能简约,小型化,追求最大的磁盘效率,适用单机用户,数据量不是很大,需要方便移植或者需要频繁读写磁盘文件
    2. 一个自包含,基于文件的数据,整个数据库都包含在磁盘上的一个文件中,SQLite提供了出色的工具集,可以处理所有类型的数据,是一个“简化版”数据库
    3. 没有用户管理,SQLite产生的目的和本身性质没有多用户并发的高层设计,SQLite 就不支持使用各种技巧来进行额外的性能优化
    4. 所有需要迁移性,不需要扩展的应用,例如,单用户的本地应用,移动应用和游戏。代替磁盘访问:在很多情况下,需要频繁直接读/写磁盘文件的应用,都很适合转为使用 SQLite
  2. MySQL:
    1. MySQL功能全面,综合化,追求最大并发效率,适用满足多用户同时访问,或者是网站访问量比较大
    2. 容易使用,功能丰富,MySQL 支持大部分关系型数据库应该有的 SQL 功能,高安全性,MYSQL 有很多安全特性,其中有些相当高级。灵活而强大,快速,放弃支持某些标准,让 MySQL 效率更高并能使用捷径,因此带来速度的提升。
    3. 有一定的局限性,MySQL本身就没打算做到全知全能,因此有一些功能的局限性
    4. 适合分布式操作,把MySQL包括进你的部署栈,就像任何一个独立的数据库服务器,它会带来大量的操作自由性和一些先进的功能。
  3. Room
    1. Room是一个持久性数据库,Room持久性数据库提供了SQLite的抽象层,以便在充分利用SQLite的同时允许流畅的数据库访问。Room底层还是使用SQLite
    2. Database: 用这个组件创建一个数据库。注解定义了一系列entities,并且类中提供一系列Dao的抽象方法,也是下层主要连接的访问点。注解的类应该是一个继承 RoomDatabase 的抽象类。在运行时,你能通过调用Room.databaseBuilder()或者 Room.inMemoryDatabaseBuilder()获得一个实例
    3. Entity: 用这个组件创建表,Database类中的entities数组通过引用这些entity类创建数据库表。每个entity中的字段都会被持久化到数据库中,除非用@Ignore注解
    4. DAO: 这个组件代表了一个用来操作表增删改查的dao。Dao 是Room中的主要组件,负责定义访问数据库的方法。被注解@Database的类必须包含一个没有参数的且返回注解为@Dao的类的抽象方法。在编译时,Room创建一个这个类的实现。

:::

  1. https一定可以保证安全吗?数字证书解决什么问题?只有服务端数字证书可以保证安全吗?客户端的数字证书?数字证书的格式是什么?内容是什么?

:::info

  1. HTTPS协议本身到目前为止没有漏洞的,即使你成功进行中间人攻击,本质上是利用客户端的漏洞,(用户点击继续访问或者被恶意导入的根证书),并不是HTTPS不够安全。
  2. 数字证书的格式普遍采用的是X.509V3国际标准,一个标准的X.509数字证书包含以下一些内容:1、证书的版本信息;2、证书的序列号,每个证书都有一个唯一的证书序列号;3、证书所使用的签名算法;4、证书的发行机构名称,命名规则一般采用X.500格式;5、证书的有效期,通用的证书一般采用UTC时间格式;6、证书所有人的名称,命名规则一般采用X.500格式;7、证书所有人的公开密钥;8、证书发行者对证书的签名。
  3. 推荐阅读:数字证书、签名到底是什么?这篇文章讲得太好了_Wang_AI的博客-CSDN博客

:::

  1. retrofit与okhttp的关系?
    1. 两者之间的联系就是Retrofit是基于Okhttp的,它底层的所有请求默认走的都是Okhttp。它在Okhttp的基础上进一步封装,使用注解方式让我们使用简单方便且看代码一目了然。Retrofit是基于APP发起请求的封装,也就是面向的是应用层(比如响应数据的处理和错误处理等)。而Okhttp是对底层网络请求的封装与优化(socket优化,数据压缩,buffer缓存等)。
  2. onSaveInstanceState方法会在什么时候被执行
    onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据
    1. 当用户按下HOME键时。
      1. 这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则
    2. 长按HOME键,选择运行其他的程序时。
    3. 按下电源按键(关闭屏幕显示)时。
    4. 从activity A中启动一个新的activity时。
    5. 屏幕方向切换时,例如从竖屏切换到横屏时。
    6. onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行。



原文地址:https://blog.csdn.net/weixin_45882303/article/details/143443233

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!