Android 开发面试的几部分
1、基础知识
基础知识包括几个部分:Java(JDK、JVM)、Android、数据结构和算法、计算机基础、设计模式,有的还会问 Flutter。
Java 部分:
不太推荐这部分只看博客,因为很多博客并不系统也不完整,推荐完整看一遍《深入理解 Java 虚拟机》这本书,基本上这里面涵盖了 JVM 相关的所有面试问题,包括内存分区、GC 机制、内存模型、锁、字节码、类加载等。JDK 的部分会杂一些,基本上可以归类为几种:容器(必问 HashMap、CurrentHashMap、ArrayList 等)、线程池(必问)、注解、同步工具、动态代理、notify/wait/sleep。这部分可以从一些 JDK 相关的文章中去整理一遍。
Android 部分:
这部分必问的是 Handler 机制、触摸事件传递、四大组件启动流程、View 绘制流程、Binder 机制、生命周期。经典的问题比如:
-
触摸事件一定是先传递到 Activity 然后才传递给 View 的么?
-
如果要在 Application 的 onCreate 生命周期之前执行一些逻辑,可以放在哪里?
-
draw、onDraw、dispatchDraw 执行的顺序如何?
-
View 真正显示是在 onResume 之前还是之后?
-
Activity 中嵌套有一个 Fragment,startActivity 之后,Activity 和 Fragment 的生命周期顺序是如何的?
数据结构与算法:
这部分在客户端开发中考察的比例会小一些,不过也有很多公司非常重视手写代码。经常碰到的问题有二叉树遍历、有序二维数组查找,排序等,基本上以《剑指 offer》或者《编程之美》为准就可以了。
计算机基础:
这部分涵盖的比较杂,比如计算机网络(7 层网络模型、Http 和 Https)、git 工作流和命令、计算机缓存策略、UML、信息安全等等。
设计模式:
在自己的项目中一定要多总结和使用设计模式,面试官问你使用过什么设计模式的时候,如果能回答出比如状态模式、责任链、装饰等等,肯定会加分不少。如果只是回答单例和建造者模式,那就会逊色多了。这部分推荐看《研磨设计模式》。
开源项目:
这个也是必问的环节之一,建议从自己项目使用的开源项目入手,跟着代码,画一遍流程图和架构图,仔细体会这里面设计的优点和缺点。
比如 OkHttp、LeakCanery 等等,这些都可以。开源项目根据面试官的个人经验不同,问的深入程度也不一样,这时候就需要你个人的一些引导技巧,比如面试官可能不是很熟悉这个框架,这时候你能主动表达出这里面设计的一些细节,也能给面试官留下不错的印象。切忌在简历上写只是用过但是没看过源码的框架。
重点项目经历
小厂的项目往往追求业务快速迭代上线,相对比较能容忍一些技术难点,但是这些技术难点往往是能体现一个人技术能力的。所以你如果在小厂,就不能过分陷入到业务中,必须跳出来找一些有亮点的技术点,做好做精。这些技术点才是一个程序员安身立命的本钱,否则公司业务陷入困境了,你的价值也就不复存在了。在如何准备面试中项目这部分,小编有几点心得:
分成三部分:
-
为什么要做这个点,
-
考虑了什么因素并且是如何做的,
-
取得了什么效果(比如启动速度从 1000ms 降到 200ms,这种具体的数据)
可以把几个小点串起来,这样可以显得更系统和全面一些,比如代码优化和性能优化结合在一起讲。
小厂的一些优化点往往只能做性价比最高的一部分,这时候还可以讲一下业界还有哪些更优的方案
最好再准备一下“如果你没有离职项目中还有有什么不足和可以优化的地方?”(笔者面试中碰到过几次这样的问题)
**小厂跳大厂,技术基础可以准备得很充分,但是项目绝对是一个弱项。**大厂一个 App 可能就有几十上百人在做,小厂很多所谓的优化在大厂的面试官眼中可能都是他们很早就做过了,这时候项目本身做了哪些东西并不是太重要,需要着重体现你解决问题的思路以及你是一个有追求的程序员,至少你要能做到业界的平均水平,这样才能有机会通过面试。
技术以外的东西
大厂面试中技术以外的部分也是非常重要的一部分,这部分大多是一些软技能:
自我驱动和追求
这部分主要是在平时工作中的一些自我驱动学习和自主追求一些技术能力,而不仅仅是完成需求就可以了,这样才能体现出你个人的成长性和潜力。
沟通和协作
这部分会评估你未来在团队的融入程度以及跟团队跨团队协作和沟通的能力。比如你在工作中是否能正确看待和解决一些跨部门沟通的问题。
我的面经总结
主要分为四个方面:
Android
-
Android 各个版本的特性(例如 6.0,动态权限)
-
ANR 的原因
-
Android 中进程间通信的方式和对比
-
AsyncTask 的实现原理及优缺点
-
Handler 机制
-
自定义 view 三部曲,流程(或者实际场景题,例如实现一个表盘,怎么做)
-
FrameWork 层次结构
-
Activity 生命周期(A 跳转到 B,A 和 B 中生命周期的执行顺序)
-
Dvlik、ART、JVM 三种虚拟机的区别
-
能不能在非 UI 线程更新 UI
-
Service 和 IntentService 的区别
-
Thread 和 HandlerThread 的区别
-
Touch 事件分发机制
-
怎么知道一个 Activity 是否发生了泄露
-
从点击一个 APP 图标开始发生了什么
-
Activity 启动流程(源码看一遍,能大致讲出来就行)
-
Activity 启动模式(四种)
-
Intent 启动 Activity 的方式
-
onStart 和 onResume 的区别
-
LruCache 以及实现原理
-
Android 中的 MVC、MVP、MVVM 的区别和联系
-
Parcelable 和 Serializable 的区别
-
Service 生命周期
-
Service 的启动方式及区别
-
Binder 机制
-
onSaveInstanceState 和 onRestoreInstanceState 的调用时机(需要理解,一般会出个场景让你判断)
-
onNewIntent 的调用时机(同上)
-
Android 中的性能优化和内存优化
-
RxJava 说一下(常用操作符,背压,线程切换原理,实际应用)
-
RxAndroid 说一下
-
EventBus 原理
-
Volley 原理
-
OkHttp 原理
-
Retrofit 原理
-
RxJava 和 Retrofit 结合
-
Dagger2 原理
-
组件化介绍一下(为什么要有组件化,怎么解决合并冲突问题)
-
快应用介绍一下
-
屏幕适配的常用方法
-
Service、进程保活
-
Glide 原理
-
AsyncTask 原理
-
SurfaceView 和普通 View 的区别
-
依赖注入框架的实现原理
-
Git 项目管理
-
什么是三级缓存
Java
-
常用的设计模式(掌握常见的大约 10 种就 Ok)
-
ThreadLoal 的作用及原理
-
线程的实现方式和实现原理
-
线程的状态,怎么创建线程,线程状态之间的转化(例如什么情况下线程会由运行变为阻塞)
-
进程的状态
-
transient 关键字的作用
-
sleep()和 wait()的区别
-
hashcode()方法的作用
-
==和 equals()方法区别(为什么重写了 equals()方法还要重写 hashcode 方法)
-
Java 的三大特性
-
接口和抽象类的区别
-
进程和线程的区别
-
线程池各参数的含义,以及四种常用线程池的应用场景
-
阻塞队列的特点和原理和应用场景
-
yield()方法的作用
-
多线程锁的类型,ReentrantLock,Lock,synchrnoized 关键字等
-
volatile 关键字的作用(在单例中的作用)
-
闭包的概念
-
泛型(什么是类型擦除,为什么要有泛型)
-
锁优化方式(自旋锁、自适应自旋、锁消除、锁粗化、偏向锁)
-
类执行顺序
-
StringBuffer 和 StringBuilder 的区别
-
HashMap 的实现原理(最好是 1.8 之前和 1.8 之后都要知道)
-
ConcurrentHashMap 的实现原理(同 1.8 之前和之后都要知道)
-
ArrayList 和 LinkedList 的区别
-
Java 内存区域(堆区和栈区的区别,什么是运行时常量池)
-
装箱和拆箱
-
OOM 异常
-
GC 机制(GC 区域,如何判断一个对象是否可以回收,GC 算法,GC 策略)
-
类加载机制(类加载过程双亲委派模型)
-
Java 内存模型(主内存与工作内存)
-
原子性(AtomicInteger 原理)、可见性(volatile 关键字)、有序性,如何保证
-
先行发生原则(happens-before 原则)
-
线程安全的实现方法
-
锁的类型(悲观锁,乐观锁 CAS 算法,公平锁,非公平锁等怎么实现)
-
StringBuffer 和 StringBuilder 区别
-
注解(自定义注解)
-
四种引用的概念和区别
计算机网络
-
HTTP 协议(头结构,几种请求方法,缓存机制,各版本的区别)
-
HTTPS 协议,和 HTTP 协议的区别,密钥协商的过程,SSL 机制
-
对称加密和非对称加密
-
TCP 和 UDP 的区别
-
TCP 的三次握手、四次挥手(包括状态码,以及四次挥手后为什么要等待 2*TIME_WAIT)
-
TCP 拥塞控制机制
-
TCP 滑动窗口机制
-
TCP 超时重传机制
-
DNS 协议
-
五层网络模型(物理层,数据链路层,网络层,传输层,应用层)
-
IP 头部
-
在浏览器地址输入一个网址,发生了什么
数据结构及算法
-
链表(单向链表,双向链表)
-
数组(旋转数组)
-
队列(双端队列,阻塞队列)
-
栈
-
堆(大根堆、小根堆)
-
二叉树(完全二叉树、满二叉树、二叉排序树、平衡二叉树,红黑树)(深度遍历,层序遍历)
-
图(重点掌握图的深度优先遍历和广度优先遍历)
-
七大排序算法(冒泡,选择,插入,归并排序,快速排序,堆排序,希尔排序)
-
剑指 offer 上的算法题(能够对照目录,一看题目,能有思路,就 ok)
-
二分查找
-
两个栈实现一个队列,两个队列实现一个栈
-
海量数据排序
-
topK 问题,有 1 千万个数,怎么快速找出最大的 100 个
-
合并两个有序数组,合并两个有序链表
-
杨氏矩阵(横向递增,纵向递增)中如何找到指定的数字
-
翻转一句话,例如 I am 3 years old,翻转后,old years 3 am I
-
有 10 亿条数据,现在只有 200M 内存,怎么找出这 10 亿条数据中出现次数最多的 100 条数据
面试宝典
面试必问知识点、BATJ 历年历年面试真题+解析
学习经验总结
(一)调整好心态心态是一个人能否成功的关键,如果不调整好自己的心态,是很难静下心来学习的,尤其是现在这么浮躁的社会,大部分的程序员的现状就是三点一线,感觉很累,一些大龄的程序员更多的会感到焦虑,而且随着年龄的增长,这种焦虑感会越来越强烈,那么唯一的解决办法就是调整好自己的心态,要做到自信、年轻、勤奋。这样的调整,一方面对自己学习有帮助,另一方面让自己应对面试更从容,更顺利。
(二)时间挤一挤,制定好计划一旦下定决心要提升自己,那么再忙的情况下也要每天挤一挤时间,切记不可“两天打渔三天晒网”。另外,制定好学习计划也是很有必要的,有逻辑有条理的复习,先查漏补缺,然后再系统复习,这样才能够做到事半功倍,效果才会立竿见影。
(三)不断学习技术知识,更新自己的知识储备对于一名程序员来说,技术知识方面是非常重要的,可以说是重中之重。**要面试大厂,自己的知识储备一定要非常丰富,若缺胳膊少腿,别说在实际工作当中,光是面试这一关就过不了。**对于技术方面,首先基础知识一定要扎实,包括自己方向的语言基础、计算机基础、算法以及编程等等。