300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Android面试从新手到架构师 一篇解析就够(1) 跳槽字节跳动

Android面试从新手到架构师 一篇解析就够(1) 跳槽字节跳动

时间:2024-04-17 15:30:10

相关推荐

Android面试从新手到架构师 一篇解析就够(1) 跳槽字节跳动

5.抽象工厂模式:

结构型(7):描述如何将类或对象按某种规则组成更大的结构

1.桥接模式:对于两个或以上纬度独立变化的场景,将抽象与具体实现分离,实例:用不同颜色画不同形状

2.外观模式:对外有一个统一接口,外部不用关心内部子系统的具体实现,这是"迪米特原则"的典型应用

3.适配器模式:改变类的接口,使原本由于接口不匹配而无法一起工作的两个类能够在一工作,实例:RecycleView 的 Adapter 不管什么类型的 View 都返回 ViewHolder

4.代理模式:由代理对象控制对原对象的引用,包括静态代理和动态代理

5.组合模式:将对象组成树形结构,用于对单个对象和组合对象的使用具有一致性,实例:ViewGroup

6.装饰模式:对对象包装一层,动态的增加一些额外功能,实例:ContextWrapper 包装 Context

7.享元模式:复用对象,实例:java 的常量池(比如 String),线程池,Message.obtain 等

行为型(11):描述类或对象之间怎么相互协作,怎样分配指责

1.观察者模式:一对多依赖关系,多个观察者可以同时监听某一个对象,实例:jetpack 的 lifeCycle 添加生命周期观察者

2.中介者模式:定义一个中介对象封装一系列对象的交互,解耦这些对象,实例:MVP 的 P

3.访问者模式:将作用于某数据结构中各元素的操作分离出来封装成独立的类,对这些元素添加新的操作,但不改变原数据结构,实例:asm 中的 classVisitor 中再分别对类注解、变量、方法等进行处理

4.状态模式:行为由状态决定,不同状态下由不同行为,与策略模式类似,实例:不同状态下有同一种操作的不同行为的子类实现

5.命令模式:将一个请求封装为一个对象发出,交给别的对象去处理请求,实例:Handler 发送定义好的消息事件

6.策略模式:将一系列的算法封装起来,方便替换,实例:动画的时间插值器

7.责任链模式:让多个对象都有机会处理一个事件,实例:View 事件传递机制

8.备忘录模式:保存对象之前的状态,方便后面恢复

9.迭代器模式:提供一种方法遍历容器中的元素,而不需要暴露该对象的内部表示,实例:集合的迭代器

10.解释器模式:多次出现的问题有一定规律,就可以归纳成一种简单的语言来解释,实例:AndroidManifest 文件、GLES 着色器语言

11.模版方法模式:定义一套固定步骤,方便直接执行,实例:AsyncTask

动态代理原理及实现

InvocationHandler 接口,动态代理类需要实现这个接口

Proxy.newProxyInstance,用于动态创建代理对象

Retrofit 应用:Retrofit 通过动态代理,为我们定义的请求接口都生成一个动态代理对象,实现请求

JVM:内存模型、内存结构、GC、四种引用、ClassLoader

=================================

JVM

定义:可以理解成一个虚构的计算机,解释自己的字节码指令集映射到本地 CPU 或 OS 的指令集,上层只需关注 Class 文件,与操作系统无关,实现跨平台

Kotlin 就是能解释成 Class 文件,所以可以跑在 JVM 上

JVM 内存模型

Java 多线程之间是通过共享内存来通信的,每个线程都有自己的本地内存

共享变量存放于主内存中,线程会拷贝一份共享变量到本地内存

volatile 关键字就是给内存模型服务的,用来保证内存可见性和顺序性

JVM 内存结构

线程私有:

1.程序计数器:记录正在执行的字节码指令地址,若正在执行 Native 方法则为空

2.虚拟机栈:执行方法时把方法所需数据存为一个栈帧入栈,执行完后出栈

3.本地方法栈:同虚拟机栈,但是针对的是 Native 方法

线程共享:

1.堆:存储 Java 实例,GC 主要区域,分代收集 GC 方法会吧堆划分为新生代、老年代

