绿鹅等大厂2020Android面试经验(持续整理更新中)

Posted Winston Wood

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了绿鹅等大厂2020Android面试经验(持续整理更新中)相关的知识,希望对你有一定的参考价值。

1、android 四大组件

Activity,Service,Content provider,broadcast receiver。 详情

2. Activity的4种启动方式

standard, singleTop, singleTask, singleInstance。详情

3. Binder机制

Binder是Android系统上实现的一种IPC(Inter-process communication,进程间通信)机制。详情

4. HIDL

HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定收集到接口和软件包的类型和方法调用。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。HIDL 旨在用于进程间通信 (IPC)。详情

5. Android的消息机制(Looper,Handler,Messagequeue)

Android系统本质上来说就是一个消息驱动的系统。Android使用消息机制实现线程间的通信,线程通过Looper建立自己的消息循环,MessageQueue是FIFO的消息队列,Looper负责从MessageQueue中取出消息,并且分发到消息指定目标Handler对象。Handler对象绑定到线程的局部变量Looper,封装了发送消息和处理消息的接口。详情

6. final,finally,finalize的区别

final为java修饰符,它可以用来定义变量,方法或者类,表示不可修改;
finally用在try/catch语句块中,表示这段语句始终会被执行到;
finalize为Object 类中定义的方法,在垃圾收集器删除对象之前调用。

7. Android的进程优先级等级

分为5个等级:Foreground,Visible,Service,Background,Empty。详情

8. ArrayList的实现

  • ArrayList是List接口的可变长数组实现,核心为
    transient Object[] elementData;
  • ArrayList是一个泛型容器,新建ArrayList需要实例化泛型参数
  • ArrayList是线程不安全的。Vector是线程安全的;
  • 了解ArrayList的扩容机制。详情
9. 单例模式的实现方法
  • 懒汉模式(线程不安全)
  • 线程安全的懒汉模式(线程安全)
  • 饿汉模式(线程安全)
  • 静态类内部加载(线程安全)
  • 枚举方法(线程安全)
  • 双重校验锁法(通常线程安全,低概率不安全)
  • 使用volatile关键字
  • 使用ThreadLocal关键字
  • 使用CAS锁实现(线程安全)详情
10. 原子操作的实现原理
  • 总线锁
    当一个处理器在总线上输出LOCK#信号时,其他处理器的请求将被阻塞住,那么该
    处理器可以独占共享内存(总线锁定把CPU和内存之间的通信锁住了,这使得锁定期间,其他处理器不能操作其他内存地址的数据,所以总线锁定的开销比较大);
  • 缓存锁
    频繁使用的内存会缓存在处理器的L1、L2和L3高速缓存里,那么原子操作就可以直接在处理器内部缓存中进行。所谓“缓存锁定”是指内存区域如果被缓存在处理器的缓存行中,并且在Lock操作期间被锁定,那么当它执行锁操作回写到内存时,处理器不在总线上输出LOCK#信号,而是修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性。详情
11. Java如何实现原子操作
  • 循环CAS
    JVM中的CAS操作利用了处理器提供的CMPXCHG指令来实现。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止。CAS虽然很高效地解决了原子操作,但是CAS仍然存在三大问题:ABA问题;循环时间长开销大;只能保证一个共享变量的原子操作。
  • 锁机制
    锁机制保证了只有获得锁的线程才能够操作锁定的内存区域。JVM内部实现了很多种锁机制,有偏向锁、轻量级锁和互斥锁。除了偏向锁,JVM实现锁的方式都用了循环CAS,即当一个线程想进入同步块的时候使用循环CAS的方式来获取锁,当它退出同步块的时候使用循环CAS释放锁。详情
12. 谈一谈Android中的序列化

Android 提供了两种方式来完成序列化与反序列化过程: Serializable Parcelable 。二者的 区别

13. 尾递归

传统递归有两个缺陷:效率低,占内存;如果递归链过长,可能会statck overflow。
尾递归是一种特殊的递归,递归形式的调用都出现在函数的末尾,特点是在回归过程中不用做任何操作,大多数现代的编译器会利用这种特点自动生成优化的代码。在尾递归中,首先执行计算,然后执行递归调用,将当前步骤的结果传递给下一个递归步骤。这导致最后一个语句采用的形式(return (recursive-function params))。基本上,任何给定递归步骤的返回值与下一个递归调用的返回值相同详情

