Android初级开发工程师面试常见八股文汇总(更新中)

4,134 阅读23分钟

计网知识:

OSI七层模型: 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层

数据单元:

物理层:bit

数据链路层:帧 frame

网络层:数据包 packet

传输层:数据包,TCP 叫 数据段 segment,UDP 叫 数据报 datagram

  1. HTTP协议和HTTPS

参考这个链接

http:超文本传输协议

特点

无连接:每次发送报文都要建立一次连接。

无状态:每一次请求都不会保存之前的状态。

灵活:通过content-type 标记,可以传输任意类型的数据(文本、图片、视频)

简单快速:http协议简单,对服务器的消耗较小。

缺点:

无状态: 意味着不会记录任何连接信息,意味着无法记录客户端的信息,后续处理前面的信息需要重传,导致每次连接传输的信息量变大。

不安全:

明文传输,header使用的是明文,暴露信息。如wifi陷阱

队头阻塞:

Http报文分为 请求报文和响应报文

HTTP请求报文的一般格式

img

image.png

HTTP请求方法:

一般有九种:

HTTP1.0: GET、POST、HEAD

HTTP1.1:PUT、PATCH、DELETE、

GET和POST的区别:

HTTP协议 GET请求和POST请求都是基于TCP/IP 协议实现的。使用任意一个都可以实现客户端和服务器的双向交互。

  1. 本质上的区别是 “约定和规范”

GET请求是用来获取服务器资源的,也就是进行查询操作,

POST请求是用来传输实体对象的,也可以用来添加、修改、删除。

按照约定,GET请求是将参数拼加到URL进行参数传递,POST是将参数写入请求正文中传递。

  1. GET方法是请求一般会被缓存。GET方法的参数是放在URL里传递的,URL是有长度限制的,
  2. 回退的刷新不一样,GET方法不会重新提交,POST会把数据再次提交。
  3. GET请求的参数会保存在历史记录里,POST请求的参数不会保留到历史记录里。

总结:GET和POST是HTTP请求中最常用的两种方法,都是基于TCP/IP 协议

常见的HTTP状态码

1xx : 指示信息

2xx:成功

3xx:重定向(重定向是指完成过程需要更多操作)

4xx:客户端错误

5xx:服务端错误

重定向:

重定向就是网络请求的方法转到其他位置(如网页重定向或者域名重定向),重定向分为301永久重定向,和302临时重定向。

HTTP的长连接和短连接

http1.0采用的是 请求-应答 模式,普通模式下每次发送一个信息都要TCP三握手,发送完就要TCP四次挥手,长连接在请求头加上一个keep - alive , 建立一次TCP连接,客户端和服务器的连接持续有效。

1.png

长连接的优点是:

  • 减少服务器内存消耗,因为不需要经常建立和关闭连接
  • 支持管道化
  • 减少网路堵塞
  • 减少了后续请求的响应时间,因为不需要频繁的建立连接

缺点:

  • 长时间保持连接会很浪费服务器资

可以通信完就关闭,或者设置超时时间,

管道化:

HTTP1.1在使用长连接时,可以这样

管道化管理消息是这样,客户端可以不等待回答就继续发送请求,服务端会按照请求顺序响应。

请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3

正向代理和反向代理

正向代理:客户端配置需要使用的代理服务器,

多个客户端使用一个代理服务器访问服务端;

用于屏蔽网站,对用户访问进行授权。

1626238781500.jpg

反向代理:

一个客户端对代理服务器,对应多个服务器。

用于服务器的负载均衡、缓存。

1626239283393.jpg

HTTPS

HTTPS是HTTP+SSL/ TLS

  • HTTP是明文传输,不安全的,HTTPS是加密传输,安全的多
  • HTTP标准端口是80,HTTPS标准端口是443
  • HTTP不用认证证书免费,HTTPS需要认证证书要钱
  • 连接方式不同,HTTP三次握手,HTTPS中TLS1.2版本7次,TLS1.3版本6次
  • HTTP在OSI网络模型中是在应用层,而HTTPS的TLS是在传输层
  • HTTP是无状态的,HTTPS是有状态

总结 HTTP和HTTPS的区别;

HTTP协议是无连接的、无状态的、相对灵活,效率较高、明文传输。