2.方法区:存储类信息,常量池,静态变量等数据

GC

回收区域:只针对堆、方法区;线程私有区域数据会随线程结束销毁,不用回收

回收类型:

1.堆中的对象:分代收集 GC 方法会吧堆划分为新生代、老年代。新生代:新建小对象会进入新生代;通过复制算法回收对象;老年代:新建大对象及老对象会进入老年代;通过标记-清除算法回收对象。

2.方法区中的类信息、常量池

判断一个对象是否可被回收:

1.引用计数法:有循环引用的缺点

2.可达性分析法:从 GC ROOT 开始搜索,不可达的对象都是可以被回收的。其中 GC ROOT 包括虚拟机栈/本地方法栈中引用的对象、方法区中常量/静态变量引用的对象。

Minor GC/Major GC/Full GC

Minor GC(Young GC):即新生代(分为一个 Eden 区和两个 Survivor 区)的垃圾回收

Eden 区无用对象被回收,存活对象会移到 Survivor 区

Survivor 区的存活对象会被复制到另一个 Survivor 区,复制次数也记做年龄,年龄足够大时(15)会移到老年代

如果 Survivor 区已满,则存活对象会被提前移动到老年代(过早提升),如果老年代也无法容纳,则会触发 Full GC(提升失败)

老年代的对象可能引用新生代对象,所以这个引用会被作为 GC Roots

Major GC:通常是跟 Full GC 等价的,回收整个堆

Full GC:回收整个堆,包括新生代和老年代

当要在老年代分配空间但无法容纳时触发

当主动调用 System.gc 时触发

四种引用

强引用:不会被回收

软引用:内存不足时会被回收

弱引用:gc 时会被回收

虚引用:无法通过虚引用得到对象,可以监听对象的回收

ClassLoader

类的生命周期:1.加载;2.验证;3.准备;4.解析;5.初始化;6.使用;7.卸载

类加载过程:1.加载:获取类的二进制字节流;生成方法区的运行时存储结构;在内存中生成 Class 对象 2.验证:确保该 Class 字节流符合虚拟机要求 3.准备:初始化静态变量 4.解析:将常量池的符号引用替换为直接引用 5.初始化:执行静态块代码、类变量赋值

类加载时机:1.实例化对象 2.调用类的静态方法 3.调用类的静态变量(放入常量池的常量除外)

类加载器:负责加载 class 文件 1.引导类加载器 - 没有父类加载器 2.拓展类加载器 - 继承自引导类加载器 3.系统类加载器 - 继承自拓展类加载器

双亲委托模型:

当要加载一个 class 时,会先逐层向上让父加载器先加载,加载失败才会自己加载

为什么叫双亲?不考虑自定义加载器,系统类加载器需要网上询问两层,所以叫双亲

判断是否是同一个类时,除了类信息,还必须时同一个类加载器

优点:防止重复加载,父加载器加载过了就没必要加载了;安全,防止篡改核心库类

Android 基础:Activity、View 绘制、动画、Window、SurfaceView、事件分发

======================================================

Activity 生命周期

A 打开 B 界面,会先执行 A 的 onPause,再执行 B 的 onCreate、onStart、onResume,再执行 A 的 onStop

B 界面的打开依赖 A 界面 onPause 方法执行完,所以不要在 onPause 中做耗时操作

Activity 启动模式

standard 标准模式

singleTop 栈顶复用模式,适用于推送点击消息界面

singleTask 栈内复用模式,适用于 App 首页

singleInstance 单例模式,单独位于一个任务栈中,适用于拨打电话界面

细节:

taskAffinity:任务相关性,用于指定任务栈名称,默认为应用包名

allowTaskReparenting:允许转移任务栈

View 工作原理

ViewRoot 的 performTraversals 方法调用触发开始 View 的绘制,然后会依次调用:

performMeasure:遍历 View 的 measure 测量尺寸

performLayout:遍历 View 的 layout 确定位置

performDraw:遍历 View 的 draw 绘制

MeasureSpec 测量规则

EXACTLY:父 View 指定了子 View 确切的大小