14. IO的NIO的区别 详情
IONIO
面向字节流面向缓冲区
阻塞基于Selector的非阻塞
15. volitile关键字
  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的;
  • 禁止进行指令重排序。详情
16. String

使用字符串常量池,每当我们使用字面量(String s=”1”;)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就将此字符串对象的地址赋值给引用s(引用s在Java栈中)。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,并将此字符串对象的地址赋值给引用s(引用s在Java栈中)。
使用字符串常量池,每当我们使用关键字new(String s=new String(”1”);)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么不再在字符串常量池创建该字符串对象,而直接堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s,如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,然后在堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s。详情

17. SurfaceFlinger

SurfaceFlinger是Android系统中的一个系统服务,这个系统服务主要实现了Surface的建立、控制、管理等功能,提供系统范围内的surface composer功能,它能够将各种应用程序的2D、3D surface进行组合。详情

18. invalidate和postInvalidate

在主线程更新UI可以调用invalidate,子线程调用postInvalidate来更新UI。详情

19. SurfaceView和TextureView
  • SurfaceView有自己的Surface,在WMS中有对应的WindowState,在SurfaceFlinger中有Layer。虽然在App端它仍在View hierachy中,但在Server端(WMS和SurfaceFlinger)中,它与宿主窗口是分离的。这样的好处是对这个Surface的渲染可以放到单独线程去做,渲染时可以有自己的GL context。缺点是Surface不在View hierachy中,显示不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中。SurfaceView 不能嵌套使用。
  • TextureView不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化。TextureView必须在硬件加速的窗口中。它显示的内容流数据可以来自App进程或是远端进程。TextureView重载了draw()方法,其中主要SurfaceTexture中收到的图像数据作为纹理更新到对应的HardwareLayer中。详情
20. 为何主线程Looper一直处于死循环但却不卡死

主线程的MessageQueue没有消息时,便阻塞在Looper的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。详情

21. init进程
start_kernel init_post run_init_process

init进程是Android系统中用户空间的第一个进程,相关源码位于system/core/init。它被赋予了很多极其重要的工作职责详情

  1. 创建文件系统目录并挂载相关的文件系统
  2. 屏蔽标准的输入输出
  3. 初始化内核log系统
  4. 调用property_init初始化属性相关的资源
  5. 完成SELinux相关工作
  6. 重新设置属性
  7. 创建epoll句柄
  8. 装载子进程信号处理器
  9. 通过property_start_service启动属性服务
  10. 通过parser.ParseConfig(“/init.rc”)来解析init.rc
22. Zygote线程

在Android系统中,JavaVM(Java虚拟机)、应用程序进程以及运行系统的关键服务的SystemServer进程都是由Zygote进程来创建的,我们也将它称为孵化器。它通过fock(复制进程)的形式来创建应用程序进程和SystemServer进程,由于Zygote进程在启动时会创建JavaVM,因此通过fock而创建的应用程序进程和SystemServer进程可以在内部获取一个JavaVM的实例拷贝。详情
ZygoteInit类的四大功能:

  • 绑定socket(registerZygoteSocket),以便接收新的Android应用的运行请求;
  • 预先加载应用程序框架中的类(preloadClasses,preloadResources)、平台资源;
  • 启动运行SystemServer(startSystemServer);
  • 监视UDS,处理新的应用处理请求。
23. findViewById

请参考详情

24. View.post
  • View.post(Runnable)内部两种判断,如果当前View没有绘制完成,通过类HandlerActionQueue内部将Runnabel缓存下来,否则就直接通过 mAttachInfo.mHandler 将这些 Runnable 操作 post 到主线程的 MessageQueue 中等待执行;
  • mAttachInfo.mHandler是ViewRootImpl中的成员变量,绑定的是主线程的Looper,所以View.post的操作会转到主线程之中,自然可以作为更新UI的根据了
  • Handler消息机制是不断的从队列中获取Message对象,所以 View.post(Runnable) 中的 Runnable 操作肯定会在 performMeaure() 之后才执行,所以此时可以获取到 View 的宽高。 详情
25. OpenGL基本原理

OpenGL是将用数学语言和色彩等信息描述的三维空间物体通过计算转换成二维图像并显示出来的程序库。三维空间中的对象被描述成一系列的顶点(几何对象)或像素(图像)。OpenGL对数据进行几个步骤的处理将其转换成像素,这些数据存放在帧缓存区中形成最终需要显示的图形。

26. EGL作用

