- 要求
- 项目经验
- 面试题解析
- Java相关
- 1. 容器(HashMap、HashSet、LinkedList、ArrayList、数组等)
- 2.垃圾回收算法(JVM)
- 3.类加载过程 ClassLoader(需要多看看,重在理解,对于热修复和插件化比较重要)
- 4.反射
- 5.多线程和线程池
- 6.HTTP、HTTPS、TCP/IP、Socket通信、三次握手四次挥手过程
- 7.设计模式(六大基本原则、项目中常用的设计模式、手写单例等)
- 8.谈谈对 java 状态机的理解。谈谈应用更新(灰度、强制更新、分区更新?)
- 9.断点续传
- 10.Java 四大引用
- 11.Java 的泛型, 和 的区别
- 12.final、finally、finalize 的区别
- 13.接口、抽象类的区别
- 14.String、StringBuffer、StringBuilder
- Android相关
- 1. 自定义 View
- 2. 事件拦截分发
- 3. 解决过的一些性能问题,在项目中的实际运用。
- 4. 性能优化工具
- 5. 性能优化 (讲讲你自己项目中做过的性能优化)
- 6. Http[s]请求慢的解决办法(DNS、携带数据、直接访问 IP)
- 7. 缓存自己如何实现(LRUCache 原理)
- 8. 图形图像相关:OpenGL ES 管线流程、EGL 的认识、Shader 相关
- 9. SurfaceView、TextureView、GLSurfaceView 区别及使用场景
- 10. 动画、差值器、估值器(Android中的View动画和属性动画 - 简书、Android 动画 介绍与使用)
- 11. MVC、MVP、MVVM
- 12. Handler、ThreadLocal、AsyncTask、IntentService 原理及应用
- 13. Gradle(Groovy 语法、Gradle 插件开发基础)
- 14. 热修复、插件化
- 15. 组件化架构思路
- 16. 系统打包流程
- 17. Android 有哪些存储数据的方式。
- 18. SharedPrefrence 源码和问题点;
- 19. sqlite 相关
- 20. 如何判断一个 APP 在前台还是后台?
- 21. Activity启动模式
- 22. 混合开发
- 23. 进程保活
- 24. 屏幕适配
- Android Framework
- 框架源码
- 音视频&FFmpeg&播放器
- 算法与数据结构
- 开放问题
- Java相关
- 项目面试常见问题(★★★)
- 相关链接 & 项目
要求
1、结合招聘岗位,只讲重点。 简历内容这么多,实际的你,更有很多很多可以描述的东西。但时间有限,没有面试官会听你说个没完。 根据你求职的岗位,说重点即可。 其实简历制作的原则,也是一样。只是自我介绍时间更短,内容更精华。
2、有理有据,少说空话。 如果你说“自己学习能力强”,这就是一句假大空的话。谁都可以说自己学习能力强。 你如果真的在这方面有突出,就要举一个例子,比如是1个月从0到1考了什么证书等。
3、有开头有结尾,有逻辑。 开头问候,结尾总结。中间1、2、3条理清楚。
4、特别提醒。 自我介绍中的内容,很可能是面试官后续发问的内容。所以,一是要引起重视,讲最重要的,你最想让面试官知道的内容。 而是做好准备,扬长避短,不要给自己挖坑。
项目经验
项目经验这块因人而异,把觉得做的有亮点的东西挑出来,从四个方面来准备:
- 功能模块的实现
- 达到了什么效果
- 遇到了什么问题,是如何解决的
- 涉及到的相关知识点
项目经验和知识点 其实是一个双向的过程,要 试图去找到它们之间的联系:
- 谈到项目经验的时候,可以说:做完 xx 之后,我还去了解了一下 yy 背后的原理,xxx,这里体现的是 你是否有求知欲 。
谈到知识点的时候,可以说:yy 的原理是这样的,在 xx 项目中我是如何应用它来解决问题的,这里体现的是 你是否具备把知识付诸实践的能力。
面试题解析
Java相关
1. 容器(HashMap、HashSet、LinkedList、ArrayList、数组等)
HashMap 、HashTable
HashMap基于数组和链表实现,数组是 HashMap 的主体;链表是为解决哈希冲突而存在的
- 当发生哈希冲突且链表 size 大于阈值时会扩容,JAVA 8 会将链表转为红黑树提高性能 允许 key/value 为 null
HashTable
- 数据结构和 HashMap 一样
- 不允许 value 为 null
- 线程安全
ArrayList、LinkedList
ArrayList
基于数组实现,查找快:o(1),增删慢:o(n) 初始容量为10,扩容通过 System.arrayCopy 方法
LinkedList
基于双向链表实现,查找慢:o(n),增删快:o(1) 封装了队列和栈的调用
ArrayMap、SparseArray
ArrayMap
1.基于两个数组实现,一个存放 hash;一个存放键值对。扩容的时候只需要数组拷贝,不需要重建哈希表 2.内存利用率高 3.不适合存大量数据,因为会对 key 进行二分法查找(1000以下)
SparseArray
1.基于两个数组实现,int 做 key 2.内存利用率高 3.不适合存大量数据,因为会对 key 进行二分法查找(1000以下)
2.垃圾回收算法(JVM)
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 开始搜索,不可达的对象都是可以被回收的
3.类加载过程 ClassLoader(需要多看看,重在理解,对于热修复和插件化比较重要)
类的生命周期:
1.加载;2.验证;3.准备;4.解析;5.初始化;6.使用;7.卸载
类加载过程:
1.加载:获取类的二进制字节流;生成方法区的运行时存储结构;在内存中生成 Class 对象
2.验证:确保该 Class 字节流符合虚拟机要求
3.准备:初始化静态变量
4.解析:将常量池的符号引用替换为直接引用
5.初始化:执行静态块代码、类变量赋值
类加载时机:
1.实例化对象
2.调用类的静态方法
3.调用类的静态变量(放入常量池的常量除外)
类加载器:负责加载 class 文件
分类:
1.引导类加载器 - 没有父类加载器
2.拓展类加载器 - 继承自引导类加载器
3.系统类加载器 - 继承自拓展类加载器
双亲委托模型:
当要加载一个 class 时,会先逐层向上让父加载器先加载,加载失败才会自己加载
为什么叫双亲?不考虑自定义加载器,系统类加载器需要网上询问两层,所以叫双亲
判断是否是同一个类时,除了类信息,还必须时同一个类加载器
优点:
- 防止重复加载,父加载器加载过了就没必要加载了
- 安全,防止篡改核心库类
4.反射
5.多线程和线程池
volatile 关键字
- 只能用来修饰变量,适用修饰可能被多线程同时访问的变量
- 相当于轻量级的 synchronized,volatitle 能保证有序性(禁用指令重排序)、可见性;后者还能保证原子性
- 变量位于主内存中,每个线程还有自己的工作内存,变量在自己线程的工作内存中有份拷贝,线程直接操作的是这个拷贝
- 被 volatile 修饰的变量改变后会立即同步到主内存,保持变量的可见性。
双重检查单例,为什么要加 volatile?
- volatile想要解决的问题是,在另一个线程中想要使用instance,发现instance!=null,但是实际上instance还未初始化完毕这个问题
- 将instance =newInstance();拆分为3句话是。1.分配内存2.初始化3.将instance指向分配的内存空
- volatile可以禁止指令重排序,确保先执行2,后执行3
wait 和 sleep
- sleep 是 Thread 的静态方法,可以在任何地方调用
- wait 是 Object 的成员方法,只能在 synchronized 代码块中调用,否则会报 IllegalMonitorStateException 非法监控状态异常
- sleep 不会释放共享资源锁,wait 会释放共享资源锁
lock 和 synchronized
- synchronized 是 Java 关键字,内置特性;Lock 是一个接口
- synchronized 会自动释放锁;lock 需要手动释放,所以需要写到 try catch 块中并在 finally 中释放锁
- synchronized 无法中断等待锁;lock 可以中断
- Lock 可以提高多个线程进行读/写操作的效率
- 竞争资源激烈时,lock 的性能会明显的优于 synchronized
可重入锁
- 定义:已经获取到锁后,再次调用同步代码块/尝试获取锁时不必重新去申请锁,可以直接执行相关代码
- ReentrantLock 和 synchronized 都是可重入锁
公平锁
- 定义:等待时间最久的线程会优先获得锁
- 非公平锁无法保证哪个线程获取到锁,synchronized 就是非公平锁
- ReentrantLock 默认时非公平锁,可以设置为公平锁
乐观锁和悲观锁
- 悲观锁:线程一旦得到锁,其他线程就挂起等待,适用于写入操作频繁的场景;synchronized 就是悲观锁
- 乐观锁:假设没有冲突,不加锁,更新数据时判断该数据是否过期,过期的话则不进行数据更新,适用于读取操作频繁的场景
- 乐观锁 CAS:Compare And Swap,更新数据时先比较原值是否相等,不相等则表示数据过去,不进行数据更新
- 乐观锁实现:AtomicInteger、AtomicLong、AtomicBoolean
死锁 4 个必要条件
- 互斥
- 占有且等待
- 不可抢占
- 循环等待
synchronized 原理
- 每个对象都有一个监视器锁:monitor,同步代码块会执行 monitorenter 开始,motnitorexit 结束
- wait/notify 就依赖 monitor 监视器,所以在非同步代码块中执行会报 IllegalMonitorStateException 异常
6.HTTP、HTTPS、TCP/IP、Socket通信、三次握手四次挥手过程
HTTPS
HTTP 是超文本传输协议,明文传输;HTTPS 使用 SSL 协议对 HTTP 传输数据进行了加密
HTTP 默认 80 端口;HTTPS 默认 443 端口
优点:安全 缺点:费时、SSL 证书收费,加密能力还是有限的,但是比 HTTP 强多了
解决的问题:正确获得服务端的公钥,采用证书机制,另外为了优化解密速度,采取对称加密
实践:HTTPS双向认证指南、利用Fiddler抓包Https指南
TCP 有哪些状态 三次握手、四次挥手。
TCP 三次握手
A:你能听到吗?
B:我能听https://leetcode.jp/handler%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E7%BA%BF%E7%A8%8B%E4%B9%8B%E9%97%B4%E7%9A%84%E5%88%87%E6%8D%A2%E7%9A%84-2/amp/到,你能听到吗?
A:我能听到,开始吧
A 和 B 两方都要能确保:我说的话,你能听到;你说的话,我能听到。所以需要三次握手
TCP 四次挥手
A:我说完了
B:我知道了,等一下,我可能还没说完
B:我也说完了
A:我知道了,结束吧
B 收到 A 结束的消息后 B 可能还没说完,没法立即回复结束标示,只能等说完后再告诉 A :我说完了。
为啥是三次不是两次?
既要保证数据可靠传输,又要提高传输的效率,三次刚刚好
HTTPS 和 HTTP 的区别,HTTPS 2.0 3.0?
浏览器输入一个 URL 按下回车网络传输的流程?
问的深一点的可能涉及到网络架构,每层有什么协议,FTP 相关原理,例:TCP 建立连接后,发包频率是怎么样的?
TCP/IP
TCP 连接;可靠;有序;面向字节流;速度慢;较重量;全双工;适用于文件传输、浏览器等
- 全双工:A 给 B 发消息的同时,B 也能给 A 发
- 半双工:A 给 B 发消息的同时,B 不能给 A 发
UDP 无连接;不可靠;无序;面向报文;速度快;轻量;适用于即时通讯、视频通话等
7.设计模式(六大基本原则、项目中常用的设计模式、手写单例等)
生产者模式和消费者模式的区别?
单例模式双重加锁,为什么这样做?
知道的设计模式有哪些?
项目中常用的设计模式有哪些?
手写生产者、消费者模式。
手写观察者模式代码。
适配器模式、装饰者模式、外观模式的异同?
创建型模式
Abstract Factory(抽象工厂模式):为创建一组相关或者是相互依赖的对象提供一个接口,而不需要制定它们的具体类。
Builder(建造者模式):将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。
Factory Method(工厂方法模式):定义一个创建对象的接口,让子类决定实例化哪个类。
Prototype(原型模式):用原型实例指向创建对象的种类,并通过拷贝这些原型创建新的对象。
Singleton(单例模式):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
结构型模式
Adapter(适配器模式):适配器模式把一个类的接口换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
Bridge(桥接模式):将抽象部分与实现部分分离,使他们都可以独立地进行变化。
Composite(组合模式):组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构,并且能够让客户端以一致的方式处理个别对象以及组合对象。
Decorator(装饰者模式):动态地将职责附加到对象上,对于扩展功能来说,装饰者提供了有别于继承的另一种选择。
Facade(外观模式):外观模式提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。
Flyweight(享元模式):使用共享对象可有效地支持大量细粒度的对象。
Proxy(代理模式):为另一个对象提供一个代理以控制对这个对象的访问。
行为型模式
Chain of responsibility(责任链模式):使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
Command(命令模式):将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
Interpreter(解释器模式):给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
Iterator(迭代器模式):提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
Mediator(中介者模式):包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用,从而使耦合松散,而且可以独立地改变它们之间的交互。
Memento(备忘录模式):在不破坏封装的前提下,捕捉一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可将该对象恢复到原先保存的状态。
Observer(观察者模式):定义对象间一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
State(状态模式):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
Strategy(策略模式):策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。
Template method(模板方法模式):定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
Visitor(访问者模式):封装一些作用于某种数据结构中的个元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。
其他特殊模式
Object Pool(对象池模式):维护一定数量的对象集合,使用时直接向对象池申请,以跳过对象的 expensive initialization 。
适配器模式是将两个不兼容的类融合在一起
装饰者模式(窗口横屏或竖屏/Activity)
在结构上类似于代理模式,但是不同的是代理模式目的是提供访问控制,而装饰模式是给一个对象动态的添加一些属性,为对象添加行为。
代理模式(明星与经纪人/Binder)
代理模式是为另一个对象提供代表,以便控制客户对象的访问。
外观模式(第三方SDK)
外观模式是采用提供一个统一的接口,来访问一群子系统的一群接口。。适配器模式的意图是,“改变”接口符合客户的期望;而
外观模式的意图是,提供子系统的一个简化接口。
桥接模式(汽车与轮子)
桥接模式是为了将抽象与实现部分分离,使他们可以独立进行实现。二者为两个不同部分,没有实现同一个接口,这是与装饰模式,代理模式最大区别。
8.谈谈对 java 状态机的理解。谈谈应用更新(灰度、强制更新、分区更新?)
9.断点续传
基础知识:
- Http基础:在Http请求中,可以加入请求头Range,下载指定区间的文件数。
- RandomAccessFile:支持随机访问,可以从指定位置进行数据的读写。
有了这个基础以后,思路就清晰了:
- 通过HttpUrlConnection获取文件长度。
- 自己分配好线程进行制定区间的文件数据的下载。
- 获取到数据流以后,使用RandomAccessFile进行指定位置的读写。
在本地下载过程中要使用数据库实时存储到底存储到文件的哪个位置了,这样点击开始继续传递时,才能通过HTTP的GET请求中的setRequestProperty(“Range”,”bytes=startIndex-endIndex”);方法可以告诉服务器,数据从哪里开始,到哪里结束。同时在本地的文件写入时,RandomAccessFile的seek()方法也支持在文件中的任意位置进行写入操作。最后通过广播或事件总线机制将子线程的进度告诉Activity的进度条。关于断线续传的HTTP状态码是206,即HttpStatus.SC_PARTIAL_CONTENT。
10.Java 四大引用
问到泛型、泛型擦除、通配符相关的东西
12.final、finally、finalize 的区别
final
1,final修饰的class,代表不可以继承扩展。
2、final的方法也是不可以重写的。
3、final修饰的变量是不可以修改的。这里所谓的不可修改对于基本类型来来,的确是不可以修改。而对于引用类型来说,只能说不能重新赋值。也就是不能改变引用地址。但是作为引用类型,它内部所包含的内容如果不是final则可以随意修改
finally
提到finally,那么try-catch就逃不掉了。finally 则是Java保证重点代码一定要被执行的一种机制。最常用的地方:通过try-catch-finally来进行类似资源释放、保证解锁等动作
finalize
设计之初的作用就是:在CG要回收某个对象时,让这个对象有底气的大喊一声:“报告,我还能再抢救一下!”。但是也正是因为如此,JVM要对它进行额外处理。finalize也就成为了CG回收的阻碍者,也就会导致这个对象经过多个垃圾收集周期才能被回收。
区别
一、性质不同
(1)final为关键字; (2)finalize()为方法; (3)finally为为区块标志,用于try语句中;
二、作用
(1)final为用于标识常量的关键字,final标识的关键字存储在常量池中(在这里final常量的具体用法将在下面进行介绍);
(2)finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);
(3)finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行
13.接口、抽象类的区别
14.String、StringBuffer、StringBuilder
String 为什么要设计成不可变的?
String是不可变的(修改String时,不会在原有的内存地址修改,而是重新指向一个新对象),String用final修饰,不可继承,String本质上是个final的char[]数组,所以char[]数组的内存地址不会被修改,而且String 也没有对外暴露修改char[]数组的方法。不可变性可以保证线程安全以及字符串串常量池的实现。
Android相关
1. 自定义 View
ViewGroup 绘制顺序 (例:自定义 ViewGroup 如何实现 FlowLayout?如何实现 FlowLayout 调换顺序)
自定义 View 如何实现打桌球效果; 自定义 View 如何实现拉弓效果,贝瑟尔曲线原理实现?
- DecorView (FrameLayout)
- LinearLayout
- titlebar
- Content
- 调用 setContentView 设置的 View
- LinearLayout
ViewRoot 的 performTraversals 方法调用触发开始 View 的绘制,然后会依次调用:
- performMeasure:遍历 View 的 measure 测量尺寸
- performLayout:遍历 View 的 layout 确定位置
- performDraw:遍历 View 的 draw 绘制
https://juejinhttpshttps.cn/post/6991483318625632286/
2. 事件拦截分发
事件分发已经不是直接让你讲了,会给你具体的场景,比如 A 嵌套 B ,B 嵌套 C,从 C 中心按下,一下滑出到 A,事件分发的过程,这里面肯定会有 ACTION_CANCEL 的相关调用时机。
- 一个 MotionEvent 产生后,按 Activity -> Window -> decorView -> View 顺序传递,View 传递过程就是事件分发,主要依赖三个方法:
- dispatchTouchEvent:用于分发事件,只要接受到点击事件就会被调用,返回结果表示是否消耗了当前事件
- onInterceptTouchEvent:用于判断是否拦截事件,当 ViewGroup 确定要拦截事件后,该事件序列都不会再触发调用此 ViewGroup 的 onIntercept
- onTouchEvent:用于处理事件,返回结果表示是否处理了当前事件,未处理则传递给父容器处理
- 细节:
- 一个事件序列只能被一个 View 拦截且消耗
- 没有 onIntercept 方法,直接调用 onTouchEvent 处理
- OnTouchListener 优先级比 OnTouchEvent 高,onClickListener 优先级最低
- requestDisallowInterceptTouchEvent 可以屏蔽父容器 onIntercet 方法的调用
3. 解决过的一些性能问题,在项目中的实际运用。
4. 性能优化工具
(TraceView、Systrace、调试 GPU 过度绘制 & GPU 呈现模式分析、Hierarchy Viewer、MAT、Memory Monitor & Heap Viewer & Allocation Tracker 等)
检测Native内存泄漏工具 : mallocDebug 、leaktracer
5. 性能优化 (讲讲你自己项目中做过的性能优化)
网络:API 优化、流量优化、弱网优化
- 速度: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 监控流量
内存:OOM 处理、内存泄漏、内存检测、分析、Bitmap 优化 ,LeakCanary 原理,什么检测内存泄漏需要两次? 绘制
电量:WeakLock 机制、JobScheduler 机制
APK 瘦身 (APK 瘦身是怎么做的,只用 armabi-v7a 没有什么问题么? APK 瘦身这个基本是 100% 被面试问到,可能是我简历上提到的原因。)
内存抖动
内存泄漏
静态变量、单例强引跟生命周期相关的数据或资源,包括 EventBus 2.游标、IO 流等资源忘记主动释放 3.界面相关动画在界面销毁时及时暂停 4.内部类持有外部类引用导致的内存泄漏
- handler 内部类内存泄漏规避:1.使用静态内部类+弱引用 2.界面销毁时清空消息队列
- 检测:Android Studio Profiler
卡顿 {如何检测卡顿,卡顿原理是什么,怎么判断页面响应卡顿还是逻辑处理造成的卡顿} ,BlockCanary 的原理
- 减少布局层级及控件复杂度,避免过度绘制;约束布局
- 使用 include、merge、viewstub
- 优化绘制过程,避免在 Draw 中频繁创建对象、做耗时操作
- 布局优化、过度渲染处理、ANR 处理、监控、埋点、Crash 上传。
OOM 场景及规避
- 加载大图:减小图片
- 内存泄漏:规避内存泄漏
启动优化
冷启动优化
- 减少第一个界面onCreate()的工作量
- 不要在Application方法中以静态变量方式保存数据
- 不要在Application中参与业务操作
- 不要在Application进行耗时操作
- 减少布局的复杂性和深度
- 不要在主线程中加载资源
- 通过懒加载方式加载第三方SDK
6. Http[s]请求慢的解决办法(DNS、携带数据、直接访问 IP)
1、不通过DNS解析,直接访问IP (httpdns)
2、解决连接无法复用
http/1.0协议头里可以设置Connection:Keep-Alive或者Connection:Close,选择是否允许在一定时间内复用连接(时间可由服务器控制)。但是这对App端的请求成效不大,因为App端的请求比较分散且时间跨度相对较大。
方案1.基于tcp的长连接 (主要) 移动端建立一条自己的长链接通道,通道的实现是基于tcp协议。基于tcp的socket编程技术难度相对复杂很多,而且需要自己定制协议。但信息的上报和推送变得更及时,请求量爆发的时间点还能减轻服务器压力(避免频繁创建和销毁连接)
方案2.http long-polling 客户端在初始状态发送一个polling请求到服务器,服务器并不会马上返回业务数据,而是等待有新的业务数据产生的时候再返回,所以链接会一直被保持。一但结束当前连接,马上又会发送一个新的polling请求,如此反复,保证一个连接被保持。 存在问题: 1)增加了服务器的压力 2)网络环境复杂场景下,需要考虑怎么重建健康的连接通道 3)polling的方式稳定性不好 4)polling的response可能被中间代理cache住 ……
方案3.http streaming 和long-polling不同的是,streaming方式通过再server response的头部增加“Transfer Encoding:chuncked”来告诉客户端后续还有新的数据到来 存在问题: 1)有些代理服务器会等待服务器的response结束之后才将结果推送给请求客户端。streaming不会结束response 2)业务数据无法按照请求分割 ……
方案4.web socket 和传统的tcp socket相似,基于tcp协议,提供双向的数据通道。它的优势是提供了message的概念,比基于字节流的tcp socket使用更简单。技术较新,不是所有浏览器都提供了支持。
3、解决head of line blocking
它的原因是队列的第一个数据包(队头)受阻而导致整列数据包受阻
使用http pipelining,确保几乎在同一时间把request发向了服务器
7. 缓存自己如何实现(LRUCache 原理)
8. 图形图像相关:OpenGL ES 管线流程、EGL 的认识、Shader 相关
Canvas的底层机制,绘制框架,硬件加速是什么原理,canvas lock的缓冲区是怎么回事?
双指缩放拖动大图
TabLayout中如何让当前标签永远位于屏幕中间
TabLayout如何设置指示器的宽度包裹内容?
自定义View如何考虑机型适配?
- 合理使用warp_content,match_parent。
- 尽可能地使用RelativeLayout。
- 针对不同的机型,使用不同的布局文件放在对应的目录下,android会自动匹配。
- 尽量使用点9图片。
- 使用与密度无关的像素单位dp,sp。
- 引入android的百分比布局。
- 切图的时候切大分辨率的图,应用到布局当中,在小分辨率的手机上也会有很好的显示效果。
9. SurfaceView、TextureView、GLSurfaceView 区别及使用场景
TextureView :与SurfaceView一样继承View,它可以将内容流直接投影到View中,TextureView重载了draw()方法,其中主要SurfaceTexture中收到的图像数据作为纹理更新到对应的HardwareLayer中。
TextureView优点与缺点
- 优点:支持移动、旋转、缩放等动画,支持截图
- 缺点:必须在硬件加速的窗口中使用,占用内存比SurfaceView高,在5.0以前在主线程渲染,5.0以后有单独的渲染线程
SurfaceView : SurfaceView继承自类View,因此它本质上是一个View。但与普通View不同的是,它有自己的Surface,在WMS中有对应的WindowState,在SurfaceFlinger中有Layer。
调用者可以通过lockCanvas
获得了一块类型为Canvas的画布之后,就可以调用Canvas类所提供的绘图函数来绘制任意的UI了,例如,调用Canvas类的成员函数drawLine
、drawRect
和drawCircle
可以分别用来画直线、矩形和圆
SurfaceView优点与缺点
- 优点: 使用双缓冲机制,可以在一个独立的线程中进行绘制,不会影响主线程,播放视频时画面更流畅
- 缺点:Surface不在View hierachy中,它的显示也不受View的属性控制,SurfaceView 不能嵌套使用。在7.0版本之前不能进行平移,缩放等变换,也不能放在其它ViewGroup中,在7.0版本之后可以进行平移,缩放等变换。
GLSurfaceView :它加入了EGL的管理,并自带了渲染线程。另外它定义了用户需要实现的Render接口,只需要将实现了渲染函数的Renderer的实现类设置给GLSurfaceView即可。
GLSurfaceView也可以作为相机的预览,但是需要创建自己的SurfaceTexture并调用OpenGl API绘制出来。GLSurfaceView 本身自带EGL的管理,并有渲染线程,这对于一些需要多个EGLSurface的场景将不适用
https://zhooker.github.io/2018/03/24/SurfaceTexture%E7%9A%84%E5%8C%BA%E5%88%AB/
10. 动画、差值器、估值器(Android中的View动画和属性动画 - 简书、Android 动画 介绍与使用)
属性动画、补间动画、帧动画的区别和使用场景
View 动画:
- 作用对象是 View,可用 xml 定义,建议 xml 实现比较易读
- 支持四种效果:平移、缩放、旋转、透明度
帧动画:
- 通过 AnimationDrawable 实现,容易 OOM
属性动画:
- 可作用于任何对象,可用 xml 定义,Android 3 引入,建议代码实现比较灵活
- 包括 ObjectAnimator、ValuetAnimator、AnimatorSet
- 时间插值器:根据时间流逝的百分比计算当前属性改变的百分比
- 系统预置匀速、加速、减速等插值器
- 类型估值器:根据当前属性改变的百分比计算改变后的属性值
- 系统预置整型、浮点、色值等类型估值器
- 使用注意事项:
- 避免使用帧动画,容易OOM
- 界面销毁时停止动画,避免内存泄漏
- 开启硬件加速,提高动画流畅性 ,硬件加速:
- 将 cpu 一部分工作分担给 gpu ,使用 gpu 完成绘制工作
- 从工作分摊和绘制机制两个方面优化了绘制速度
11. MVC、MVP、MVVM
相互间的区别和各种使用场景,如何选择适合自己的开发架构。
- MVP:Model:处理数据;View:控制视图;Presenter:分离 Activity 和 Model
适用场景:工具类等业务流程繁琐的场景 - MVVM:Model:处理获取保存数据;View:控制视图;ViewModel:数据容器
- 使用 Jetpack 组件架构的 LiveData、ViewModel 便捷实现 MVVM
适用场景:电商、新闻等展示数据多的场景
12. Handler、ThreadLocal、AsyncTask、IntentService 原理及应用
Handler 机制原理,IdleHandler 什么时候调用。
- Handler:开发直接接触的类,内部持有 MessageQueue 和 Looper
- MessageQueue:消息队列,内部通过单链表存储消息
- Looper:内部持有 MessageQueue,循环查看是否有新消息,有就处理,没就阻塞
- 如何实现阻塞:通过 nativePollOnce 方法,基于 Linux epoll 事件管理机制
为什么主线程不会因为 Looper 阻塞:系统每 16ms 会发送一个刷新 UI 消息唤醒
13. Gradle(Groovy 语法、Gradle 插件开发基础)
提高 Android studio 的编译速度的几个方法
Google I/O 中有一个How to speed up your slow Gradle builds 的演讲,提出了一些加快Android studio编译速度的建议,整理如下:
1. 使用最新的Android gradle插件
Google tools team一直致力于加快Android studio的编译速度,因此最好使用最新的Android Gradle Plugin:
buildscript {
repositories {
google()
}
dependencies {
classpath ‘com.android.tools.build.gradle:3.0.0-alpha3’
}
}复制代码
2. 避免使用multidex
我们知道当方法书超过64k时,需要配置multidex,但是如果我们的工程minSdkVersion 设置为 20 或更低值,那么构建时间会大大增加,因为构建系统必须就哪些类必须包括在主 DEX 文件中以及哪些类可以包括在辅助 DEX 文件中作出复杂的决策。
这种情况下可以利用 productFlavors(一个开发定制和一个发布定制,具有不同的 minSdkVersion值)创建两个构建变型。
android {
defaultConfig {
...
multiDexEnabled true
}
productFlavors {
dev {
// Enable pre-dexing to produce an APK that can be tested on
// Android 5.0+ without the time-consuming DEX build processes.
minSdkVersion 21
}
prod {
// The actual minSdkVersion for the production version.
minSdkVersion 14
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:multidex:1.0.1'
}复制代码
3. 减少打包的资源文件
在开发模式下,可以只打包需要的资源,不必适配所有的资源文件:
productFlavors {
dev {
minSdkVersion 21
//only package english translations, and xxhdpi resources
resConfigs (“en”, “xxhdpi”)
}
}复制代码
4. 禁用 PNG 处理
PNG优化在默认情况下是打开的,我们可以在开发模式下禁用:
android {
if (project.hasProperty(‘devBuild’)){
aaptOptions.cruncherEnabled = false
}
}复制代码
5. 使用Instant run
Instant Run在android studio 3.0中有了很多的改进,可以尝试使用。
6. 不要随便修改配置
Gradle使用非常灵活,但是如果不正确的使用反而会降低编译速度。比如:
//this is BAD! 这种做法会导致每次编译 manifest文件都需要被修改,造成不必要的编译时间增加
def buildDateTime = new Date().format(‘yyMMddHHmm’).toInteger()
android {
defaultConfig {
versionCode buildDateTime
}
}复制代码
正确的做法是:
def buildDateTime = project.hasProperty(‘devBuild’) ? 100 : new Date().format(‘yyMMddHHmm’).toInteger()
android {
defaultConfig {
versionCode buildDateTime
}
}复制代码
7. 避免使用动态版本依赖
一般使用固定版本依赖即可。
8. 注意内存使用
要注意分配给Gradle的内存使用:
目前配置
org.gradle.jvmargs=-Xmx1536m复制代码
即可,不必再配置:
dexOptions {
javaMaxHeapSize = ‘4g’
}复制代码
9. 使用Gradle caching
在Gradle 3.5中,使用cache可以缓存并重复利用之前builds的生成的文件。
# Set this in gradle.properties
org.gradle.caching=true
14. 热修复、插件化
谈谈对 ClassLoader 的理解
双亲委托机制的好处:使得java类随着它的类加载器一起具备了一种带有优先级的层次关系,越是基础的类,越是被上层的类加载器进行加载,保证了java程序的稳定运行。
自定义 ClassLoader 插件化为什么会出现,如何代码加载,资源加载,代理 Hook)
15. 组件化架构思路
如何从一个老项目一步步实现组件化,主要问思路,考察架构能力和思考能力。(需要考虑很多,每一步做什么,顺序很重要)
组件化和模块化的理解与区别?
组件化开发的好处:
- 避免重复造轮子,可以节省开发和维护的成本。
- 可以通过组件和模块为业务基准合理地安排人力,提高开发效率。
- 不同的项目可以共用一个组件或模块,确保整体技术方案的统一性。
- 为未来插件化共用同一套底层模型做准备。
应用:ARouter:通过 APT 解析 @Route 等注解,结合 JavaPoet 生成路由表,即路由与 Activity 的映射关系
https://zhuanlan.zhihu.com/p/333887361
16. 系统打包流程
1.aapt 打包资源文件生成 R.java 文件;aidl 生成 java 文件
2.将 java 文件编译为 class 文件
3.将工程及第三方的 class 文件转换成 dex 文件
4.将 dex 文件、so、编译过的资源、原始资源等打包成 apk 文件
5.签名 (v1,v2,v3签名相关)
6.资源文件对齐,减少运行时内存
7.加壳加固防止反编译
17. Android 有哪些存储数据的方式。
数据库、sharedprefrence、文件、content provider、网络
18. SharedPrefrence 源码和问题点;
- apply没有返回值而commit返回boolean表明修改是否提交成功。
- apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内容,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
- apply方法不会提示任何失败的提示。 由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。
19. sqlite 相关
sqlite 升级,增加字段的语句
数据库框架对比和源码分析
数据库优化及数据迁移问题
getWritableDatabase 和 getReadableDatabase 的区别20. 如何判断一个 APP 在前台还是后台?
- Activitylifecycles
- AccessibilityService
-
21. Activity启动模式
standard 标准模式
- singleTop 栈顶复用模式,
- 推送点击消息界面
- singleTask 栈内复用模式,
- 首页
singleInstance 单例模式,单独位于一个任务栈中
进程优先级:1.前台进程 ;2.可见进程;3.服务进程;4.后台进程;5.空进程
- 进程被 kill 场景:1.切到后台内存不足时被杀;2.切到后台厂商省电机制杀死;3.用户主动清理
保活方式:
- 1.Activity 提权:挂一个 1像素 Activity 将进程优先级提高到前台进程
- 2.Service 提权:启动一个前台服务(API>18会有正在运行通知栏)
- 3.广播拉活
- 4.Service 拉活
- 5.JobScheduler 定时任务拉活
- 6.双进程拉活
进程保活原理:太极作者的博客、大神gityuan的分析
新的黑科技保活中通过 2 个机制来保证进程之间的互相拉起:
- 2 个进程通过互相监听文件锁的方式,来感知彼此的死亡。
- 通过 fork 产生子进程,fork 的进程同属一个进程组,一个被杀之后会触发另外一个进程被杀,从而被文件锁感知。
具体来说,创建 2 个进程 p1, p2,这两个进程通过文件锁互相关联,一个被杀之后拉起另外一个;同时 p1 经过 2 次 fork 产生孤儿进程 c1,p2 经过 2 次 fork 产生孤儿进程 c2,c1 和 c2 之间建立文件锁关联。这样假设 p1 被杀,那么 p2 会立马感知到,然后 p1 和 c1 同属一个进程组,p1 被杀会触发 c1 被杀,c1 死后 c2 立马感受到从而拉起 p1,因此这四个进程三三之间形成了铁三角,从而保证了存活率。
作者:weishu
链接:https://zhuanlan.zhihu.com/p/103126724
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
24. 屏幕适配
适配的最多的3个分辨率:1280_720,1920_1080,800480。
解决方案:
对于Android的屏幕适配,我认为可以从以下4个方面来做:
*1、布局组件适配
- 请务必使用密度无关像素 dp 或独立比例像素 sp 单位指定尺寸。
- 使用相对布局或线性布局,不要使用绝对布局
- 使用wrap_content、match_parent、权重
- 使用minWidth、minHeight、lines等属性
dimens使用:
不同的屏幕尺寸可以定义不同的数值,或者是不同的语言显示我们也可以定义不同的数值,因为翻译后的长度一般都不会跟中文的一致。此外,也可以使用百分比布局或者AndroidStudio2.2的新特性约束布局。
2、布局适配
使用限定符(屏幕密度限定符、尺寸限定符、最小宽度限定符、布局别名、屏幕方向限定符)根据屏幕的配置来加载相应的UI布局。
3、图片资源适配
使用自动拉伸图.9png图片格式使图片资源自适应屏幕尺寸。
普通图片和图标:
建议按照官方的密度类型进行切图即可,但一般我们只需xxhdpi或xxxhdpi的切图即可满足我们的需求;
4、代码适配:
在代码中使用Google提供的API对设备的屏幕宽度进行测量,然后按照需求进行设置。
5、接口配合:
本地加载图片前判断手机分辨率或像素密度,向服务器请求对应级别图片。
Android Framework
1. AMS、WMS
Window 、 WindowManager、WMS、SurfaceFlinger
- Window:抽象概念不是实际存在的,而是以 View 的形式存在,通过 PhoneWindow 实现
- WindowManager:外界访问 Window 的入口,内部与 WMS 交互是个 IPC 过程
- WMS:管理窗口 Surface 的布局和次序,作为系统级服务单独运行在一个进程
- SurfaceFlinger:将 WMS 维护的窗口按一定次序混合后显示到屏幕上
2. Activity 启动流程,App 启动流程
Android 系统启动流程
- 按电源键 -> 加载引导程序 BootLoader 到 RAM -> 执行 BootLoader 程序启动内核 -> 启动 init 进程 -> 启动 Zygote 和各种守护进程 ->
- 启动 System Server 服务进程开启 AMS、WMS 等 -> 启动 Launcher 应用进程
App 启动流程
总结:Launcher 中点击一个应用图标 -> 通过 AMS 查找应用进程,若不存在就通过 Zygote 进程 fork
详细:
1.点击App图标 Launcher进程发送startActivity IPC请求到AMS
2.AMS startProcessLocked
会去请求Zygote进程(socket方式)去创建进程
3.Zygote进程fork一个新的进程
4.被创建的app会执行ActivityThread的main方法
5.Service会往App发送BIND_APPLICATION,创建Application
6.接着会往App发送LAUNCH_ACTIVITY,反射创建Activity
3. Binder 机制(IPC、AIDL 的使用)
讲讲 Linux 上的 IPC 通信,Binder 有什么优势,Android 上有哪些多进程通信机制?
项目中遇见了什么多进程场景? AIDL 是什么?解决了什么问题? 谈谈对进程共享和线程安全的认知?
Binder
- Android 进程间通信的中流砥柱,基于客户端-服务端通信方式
- 使用 mmap 一次数据拷贝实现 IPC,传统 IPC:用户A空间->内核->用户B空间;mmap 将内核与用户B空间映射,实现直接从用户A空间->用户B空间
- BinderPool 可避免创建多 Service
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,适用于网络数据交换
4. 为什么使用 Parcelable,好处是什么?
Serializable、Parcelable
- Serializable :Java 序列化方式,适用于存储和网络传输,serialVersionUID 用于确定反序列化和类版本是否一致,不一致时反序列化回失败
Parcelable :Android 序列化方式,适用于组件通信数据传递,性能高,因为不像 Serializable 一样有大量反射操作,频繁 GC
5. Android 图像显示相关流程,Vsync 信号等
6. App 安装过程
首先要解压 APK,资源、so等放到应用目录
- Dalvik 会将 dex 处理成 ODEX ;ART 会将 dex 处理成 OAT;
- OAT 包含 dex 和安装时编译的机器码
Dalvik 和 ART
- Dalvik
- 谷歌设计专用于 Android 平台的 Java 虚拟机,可直接运行 .dex 文件,适合内存和处理速度有限的系统
- JVM 指令集是基于栈的;Dalvik 指令集是基于寄存器的,代码执行效率更优
- ART
- Dalvik 每次运行都要将字节码转换成机器码;ART 在应用安装时就会转换成机器码,执行速度更快
- ART 存储机器码占用空间更大,空间换时间
框架源码
1. Glide :加载、缓存、LRU 算法(如何自己设计一个大图加载框架)(LRUCache 原理)
创建隐藏fragment用于监控生命周期
1.图片缓存 - 内存缓存,硬盘缓存
2.线程切换 - 请求图片的时候放在子线程,设置Imageview的时候转换到主线程
3.OOM问题 - 弱引用,LRuCache
4.内存泄漏
缓存Resource使用三层缓存,包括
- 一级缓存:缓存被回收的资源,使用LRU算法(Least Frequently Used,最近最少使用算法)。当需要再次使用到被回收的资源,直接从内存返回。
- 二级缓存:使用弱引用缓存正在使用的资源。当系统执行gc操作时,会回收没有强引用的资源。使用弱引用缓存资源,既可以缓存正在使用的强引用资源,也不阻碍系统需要回收无引用资源。
- 三级缓存:磁盘缓存。网络图片下载成功后将以文件的形式缓存到磁盘中。
Bitmap缓存算法
在Glide中,使用BitmapPool来缓存Bitmap,使用的也是LRU算法。当需要使用Bitmap时,从Bitmap的池子中取出合适的Bitmap,若取不到合适的,则再新创建。当Bitmap使用完后,不直接调用Bitmap.recycler()回收,而是放入Bitmap的池子。
2. EventBus
3. LeakCanary
- 通过弱引用和引用队列监控对象是否被回收
比如 Activity 销毁时开始监控此对象,检测到未被回收则主动 gc ,然后继续监控
4. ARouter
重点:Router 原理,如何实现组件间的通信,组件化平级调用数据的方式。
5. 插件化(不同插件化机制原理与流派,优缺点。局限性)
插件化:动态加载主要解决3个技术问题:
1、使用ClassLoader加载类。
- 2、资源访问。
- 3、生命周期管理。
插件化是体现在功能拆分方面的,它将某个功能独立提取出来,独立开发,独立测试,再插入到主应用中。以此
来减少主应用的规模。
插件化的优势:
- 低耦合
- 应用间的接入和维护更便捷,每个应用团队只需要负责自己的那一部分。
- 应用及主dex的体积也会相应变小,间接地避免了65536限制。
- 第一次加载到内存的只有淘宝客户端,当使用到其它插件时才会加载相应插件到内存,以减少内存占用。
插件化框架对比:
- 最早的插件化框架:2012年大众点评的屠毅敏就推出了AndroidDynamicLoader框架。
- 目前主流的插件化方案有滴滴任玉刚的VirtualApk、360的DroidPlugin、RePlugin、Wequick的Small框架。
- 如果加载的插件不需要和宿主有任何耦合,也无须和宿主进行通信,比如加载第三方App,那么推荐使用RePlugin,其他情况推荐使用VirtualApk。由于VirtualApk在加载耦合插件方面是插件化框架的首选,具有普遍的适用性,因此有必要对它的源码进行了解。
插件的加载机制方案:
- 1、Hook ClassLoader。
- 2、委托给系统的ClassLoader帮忙加载。
6. 热修复
代码热修复原理:
- 将编译好的class文件拆分打包成两个dex,绕过dex方法数量的限制以及安装时的检查,在运行时再动态加载第二个dex文件中。
- 热修复是体现在bug修复方面的,它实现的是不需要重新发版和重新安装,就可以去修复已知的bug。
利用PathClassLoader和DexClassLoader去加载与bug类同名的类,替换掉bug类,进而达到修复bug的目的,原理是在app打包的时候阻止类打上CLASS_ISPREVERIFIED标志,然后在热修复的时候动态改变BaseDexClassLoader对象间接引用的dexElements,替换掉旧的类。
7. RXJava(RxJava 的线程切换原理)
异步,扩展观察者模式完成
- 通过handler进行主线程切换
- RxJava 详解 https://gank.io/post/560e15be2dca930e00da1083
RxJava 有四个基本概念:Observable (可观察者,即被观察者)、 Observer (观察者)、 subscribe (订阅)、事件。Observable 和 Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer。
8. Retrofit(Retrofit 在 OkHttp 上做了哪些封装?动态代理和静态代理的区别,是怎么实现的)
动态代理原理及实现
- InvocationHandler 接口,动态代理类需要实现这个接口
- Proxy.newProxyInstance,用于动态创建代理对象
- Retrofit 应用: Retrofit 通过动态代理,为我们定义的请求接口都生成一个动态代理对象,实现请求
9. OkHttp
1、OkHttp 源码,网络缓存
2、如果从网络加载一个 10M 的图片,说下注意事项
几个重要的组成部分:
1.RealCall:
2.Dispatcher:
3.OkHttpClient:
4.Interceptor:
拦截器设计,是OkHttp最重要的设计
音视频&FFmpeg&播放器
FFmpeg
基于命令方式实现了一个音视频编辑 App: https://github.com/yhaolpz/FFmpegCmd
集成编译了 AAC、MP3、H264 编码器
播放器原理
视频播放原理:(mp4、flv)-> 解封装 -> (mp3/aac、h264/h265)-> 解码 -> (pcm、yuv)-> 音视频同步 -> 渲染播放
音视频同步:
- 选择参考时钟源:音频时间戳、视频时间戳和外部时间三者选择一个作为参考时钟源(一般选择音频,因为人对音频更敏感,ijk 默认也是音频)
- 通过等待或丢帧将视频流与参考时钟源对齐,实现同步
IjkPlayer 原理
集成了 MediaPlayer、ExoPlayer 和 IjkPlayer 三种实现,其中 IjkPlayer 基于 FFmpeg 的 ffplay
音频输出方式:AudioTrack、OpenSL ES;视频输出方式:NativeWindow、OpenGL ES音频音效
人声分离、混音、降噪
算法与数据结构
1. 单链表:反转、插入、删除
2. 双链表:插入、删除
3. 手写常见排序、归并排序、堆排序
4. 二叉树前序、中序、后序遍历
5. 最大 K 问题
6. 广度、深度优先搜索算法
7. String 转 int。
核心算法就三行代码,不过临界条件很多,除了判空,还需要注意负数、Integer 的最大最小值边界等;
8. 如何判断一个单链表有环?
9. 链表翻转;
10. 快排;
11. 100 亿个单词,找出出现频率最高的单词。要求几种方案;
12. 链表每 k 位逆序;
13. 镜像二叉树;
14. 找出一个无序数组中出现超过一半次数的数字;
15. 计算二叉树的最大深度,要求非递归算法。
16. String 方式计算加法。
开放问题
1.项目开发中遇到的最大的一个难题和挑战,你是如何解决的。(95% 会问到)
做过比较难的是做k歌评分系统,评分算法的选择和调优。因为对乐理一窍不通,所以Google恶补相关乐理,
前期准备:你得有人做原声谱子,作为评分的标准答案。也叫原唱标注
然后App里:
1.录音进来、
2.傅立叶转频率、
3.频率转音高、
4.音高对比该时刻的标准(或者带升降key的)音高,
5.逐帧对比(50ms),
6.根据换气频率和难度系数对评分进行校正,将每句得分进行累积计总分
主要分析是建立在 pitch(音调), volume(音量), rhythm(节奏) 三个标准上的,
最主要的问题依我看来应该是如何获得大量准确的 vocal sample(原唱)作为参照标准,这个就需要大量的人力或者财力来解决了。
https://ieeexplore.ieee.org/document/5946974
2.说说你开发最大的优势点(95% 会问到)
3.你为什么会离开上家公司
4.你的缺点是什么?
5.你能给公司带来什么效益?
6.你对未来的职业规划?
7.英文简单介绍你自己
我是小小,具有xx年xx领域的经验,现在是xx公司xx职位。之前是有xx年xx行业经验,xx行业xx经验。我的专长是xx比如说品牌管理和业务发展。我也很擅长xx比如说项目管理和活动策划。我英语很好英语、计算机和xx,擅长xx, 在工作中,我曾经获得xx等奖项。我有优秀的沟通技能,有很强的团队精神和自我驱动力。我相信以我的经验和热情我能够在xx岗位上给xx公司的成功做出贡献。谢谢。
I am Xiao Xiao. I have over 8 years of experience in marketing. I am the team manager of marketing for HP since 2013. Previously I worked in IT for a year and retail for another 4 years or so. My specialties include brand management and business development. I’m also very experienced with project management and event coordination. I was voted the 2016 Best Employee among my peers. I could speak (pretty) good English and I’m fluent in Mandarin Chinese(Hahaha…因为我是中国人). I am highly proficient with Microsoft Office softwares and Photoshop. I have excellent written and verbal communication skills. I’m a team player and self-driven. I believe I have the combination of experience and enthusiasm to contribute to the success of Intel Shanghai as the Senior Manager of Marketing. Thanks.
结尾:That’s pretty much about myself.
I’s so excited to be here for this interview.
项目面试常见问题(★★★)
- 开发周期
- 项目中遇到的难题
- 项目中最大的收获
- 项目是如何上线的
- 项目是如何盈利的
- 绘制项目架构图
- 项目开发流程
- 你在项目中的角色
- 你负责项目中的哪些模块
- 讲讲你负责模块的具体实现
- 项目中都用到了哪些第三发框架
- 有没有自己写过框架
- 业余时间你是如何提高自己(学习)的
- 有没有自己的技术 blog
- 你的职业规划
- 为什么离职
- 为什么选择我们公司
- 说说你们项目的亮点和不足
- 你们的项目是如何保持风格一致的
- 项目架构是如何搭建的
- 屏幕适配是如何解决的
- 都看过哪些源码
- 项目版本是如何升级的
- 用的什么版本控制工具
- 你能独立开发吗
- App 跟服务器是如何交互的
- 需求文档写过吗
- 接口文档写过吗
- 云服务器都用过哪些
- 第三方平台都用过哪些
1.recylerview listview 缓存机制 区别
recylerview 四级缓存,保存viewholder,避免频繁findviewbyid,因为findviewbyid是深度搜索
listview二级缓存,getView
2.描述生命周期 框架怎么向上层传递处理
A打开B:简单 A.onPause -> B.onCrete -> B.onStart -> B.onResume -> A.onStop . Naive ! 这样回答只是及格,因为仅在 B Activity 的 launchMode 为 standard 或者 B Activity 没有可复用的实例时是这样的。
当 B Activity 的 launchMode 为 singleTop 且 B Activity 已经在栈顶时(一些特殊情况如通知栏点击、连点),此时只有 B 页面自己有生命周期变化: B.onPause -> B.onNewIntent -> B.onResume
当 B Activity 的 launchMode 为 singleInstance ,singleTask 且对应的 B Activity 有可复用的实例时,生命周期回调是这样的: A.onPause -> B.onNewIntent -> B.onRestart -> B.onStart -> B.onResume -> A.onStop -> ( 如果 A 被移出栈的话还有一个 A.onDestory)
3.hadler消息机制 链表 .next()轮训
4.view绘制过程
5.android ipc机制,系统分几层,socket在哪一层进行
6.ANR 出现场景, 如何架构设计减少anr
ANR 的四种场景:
- Service TimeOut: service 未在规定时间执行完成: 前台服务 20s,后台 200s
- BroadCastQueue TimeOut: 未在规定时间内未处理完广播:前台广播 10s 内, 后台 60s 内
- ContentProvider TimeOut: publish 在 10s 内没有完成
- Input Dispatching timeout: 5s 内未响应键盘输入、触摸屏幕等事件
教育公司:
1.热修复 classloader
2.启动过程
3.
相关链接 & 项目
优秀博客
- 安卓 offer 收割基 https://github.com/Blankj/AndroidOfferKiller
- AndroidUtilsCode作者 https://blankj.com/
- 玩安卓 https://www.wanandroid.com/
- 面试题汇总https://juejin.cn/post/6890407553457963022