AT_MOST:父 View 指定一个大小,子 View 不能超过这个值

UNSPECIFIEND:父 View 不对子 View 有任何限制

View 动画、帧动画及属性动画

View 动画:

作用对象是 View,可用 xml 定义,建议 xml 实现比较易读

支持四种效果:平移、缩放、旋转、透明度

帧动画:

通过 AnimationDrawable 实现,容易 OOM

属性动画:

可作用于任何对象,可用 xml 定义,Android 3 引入,建议代码实现比较灵活

包括 ObjectAnimator、ValuetAnimator、AnimatorSet

时间插值器:根据时间流逝的百分比计算当前属性改变的百分比,系统预置匀速、加速、减速等插值器

类型估值器:根据当前属性改变的百分比计算改变后的属性值,系统预置整型、浮点、色值等类型估值器

使用注意事项:避免使用帧动画,容易OOM;界面销毁时停止动画,避免内存泄漏;开启硬件加速,提高动画流畅性

硬件加速原理:将 cpu 一部分工作分担给 gpu ,使用 gpu 完成绘制工作;从工作分摊和绘制机制两个方面优化了绘制速度

Window 、WindowManager、WMS、SurfaceFlinger

WIndow:抽象概念不是实际存在的,而是以 View 的形式存在,通过 PhoneWindow 实现

WindowManager:外界访问 Window 的入口,内部与 WMS 交互是个 IPC 过程

WMS:管理窗口 Surface 的布局和次序,作为系统级服务单独运行在一个进程

SurfaceFlinger:将 WMS 维护的窗口按一定次序混合后显示到屏幕上

SurfaceView、TextureView、SurfaceTexture、GLSurfaceView

SurfaceView:使用双缓冲机制,有自己的 surface,在一个独立的线程里绘制,Android7.0之前不能平移、缩放

TextureView:持有 SurfaceTexture,将图像处理为 OpenGL 纹理更新到 HardwareLayer,必须开启硬件加速,Android5.0之前在主线程渲染,之后有独立的渲染线程,可以平移、旋转、缩放

SurfaceTexture:将图像流转为 OpenGL 外部纹理,不直接显示

GLSurfaceView:加入 EGL 管理,自带 GL 上下文和 GL 渲染线程

事件分发机制

一个 MotionEvent 产生后,按 Activity -> Window -> decorView -> View 顺序传递,View 传递过程就是事件分发,主要依赖三个方法:

dispatchTouchEvent:用于分发事件,只要接受到点击事件就会被调用,返回结果表示是否消耗了当前事件

onInterceptTouchEvent:用于判断是否拦截事件,当 ViewGroup 确定要拦截事件后,该事件序列都不会再触发调用此 ViewGroup 的 onIntercept

onTouchEvent:用于处理事件,返回结果表示是否处理了当前事件,未处理则传递给父容器处理

细节:

一个事件序列只能被一个 View 拦截且消耗

View 没有 onIntercept 方法,直接调用 onTouchEvent 处理

OnTouchListener 优先级比 OnTouchEvent 高,onClickListener 优先级最低

requestDisallowInterceptTouchEvent 可以屏蔽父容器 onIntercept 方法的调用

Android 通信:Handler、Parcelable、IPC、Binder

========================================

Handler、MessageQueue、Looper 及 postDelayed 原理

Handler:开发直接接触的类,内部持有 MessageQueue 和 Looper

MessageQueue:消息队列,内部通过单链表存储消息

Looper:内部持有 MessageQueue,循环查看是否有新消息,有就处理,没就阻塞

postDelayed 其实就是调用 postAtTime 实现的,传入的时间戳基于 SystemClock.uptimeMillis,即 boot 时间

进一步会调用 MessageQueue#enqueueMessage 将消息插入到队列

插入消息时会根据消息执行时刻 Message#when 来决定插入到什么位置,when 为 0 或最早执行就会插入到链表头,否则按执行时刻排序插入

插入后如果正在阻塞则会尝试唤醒,插入到头部则会唤醒,插入到队列中则再根据其他条件判断是否需要唤醒