HTTPS协议是有状态的,

  • 加密:HTTPS使用SSL/TLS 加密数据

  • 连接方式:HTTP(三次握手),HTTPS(三次握手+数字证书)

  • 端口:HTTP的端口是80,HTTPS的端口是443

    HTTPS加密流程,

    1、客户端请求 HTTPS 请求并连接到服务器的 443 端口,此过程和请求 HTTP 请求一样,进行三次握手;

    2、服务端向客户端发送数字证书,其中包含公钥、证书颁发者、到期日期

    现比较流行的加解密码对,即公钥和私钥。公钥用于加密,私钥用于解密。所以服务端会保留私钥,然后发送公钥给客户端。

    3、客户端收到证书,会验证证书的有效性。验证通过后会生成一个随机的 pre-master key。再将密钥通过接收到的公钥加密然后发送给服务端

    4、服务端接收后使用私钥进行解密得到 pre-master key

    5、获得 pre-master key 后,服务器和客户端可以使用主密钥进行通信。

TCP三次握手和四次挥手

  • 三次握手:

客户端发送SYN包到服务器,等待服务器确认。

服务器收到包,发送一个ACK包,自己也发一个SYN包,表示收到了信息,我这边正常,可以连接。

客户端收到服务器的SYN+ACK包,向服务器发送ACK确认包,表示我们可以通信了。

  • 四次挥手:

客户端返送一个FIN,ACK报文,表示我想断开连接

服务端收到FIN报文,发送一个ACK给客户端确认收到,表示我收到了断开申请,请等待,

服务端发送一个FIN报文,关闭和客户端的数据传送,表示我可以断开连接了。

客户端收到后,发送ACK报文确认断开,表示断开吧。

  • 为什么三次握手,四次挥手

ACK是用来应答的,SYN是用来同步的。

当服务端收到客户端的ACK报文断开连接时,服务端发送一个ACK告诉客户端我知道了,你没有信息发送了。但其实socket还保持连接。服务端还可以发送给客户端数据。

当服务端也发送了FIN报文,表示服务端也没有数据要发送了,

因为服务端还有数据需要处理和发送,等服务端不再发送数据,再断开连接。

举个例子:A 和 B 打电话,通话即将结束后,A 说 “我没啥要说的了”,B 回答 “我知道了”,于是 A 向 B 的连接释放了。但是 B 可能还会有要说的话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,于是 B 向 A 的连接释放了,这样整个通话就结束了。

TCP和UDP

TCP 传输层控制协议

UDP 用户数据报协议

都是传输层的功能。

TCP

面向连接,可靠,用于文件传输、发送接收邮件、远程登录

有拥塞控制,流量控制

UDP

无连接,不可靠,但高效,用及时通信,如语音、视频、直播等。

  1. HashMap原理(红黑树)
  2. Recyclerview的优化原理。

2023-02-20

一共两轮面试

  1. 快速排序,堆排序
  2. 安卓和ios退出桌面,活动周期变化
  3. for 循环,remove a[ i ] ,问怎么变化
  4. 大量需要及时加载数据怎么优化 tableview
  5. 碎片

十大经典排序算法

  1. 冒泡排序

冒泡排序每次比较相邻两个元素大小,把大的放到右边,每一轮排序后,最后位置的元素是最大的。

时间复杂度:O(n ^ 2)

空间复杂度:O(1)

  1. 选择排序

把数组分成两端,每次把未排序的数组里找最大的元素,放到已经排序好的数组的最前端。

时间复杂度:O(n ^ 2)

空间复杂度:O(1)

  1. 插入排序

插入排序把数组看成有序序列和无序序列,从头到尾扫描无序序列,每次把选择的元素扫描一遍插入有序序列,最终整个序列是有序序列。

时间复杂度:最好情况 O(n) O(n ^ 2)

空间复杂度:O(1)

  1. 希尔排序
  2. 归并排序

基于分治法,自底向上的迭代,想象如何归并两个有序数组,给两个数组维两个游标,比较两个游标所比的元素,小的填入新数组,因为两个数组都是有序的。

时间复杂度:稳定 O( nlogn)

空间复杂度:O(n)

  1. 快速排序

分治想法,每次选择一个基准,把比基准小的元素放到基准左边,把比基准大的放到右边。递归的快速排序左右两个数组。

首先,找一个基准(数组头尾或者中间),设置头指针 i = 1,尾指针 j = n -1 ,让尾指针从后往前找到第一个比基准小的然后和基准交换位置,再让头指针从前往后扫描,找到第一个比基准大的位置,然后交换位置,一直交换到尾指针在头指针前面,说明此时基准左边的都比他小,右边都比他大,此时递归左右两边的数组。

快速排序是冒泡排序的基础上改进的,冒泡是相邻的交换,快速是跳着交换。

时间复杂度:O( nlogn),恶化 O( n ^ 2)

空间复杂度:O( log n )

  1. 堆排序

基于堆,堆是一种完全二叉树,大顶堆就是所有节点的值大于等于子节点。