EGL 是渲染 API(如 OpenGL ES)和原生窗口系统之间的接口。通常来说,OpenGL 是一个操作 GPU 的 API,它通过驱动向 GPU 发送相关指令,控制图形渲染管线状态机的运行状态,但是当涉及到与本地窗口系统进行交互时,就需要这么一个中间层,且它最好是与平台无关的。 详情

EGL API 是独立于 OpenGL ES的独立的一套 API,其主要作用是为 OpenGL指令创建 Context 、绘制目标 Surface 、配置 FrameBuffer 属性、Swap 提交绘制结果等。EGL 提供如下机制:

  • 与设备原生窗口通信
  • 查询绘制 surface 的可用类型和配置
  • 创建绘制 surface
  • 在 OpenGL ES 3.0 或其他渲染 API 之间同步渲染
  • 管理纹理贴图等渲染资源
26. Context对象

Context的作用: 详情

  • 获取应用资源;
  • 访问应用preference;
  • 管理私有的应用文件和目录;
  • 获取未编译的应用资产;
  • 访问系统服务;
  • 管理私有应用数据库;
  • 使用应用权限。
27. JNI机制

JNI(Java Native Interface )标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。[详情](https://blog.csdn.net/shulianghan/article/details/18964835

27. JNI之registerNativeMethods方法

传统java Jni方式:1.编写带有native方法的Java类;2.使用javah命令生成.h头文件;3.编写代码实现头文件中的方法。这种方式必须遵循一定的方法命名格式,native方法名必须带有较长的前缀,不易修改维护。
RegisterNatives方法能帮助你把c/c++中的方法隐射到Java中的native方法,而无需遵循特定的方法命名格式。应用层级的Java类别透过VM而呼叫到本地函数。一般是根据VM去寻找*.so里的本地函数。如果需要连续呼叫很多次,每次都需要寻找一遍,会多花许多时间。此时,组件开发者可以自行将本地函数向VM进行登记,VM调registerNativeMethods()函数的用途有二:

  • 更有效率去找到函数;
  • 可在执行期间进行抽换。由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,可多次呼叫registerNativeMethods()函数来更换本地函数之指针,而达到弹性抽换本地函数之目的。详情
28. 硬件加速

硬件加速是指Android中在View上进行绘制的图形图像都使用GPU来进行绘制,使用硬件加速,在大部分时候都让绘制更加流畅,但付出的代价是需要消耗更多的内存资源。硬件加速在API L14之上是默认开启的,对于基本的View绘制,通过硬件加速可以增加绘图的流程性,但是要注意的是,并不是所有的2D图形绘制API都支持硬件加速。通过开发者选项中的“强制进行GPU渲染”,用户可以为全局打开硬件加速。Android上打开硬件加速之后View通过GPU渲染,关闭的话是使用Android中的2D图形库Skia。 由于硬件加速对某些2D绘图API的不支持,Android系统提供了Application, Activity, Window, View四种级别的控制方式。[详情](https://blog.csdn.net/qq_43666827/article/details/87353184)

29. this关键字

this关键字指向的是当前对象的引用。详情

  • this.属性名称,指访问类中的成员变量,用来区分成员变量和局部变量(重名问题);
  • this.方法名称,用来访问本类的成员方法;
  • this(),访问本类的构造方法。
30. treble机制

Android 8.0 版本的一项新元素是 Project Treble。这是 Android 操作系统框架在架构方面的一项重大改变,旨在让制造商以更低的成本更轻松、更快速地将设备更新到新版 Android 系统。Project Treble 适用于搭载 Android 8.0 及后续版本的所有新设备(这种新的架构已经在 Pixel 手机的开发者预览版中投入使用)。详情

31. 线程池

线程池的作用:详情

  • 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
  • 提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行;
  • 方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换(cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场));
  • 提供更强大的功能,延时定时线程池。

持续更新中…
若有错误,欢迎留言指正,多谢!
本文参考了很多文章,每个问题的详情附有参考链接。感谢原作者们,如有侵权,请及时联系。
欢迎收藏本文。转载请注明出处。

以上是关于绿鹅等大厂2020Android面试经验(持续整理更新中)的主要内容,如果未能解决你的问题,请参考以下文章

Android面试内容整理(持续收录中)

2022最新Android 大厂高频面试题解析大全(持续更新中~)下载超10W+

2021大厂Android面试经验,面试必会

2020-2022最新Android大厂校招社招面试题整理合集

Java相关面试题整理(持续更新)

2021大厂Android面试经验BO5,大海胜三场,拿下。