Looper#loop 中调用 MessageQueue#next 取消息,next 方法除非是即将销毁时会返回 null,否则就会返回消息,没有消息就阻塞。如果当前时刻还没到消息的执行时刻 when,就会再阻塞这个时间差的时间

阻塞是调用 nativePollOnce 实现,基于 Linux epoll 事件管理机制

Looper#loop 中取出消息后通过 Message#target 拿到 handler,然后调用 Handler#dispatchMessage 分发处理消息

Serializable、Parcelable

Serializable :Java 序列化方式,适用于存储和网络传输,serialVersionUID 用于确定反序列化和类版本是否一致,不一致时反序列化回失败

Parcelable :Android 序列化方式,适用于组件通信数据传递,性能高,因为不像 Serializable 一样有大量反射操作

Linux IPC 方式

管道

socket

信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段

信号:不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等(Android 中的 Kill Process 采用的就是 signal(信号)机制)

消息队列:信息复制两次,额外的 CPU 消耗;不合适频繁或信息量大的通信

共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决

Binder

Android 中基于 C/S 结构的一种面向对象的进程间通信的机制

主要用在 system_server 进程与上层 App 层的 IPC 交互

包含:Client,Server,Binder 驱动和 ServiceManager 四部分

Android 为什么选择 binder

性能:使用 mmap 一次数据拷贝实现 IPC,传统 IPC:用户 A 空间->内核->用户 B 空间;mmap 将内核与用户 B 空间映射,实现直接从用户 A 空间->用户B空间,而 Linux 的管道、消息队列、Socket 都需要拷贝两次,binder 仅次于共享内存

稳定性:基于C/S架构,架构清晰,稳定性好,不像共享内存实现方式复杂,需要充分考虑访问临界资源的并发同步问题

安全:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份

Android IPC 方式

Intent extras、Bundle:要求传递数据能被序列化,实现 Parcelable、Serializable ,适用于四大组件通信

文件共享:适用于交换简单的数据实时性不高的场景

AIDL:AIDL 接口实质上是系统提供给我们可以方便实现 Binder 的工具

Android Interface Definition Language,可实现跨进程调用方法

服务端:将暴漏给客户端的接口声明在 AIDL 文件中,创建 Service 实现 AIDL 接口并监听客户端连接请求

客户端:绑定服务端 Service ,绑定成功后拿到服务端 Binder 对象转为 AIDL 接口调用

RemoteCallbackList 实现跨进程接口监听,同个 Binder 对象做 key 存储客户端注册的 listener

监听 Binder 断开:1.Binder.linkToDeath 设置死亡代理;2. onServiceDisconnected 回调

Messenger:基于 AIDL 实现,服务端串行处理,主要用于传递消息,适用于低并发一对多通信

ContentProvider:基于 Binder 实现,适用于一对多进程间数据共享

Socket:TCP、UDP,适用于网络数据交换

Android 系统:系统架构、Dalvik、ART、系统启动、类加载器、Apk 打包、Apk 安装

==================================================

Android 系统架构

应用层

Framework 框架层

本地 Native 库和 Android 运行时环境

HAL

Linux 内核

Dalvik 和 ART

Dalvik

谷歌设计专用于 Android 平台的 Java 虚拟机,可直接运行 .dex 文件,适合内存和处理速度有限的系统

JVM 指令集是基于栈的;Dalvik 指令集是基于寄存器的,代码执行效率更优

ART

Dalvik 每次运行都要将字节码转换成机器码;ART 在应用安装时就会转换成机器码,执行速度更快

ART 存储机器码占用空间更大,空间换时间

Android 系统启动流程

按电源键 -> 加载引导程序 BootLoader 到 RAM -> 执行 BootLoader 程序启动内核 -> 启动 init 进程 -> 启动 Zygote 和各种守护进程 -> 启动 System Server 服务进程开启 AMS、WMS 等 -> 启动 Launcher 应用进程

Android 类加载器

BootClassLoader(加载 Framework 级别的类)

PathClassLoader(加载系统类和 data/app 应用目录下的 dex 文件)

DexClassLoader(加载自定义的 dex 文件或 jar,支持从 sd 卡中进行加载)

APK 打包流程