将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

时间复杂度:O( nlogn),

空间复杂度:O( 1 )

操作系统

进程和线程的区别

进程是系统进行资源分配的最小单位,线程是cpu调度的最小单位。

引入线程是为了增加并发度

线程切换消耗比进程少

线程也有用户级线程和内核级线程。

进程相关

  • PCB 是进程控制块,是进程存在的唯一标志。
  • 一般把进程控制用的叫做原语,原语在执行过程中不可被中断
  • 进程的通信方式有:共享存储、消息传递、管道通信

线程相关

  • 线程有用户级线程和内核级线程,内核级线程必须在核心态,内核级线程是处理机分配的单位。

内存管理

  • 地址重定位:逻辑地址到物理地址的转换。

最佳置换算法、先进先出、LRU最近最久未使用、时钟法

Android相关

APP启动流程

Recyclerview优化机制

  1. viewHolder实现View复用

viewHolder将视图的创建和数据分离分开,避免频繁的创建视图;

当用户滑动列表的时候,离开屏幕的视图不会立刻被销毁,会放进一个缓存池里,当有新的item进入屏幕的时候,Recyclerview会尝试从缓存池里寻找与目标类型相同的视图,找到了直接绑定数据,避免创建新的视图,

  1. viewType实现视图类型复用

列表中的Item可能有不同的布局,为了实现不同类型视图的复用,Recyclerview有一个viewType机制,为不同视图类型分配不同的viewType,然后在 onCreateViewHolderonBindViewHolder 方法中根据 ViewType 创建和绑定不同类型的视图。这样,RecyclerView 可以为每种类型的视图分别进行复用。

  1. 局部刷新机制

Recyclerview支持局部刷新,即只刷新列表的某一项的部分内容,不是整个列表,这样可以避免不必要的视图重绘,提高性能。

我们可以在调用 notifyItemChanged 时传入一个 Payload 对象,然后在 onBindViewHolder 方法中根据 Payload 更新部分内容。

  1. 列表动画

RecyclerView 使用 ItemAnimator 来实现列表动画。ItemAnimator 可以对添加、删除、移动等操作进行动画处理,提升用户体验。

Fragment之间通信

  1. 通过Activity进行通信

通过 Activity 进行通信:使用 getActivity() 方法获取当前 Fragment 所在的 Activity 对象,然后通过该对象的方法进行通信。

  1. 使用接口进行通信

定义一个接口,Fragment 实现该接口并将自身作为参数传递给其他 Fragment 或 Activity,其他 Fragment 或 Activity 调用该接口中的方法时将数据传递给当前 Fragment。

  1. 广播进行通信

使用 Android 广播机制,发送广播时指定特定的 action,接收广播的 Fragment 或 Activity 实现相应的 BroadcastReceiver 并在 onReceive() 方法中处理广播。

  1. 通过jetpack的viewModel

通过 ViewModel 进行通信:使用 Android Jetpack 中的 ViewModel 组件,将数据存储在 ViewModel 中,多个 Fragment 或 Activity 可以通过获取 ViewModel 实例共享数据。

事件分发机制

事件分发机制,说明它是 Android 应用程序中的重要机制之一,用于处理用户输入事件,如触摸、键盘、轨迹球等。

实际上是将点击事件传递到某个具体View并处理的整个过程。

分发的时候由由外到内传递给View,响应的时候是由事件源以此向外传递。

复杂性实际上在于每层事件是否继续传递、以及事件的具体消费。

方法

dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()

流程

  • 事件分发的起点是 Activity,Activity 会将事件传递给根布局的 ViewGroup。
  • ViewGroup 会遍历其子 View,按照顺序将事件传递给每个子 View。
  • 在传递事件的过程中,每个 View 都会依次收到三类事件:按下事件、移动事件、抬起事件。
  • 对于每类事件,View 可以根据需要进行消费(处理)或者传递给下一个 View 处理。
  • 如果所有的 View 都没有消费事件,则事件会回传给父 ViewGroup 和 Activity 处理。

被分发的对象:用户因为触摸屏幕而产生的事件,按下、滑动、抬起与取消,事件被封装成MotionEvent对象

MVC和MVVM

MVC和MVVM都是常见的软件开发架构模式,它们都旨在将应用程序的不同部分相互分离,以提高代码的可读性、可维护性和可扩展性。但是它们之间存在以下几个区别:

  1. 设计思想:MVC是一种基于分层的设计思想,它将应用程序分为模型(Model)、视图(View)和控制器(Controller)三层,每层都有不同的职责。而MVVM则是一种数据绑定的设计思想,它将应用程序分为模型(Model)、视图模型(ViewModel)和视图(View)三层,通过数据绑定机制使得视图模型与视图之间的交互更加简单和方便。
  2. 数据绑定:MVVM中的视图模型与视图之间使用数据绑定机制来实现数据的同步更新,而MVC中的视图和控制器之间则通常使用手动更新的方式来实现数据的同步更新。
  3. 可测试性:MVVM中的视图模型与视图之间使用数据绑定机制来实现数据的同步更新,因此视图模型可以独立于视图进行测试。而MVC中的控制器和视图之间的交互通常是紧密耦合的,难以单独进行测试。
  4. 可复用性:MVVM中的视图模型可以在不同的视图中复用,因为它们不依赖于具体的视图实现。而MVC中的控制器则通常是与视图紧密耦合的,难以在不同的视图中复用。

总的来说,MVC和MVVM都是有效的软件开发架构模式,适用于不同的应用场景。MVC适用于需要分离业务逻辑和界面层的应用,而MVVM则适用于需要简化视图与模型之间的交互的应用。在实际开发中,开发人员需要根据具体情况选择合适的架构模式,以提高代码的可读性、可维护性和可扩展性。

自定义View

首先需要继承View、ViewGroup或者View的子类如Button之类的;

然后重写onMeasure方法,用于测量View的大小。在自定义View时,我们需要重写这个方法来指定View的大小。

重写onLayout ( )方法,计算当前View以及子View的位置。

重写onDraw方法,视图的绘制工作。Android的坐标系里,向右是X轴,向下是Y轴。这个和HTML5 Canvas差不多的。可以声明一个paint,用canvas绘制。

IPC跨进程通信

在Android中,由于应用程序是以进程的方式运行的,因此不同应用程序之间的通信就需要使用IPC机制。

  1. Intent,Intent可以通过设置action、category、data等属性来实现不同应用程序之间的数据传递和通信。例如,一个应用程序可以通过Intent启动另一个应用程序,并将一些数据传递给它。

  2. 文件共享

  3. 内容提供器。

  4. Binder是Android中的一种高效的IPC机制。Binder采用了进程间共享内存的方式进行通信,可以实现高效的数据传递和远程调用。

    在Android开发中,选择合适的IPC机制是非常重要的。例如,如果只需要进行简单的数据传递,可以选择Intent或文件共享;如果需要进行复杂的数据共享和远程调用,可以选择ContentProvider或Binder等机制。同时,需要注意IPC机制的安全性和性能,避免出现数据泄露、死锁等问题。

Android多线程

在 Android 开发中,多线程是一个重要的概念。由于 Android 系统不允许在主线程(UI线程)上执行耗时操作,因此开发者需要使用多线程技术来实现并发处理,避免应用程序出现卡顿、无响应等问题。以下是 Android 多线程的详细解释:

  1. AsyncTask

AsyncTask 是 Android 提供的一个轻量级的异步类,适用于简单的异步任务。它提供了四个核心方法,方便开发者进行操作:

  • onPreExecute(): 在后台任务开始之前执行,通常用于初始化 UI 界面。
  • doInBackground(Params... params): 在后台线程上执行耗时操作,返回结果给 onPostExecute()
  • onProgressUpdate(Progress... values): 在后台任务执行过程中,更新 UI 界面。
  • onPostExecute(Result result): 后台任务执行完毕后,将结果返回给主线程,用于更新 UI 界面。
  1. Handler 和 Thread

通过创建新的 Thread 和使用 Handler 与主线程进行通信,可以实现多线程。

  1. ThreadPoolExecutor

ThreadPoolExecutor 是 Java 提供的一个线程池,可以用于执行多个并发任务。

以上是 Android 多线程的几种实现方式,根据不同的需求选择合适的方法。需要注意的是,多线程编程涉及到线程安全、同步、死锁等问题,在实际开发中应谨慎使用。

JNI相关

JNI是Java语言和Native语言C、C++的桥梁,

三方库

okHttp

RxJava,异步响应

Glide 图片库

Retrofit 集成OkHttp

jetpack,本身是个工具集,包含liveData,

eventBus:EventBus是一种开源的、轻量级的Java事件发布/订阅框架,可以用于不同组件之间的通信,包括Activity、Fragment、Service等。它的主要思想是将事件的发布者和订阅者解耦,从而实现松散耦合的组件通信。

EventBus的基本原理是通过“事件”的方式进行通信。事件是一个普通Java类,用于封装一些数据或信息。发布者通过EventBus向订阅者发送事件,订阅者通过注册对应的事件来接收并处理事件。EventBus会根据事件类型自动将事件分发给对应的订阅者,订阅者可以在接收到事件后执行相应的操作。