1.aapt 打包资源文件生成 R.java 文件;aidl 生成 java 文件

2.将 java 文件编译为 class 文件

3.将工程及第三方的 class 文件转换成 dex 文件

4.将 dex 文件、so、编译过的资源、原始资源等打包成 apk 文件

5.签名

6.资源文件对齐,减少运行时内存

App 安装过程

首先要解压 APK,资源、so等放到应用目录

Dalvik 会将 dex 处理成 ODEX ;ART 会将 dex 处理成 OAT;

OAT 包含 dex 和安装时编译的机器码

Android 优化:网络优化、卡顿优化、内存优化、瘦包、内存泄漏、ANR、Native Crash

==================================================

网络优化及检测

速度:1.GZIP 压缩(okhttp 自动支持);2.Protocol Buffer 替代 json;3.优化图片/文件流量;4.IP 直连省去 DNS 解析时间

成功率:1.失败重试策略;

流量:1.GZIP 压缩(okhttp 自动支持);2.Protocol Buffer 替代 json;3.优化图片/文件流量;5.文件下载断点续传 ;6.缓存

协议层的优化,比如更优的 http 版本等

监控:Charles 抓包、Network Monitor 监控流量

UI卡顿优化

减少布局层级及控件复杂度,避免过度绘制

使用 include、merge、viewstub

优化绘制过程,避免在 Draw 中频繁创建对象、做耗时操作

内存优化

内存问题

内存泄漏

内存抖动:频繁创建临时对象

Bitmap 大内存:规避位图超标

代码质量:intdef 代替枚举,使用 SparseArray 代替 HashMap

检测工具

MAT(Memory Analysis Tools) ,可分析 Java 堆数据,可查看实例占用空间、引用关系等

Android Studio 自带的 Profiler

LeakCanary:通过弱引用和引用队列监控对象是否被回收,比如 Activity 销毁时开始监控此对象,检测到未被回收则主动 gc ,然后继续监控

瘦包

1.资源方面:资源在线化、图片使用 webp 格式、tint 着色生成不同色调的切、使用 icon font

2.so 库:保留一个 cpu 架构的 so 文件

3.AS Inspect Code 清除无用代码和资源

4.代码混淆:使用 ProGuard 可以移除无用的类、字段、方法(压缩),移除无用字节码指令

5.不保留行号:使用 ProGuard 配置不保留行号

6.开启 shrinkResources:移除无用资源

7.资源混淆:使用 AndResGuard 缩短资源长度,对资源进行 7z 压缩等(直接对apk操作)

8.代码结构简化,比如用 intdef 代替 枚举(一个枚举有1~1.4kb大小)

9.使用 compileOnly 在只需编译时依赖的场景,不会打到 apk 里

10.使用 thinR 插件剔除 R 文件,将引用 R 字段的地方替换成对应常量

11.Android 7.0 使用 V2(apksigner) 代替 V1(jarsigner) 签名工具

12.动态加载 so 库(System.load加载绝对路径文件)、插件化技术、App Bundle

13.使用 facebook 的 redex

内存泄漏场景及规避

1.静态变量、单例强引跟生命周期相关的数据或资源,包括 EventBus

2.游标、IO 流等资源忘记主动释放

3.界面相关动画在界面销毁时及时暂停

4.内部类持有外部类引用导致的内存泄漏

handler 内部类内存泄漏规避:1.使用静态内部类+弱引用 2.界面销毁时清空消息队列

检测:Android Studio Profiler

ANR 问题及分析

anr 分类

主线程 5s 内没有处理完输入事件

service 阻塞 20s

前台广播阻塞 10s 或后台广告阻塞 20s

ContentProvider publish 在 20s 内没有处理完

anr 发生过程

1.捕获到 anr,发送 linux 信号量 3

2.进程接受到信号量将 anr 信息写入 data/anr/traces.txt 文件

3.Log 打印 anr 信息

4.进程进入 anr 状态,弹出 anr 提示框

监控 anr

1.Android 5.0 以下监听 traces.txt 文件写入

2.每隔 5s 向主线程发送消息判断主线程是否阻塞

分析 anr