Binder机制

Java相关

设计模式

工厂模式

工厂模式是一种创建型设计模式,它提供了一种创建对象的方法,而不必将对象的创建逻辑暴露给客户端。工厂模式将对象的实例化过程封装在一个工厂类中,客户端只需要通过工厂类来获取所需要的对象,而不需要知道对象的创建细节。

工厂模式通过将对象的创建过程封装在工厂类中,可以提高代码的可维护性和可扩展性,因为客户端代码只依赖于工厂类,而不依赖于具体的对象实现类。如果需要添加新的对象实现类,只需要修改工厂类,而不需要修改客户端代码。

单例模式

单例模式是为了确保类只有一个实例。单例模式经常被用于管理共享资源或状态,例如数据库连接、线程池等。特点是:

  1. 单例类只能有一个实例,并提供全局访问点。这个实例通常是通过一个静态方法或属性来获取的。
  2. 构造函数私有:为了确保只有一个实例,单例模式通常将构造函数设为私有的,防止外部代码创建实例。
  3. 单例模式提供一个全局访问点,使得其他代码可以访问这个唯一的实例。

有饿汉模式和懒汉模式来实现单例模式。

饿汉式单例模式是在类加载时就创建了一个实例,因此线程安全,但是可能会浪费一些内存。懒汉式单例模式是在第一次使用时才创建实例,因此节省了内存空间,但是需要考虑线程安全问题。

Java里linkedList和arraylist的区别

HashMap

  1. 数据结构:HashMap是一种基于哈希表实现的Map,它通过将键映射到值的方式来存储和访问数据。具体来说,HashMap内部使用了一个数组来存储数据,并且每个数组元素都是一个链表的头结点。当有新的元素插入时,HashMap会根据元素的哈希码计算出对应的数组下标,并将元素插入到对应链表的末尾。
  2. 哈希冲突:由于哈希表的容量是有限的,所以不同的元素可能会映射到同一个数组下标上,这种情况称为哈希冲突。当发生哈希冲突时,HashMap会将新元素插入到链表的末尾,因此链表的长度可能会很长,导致HashMap的性能下降。
  3. 扩容机制:为了避免哈希冲突带来的性能问题,HashMap会在链表长度达到一定阈值时,将链表转化为红黑树。同时,当HashMap中元素数量超过容量的75%时,HashMap会进行扩容,将容量扩大一倍,并将原有元素重新分布到新的数组中。
  4. 线程安全:HashMap是非线程安全的,因此在多线程环境下使用HashMap需要进行同步处理。可以使用ConcurrentHashMap来替代HashMap,在保证线程安全的同时,也具有较高的并发性能。
  5. 性能分析:HashMap的性能主要取决于哈希函数的质量、负载因子的大小和数组容量的大小。在实际使用中,需要根据具体情况来调整负载因子和数组容量的大小,以达到最佳的性能表现。

堆和栈

  1. 存储位置

    • :栈是一种自动分配内存的区域,通常位于程序的内存空间中。函数调用时,局部变量和函数调用信息会存储在栈中。
    • :堆是一种手动分配内存的区域,位于程序的动态内存空间中。程序员可以通过mallocnew等操作手动分配内存。
  2. 内存管理

    • :栈的内存管理由编译器自动完成,无需程序员手动管理。当函数调用结束时,栈内存会自动释放。
    • :堆的内存管理需要程序员手动操作。在需要内存时,需要手动分配;在不再需要内存时,需要手动释放。
  3. 生命周期

    • :栈中的数据具有局部作用域,函数调用结束后,栈中的数据会被销毁。
    • :堆中的数据在手动释放之前一直存在,因此其生命周期可以跨越多个函数调用。但如果在不再需要时未能及时释放,可能会导致内存泄漏。
  4. 访问速度

    • :由于栈的内存管理方式是先进后出(LIFO),管理方式简单且高效,因此访问速度相对较快。
    • :堆的内存管理相对复杂,内存分配和释放需要经过寻址、分配等操作,因此访问速度相对较慢。
  5. 空间大小

    • :栈的空间相对较小,受操作系统限制。如果栈空间不足,可能会导致栈溢出(Stack overflow)。
    • :堆的空间相对较大,通常仅受计算机内存大小的限制。但过多的堆内存分配可能会导致内存不足(Out of memory)。
  6. 适用场景

    • :适用于生命周期短、空间需求小、访问速度要求高的场景,如局部变量、函数调用信息等。
    • :适用于生命周期长、空间需求大、可手动管理内存的场景,如动态数据结构(链表、树等)。