查看 cpu 负载是否是 cpu 资源紧张导致

查看堆栈看是否是我们的代码耗时过长

避免 anr

主线程中不要做耗时操作,注意使用 IntentService

降低子线程优先级,让主线程可以更多的获取到 cpu 资源

Native Crash

崩溃过程:native crash 时操作系统会向进程发送信号,崩溃信息会写入到 data/tombstones 下,并在 logcat 输出崩溃日志

定位:so 库剥离调试信息的话,只有相对位置没有具体行号,可以使用 NDK 提供的 addr2line 或 ndk-stack 来定位

addr2line:根据有调试信息的 so 和相对位置定位实际的代码处

ndk-stack:可以分析 tombstone 文件,得到实际的代码调用栈

其他:解析 XML、进程保活、播放器、Lint、CI、CD、AOP、JetPack

=========================================

Android 解析 XML

SAX:流式解析

DOM:先把 XML 全部读取到内存,再访问树形结构,很消耗内存

PULL:流式解析,Android 内置的默认解析方式

热修复、插件化、组件化

热修复原理:

Native Hook(AndFix):直接在 native 层进行方法的结构体信息对换

分包(QFix):插入新 dex 到 dexElements[],利用 ClassLoader 通过遍历 dexElements[] 来 findClass 的特性

Java Hook(Robust):hook 每个方法,在每个方法里埋好准备替换的逻辑

插件化:DexClassLoader 动态加载,四大组件未注册问题通过 hook AMS、Instrumentation 等解决,VirtualAPK 源码分析

组件化:ARoute 路由实现:通过 APT 解析 @Route 等注解,结合 JavaPoet 生成路由表,即路由与 Activity 的映射关系

进程保活

进程优先级:1.前台进程 ;2.可见进程;3.服务进程;4.后台进程;5.空进程

进程被 kill 场景:1.切到后台内存不足时被杀;2.切到后台厂商省电机制杀死;3.用户主动清理

保活方式:

1.Activity 提权:挂一个 1像素 Activity 将进程优先级提高到前台进程

2.Service 提权:启动一个前台服务(API>18会有正在运行通知栏)

3.广播拉活

4.Service 拉活

最后

面试题文档来啦,内容很多,485页!

由于笔记的内容太多,没办法全部展示出来,下面只截取部分内容展示。

CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

1111道Java工程师必问面试题

MyBatis 27题 + ZooKeeper 25题 + Dubbo 30题:

Elasticsearch 24 题 +Memcached +Redis 40题:

Spring 26 题+ 微服务 27题+ Linux 45题:

Java面试题合集:

个方法里埋好准备替换的逻辑

插件化:DexClassLoader 动态加载,四大组件未注册问题通过 hook AMS、Instrumentation 等解决,VirtualAPK 源码分析

组件化:ARoute 路由实现:通过 APT 解析 @Route 等注解,结合 JavaPoet 生成路由表,即路由与 Activity 的映射关系

进程保活

进程优先级:1.前台进程 ;2.可见进程;3.服务进程;4.后台进程;5.空进程

进程被 kill 场景:1.切到后台内存不足时被杀;2.切到后台厂商省电机制杀死;3.用户主动清理

保活方式:

1.Activity 提权:挂一个 1像素 Activity 将进程优先级提高到前台进程

2.Service 提权:启动一个前台服务(API>18会有正在运行通知栏)

3.广播拉活

4.Service 拉活

最后

面试题文档来啦,内容很多,485页!

由于笔记的内容太多,没办法全部展示出来,下面只截取部分内容展示。

CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

1111道Java工程师必问面试题

[外链图片转存中…(img-NAxVUQIz-1630994672791)]

MyBatis 27题 + ZooKeeper 25题 + Dubbo 30题:

[外链图片转存中…(img-DlSEIakr-1630994672792)]

Elasticsearch 24 题 +Memcached +Redis 40题:

[外链图片转存中…(img-XSRwiEfi-1630994672793)]

Spring 26 题+ 微服务 27题+ Linux 45题:

[外链图片转存中…(img-DPwOwWl0-1630994672795)]

Java面试题合集:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。