Android工程师面试问题记载
主要记录一些在面试中未完全回答正确的问题。
技术面试官考虑的是:1.技术是否过关,进来是否会拖后腿,2.技术是不是太强,进来会不会影响我当前的岗位,3.开的工资有没有我高,会不会导致我心里不平衡
老板面试官考虑的是:1.这个人进来之后能不能干活,2.能干活但是开的工资性价比怎么样,3.这个人有没有其他特点好不好相处、管理
重点考虑的是:1.岗位是否真是急着缺人,2.你的技能与其岗位是否对口
Android工程师教你如何准备面试复习
Android 性能优化最佳实践
1 、布局优化
2、绘制优化
3、内存优化
4、启动速度优化
5、包体优化
6、耗电优化
7、ListView和 Bitmap优化
8、响应速度优化
9、线程优化
10、微优化(提高代码质量)
简析Window、Activity、DecorView以及ViewRoot之间的错综关系
常用设计模式
-
工厂模式和抽象工厂模式:注意他们的区别。
-
责任链模式:View的事件分发和OkHttp的调用过程都使用到了责任链模式。
-
观察者模式:重要性不言而喻。(广播、Android事件监听等等)
-
代理模式:建议了解一下动态代理
-
单例模式:考的少但是很常用
-
模板模式:定义算法结构顺序
-
装饰者模式(java的流处理使用的就是装饰者模式)
软件设计原则?
- 单一职责原则:一个类只专注于做一件事; 高内聚,低耦合;
- 开放-封闭原则:对拓展开放,对修改关闭(尽可能不动原有代码进行拓展);
- 里氏代换原则:子类必须能够替换它们的基类型,基类与子类可互换,客户端没有察觉情况下;
- 依赖倒置原则:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
- 接口隔离原则:使用多个专一功能的接口比使用一个总接口总要好,但不能过渡;一个接口不能过于臃肿;
- 迪米特法则:对象与对象之间应该使用尽可能少的方法来关联,避免千丝万缕的关系; 低耦合; 类知道其他类应尽量少;
示例
http 和 https 的区别
内存泄露检测,内存性能优化 ?
内存性能优化
- 优先使用Parcelable序列化传输数据而不是使用Serializable序列化
- 优化布局,减少嵌套,慎用weight属性
- 节制地使用Service:系统会倾向于将这个Service所依赖的进程进行保留,这样就会导致这个进程变得非常消耗内存
- 当界面不可见时释放内:在Activity中重写onTrimMemory()方法,监听TRIM_MEMORY_UI_HIDDEN
- 当内存紧张时释放内存:onTrimMemory()方法
- 避免在Bitmap上浪费内存:有效压缩、合理配置分辨率、使用bitmapFactory.decode方法加载bitmap。即Jni调用c底层加载优化速度
- 谨慎使用抽象编程:抽象的编程方法需要编写额外的代码,虽然这些代码根本执行不到,但是却也要映射到内存当中,不仅占用了更多的内存,在执行效率方面也会有所降低
- 尽量避免使用依赖注入框架:这些框架为了要搜寻代码中的注解,通常都需要经历较长的初始化过程。并且还可能将一些你用不到的对象也一并加载到内存当中
- 使用ProGuard简化代码:除了混淆之外,它还具有压缩和优化代码的功能,ProGuard会对我们的代码进行检索,删除一些无用的代码,并且会对类、字段、方法等进行重命名
- 使用多个进程:如音乐播放器。Service配置android:processs属性为:background就可以了
内存泄露检测
- LeakCanary
- 追踪内存分配
- 查询方法执行的时间
通常判断方法是:如果方法调用次数不多,但每次调用却需要花费很长的时间的函数,可能会有问题。 如果自身占用时间不长,但调用却非常频繁的函数也可能会有问题。
Jave中的引用分为3种:
- 强引用:引用为空的时候,Java的垃圾回收器会处理。一般来说自己写的程序大部分都是强引用。
- 软引用:如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存
- 弱引用:是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。
- 虚引用:虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。
OOM与ANR的区别?
OOM:就是当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出。
主要是因为使用过多的内存泄漏造成的OOM。
主要原因可能有:
- 内部类对Activity的引用(未及时释放)
- 大量Bitmap使用(未及时回收)
- 游标cursor的使用(未关闭)
- 在onDraw方法里面执行大量对象的创建
- 不恰当的使用static关键字(堆占用内存过多)
- 滥用多线程
OOM:是当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制;
内存抖动:是短时间内大量的对象被创建,然后又被马上释放;
内存泄漏:指无用对象持续占有内存或得不到及时释放,从而造成的内存空间的浪费,过多的内存泄漏会造成OOM.
ANR:Application not response,应用程序无响应。(android中Activity的最长执行时间是5秒,BroadcastReceiver的最长执行时间则是10秒)
布局优化?
- 减少嵌套布局
- 慎用weight属性
- 重用布局文件
- include
- merge
- 仅在需要时才加载布局ViewStub
Picasso、Okhttp、Retrofit、Rxjava源码的理解?
加锁机制的使用有哪些?
-
Lock : 明锁,用于处理synchronized解决不了的特殊问题下使用
-
synchronized : 同步锁,java有优化一般情况下推荐使用,锁少的情况下效率较高。(类锁、方法锁、对象锁)
-
Atomic :
AtomicBoolean:以原子更新的方式更新boolean; AtomicInteger:以原子更新的方式更新Integer; AtomicLong:以原子更新的方式更新Long; AtomicIntegerArray:原子更新整型数组中的元素; AtomicLongArray:原子更新长整型数组中的元素; AtomicReferenceArray:原子更新引用类型数组中的元素 AtomicReference:原子更新引用类型; AtomicReferenceFieldUpdater:原子更新引用类型里的字段; AtomicMarkableReference:原子更新带有标记位的引用类型; AtomicIntegeFieldUpdater:原子更新整型字段类; AtomicLongFieldUpdater:原子更新长整型字段类; AtomicStampedReference:原子更新引用类型,这种更新方式会带有版本号。
-
volatile修饰的变量:
多线程、加锁多线程的使用?
Executor执行器的使用多线程。
newCacheThreadPool:将为每一个任务创建一个线程,但如果已有线程是空闲的会重用已有线程(默认情况下推荐使用)
newFixedThreadPool:使用有限线程集执行所提交的任务,可控制线程最大并发数,超出的线程会在队列中等待,不会滥用可获得的资源。(资源紧张下使用)
newSingleThreadExecutor:一个单线程化的线程池,它只会用唯一的工作线程来执行任务(用于文件日志)
ForkJoinPool:优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行。 缺点: 在执行过程中,会创建大量的子任务,导致GC进行垃圾回收,这些是需要注意的
多线程间通信的几种模式?
- 同步(Lock、synchronized、Atomic):这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信。(通过判断这个“共享的条件变量“是否改变了,来实现进程间的交流)
- while轮询的方式(通过判断这个“共享的条件变量“是否改变了,来实现进程间的交流)
- wait/notify机制。(通知过早,会打乱程序的执行逻辑。)
- 管道通信:通过管道,将一个线程中的消息发送给另一个。
java线程安全的容器有哪些?
-
同步容器过时类:使用了synchronized
1.Vector、Stack(Vector子类、先进后出) 2.HashTable
-
并发容器:
3.ConcurrentHashMap:分段 4.CopyOnWriteArrayList:写时复制 5.CopyOnWriteArraySet:写时复制
-
Queue:
6.ConcurrentLinkedQueue:是使用非阻塞的方式实现的基于链接节点的无界的线程安全队列,性能非常好。 (java.util.concurrent.BlockingQueue 接口代表了线程安全的队列。) 7.ArrayBlockingQueue:基于数组的有界阻塞队列 8.LinkedBlockingQueue:基于链表的有界阻塞队列。 9.PriorityBlockingQueue:支持优先级的无界阻塞队列,即该阻塞队列中的元素可自动排序。默认情况下,元素采取自然升序排列 10.DelayQueue:一种延时获取元素的无界阻塞队列。 11.SynchronousQueue:不存储元素的阻塞队列。每个put操作必须等待一个take操作,否则不能继续添加元素。内部其实没有任何一个元素,容量是0
-
Deque:
(Deque接口定义了双向队列。双向队列允许在队列头和尾部进行入队出队操作。) 12.ArrayDeque:基于数组的双向非阻塞队列。 13.LinkedBlockingDeque:基于链表的双向阻塞队列。
-
Sorted容器:
14.ConcurrentSkipListMap:是TreeMap的线程安全版本 15.ConcurrentSkipListSet:是TreeSet的线程安全版本
google混合App开源框架fluter的使用?
view事件分发拦截机制?
自定义view?
-
集成View,重写onDraw()方法
-
集成现有空间如TextVeiw
-
组合View
onMeasure()会在初始化之后调用一到多次来测量控件或其中的子控件的宽高; onLayout()会在onMeasure()方法之后被调用一次,将控件或其子控件进行布局; onDraw()会在onLayout()方法之后调用一次,也会在用户手指触摸屏幕时被调用多次,来绘制控件。
Android 动画?
补间动画、帧动画
Handler源码的理解?
– 功能
——执行计划任务
——线程间通信
——-确保操作始终在某个特定的线程中执行
– 结论
——1在同一线程可以创建不同的handler
——2handler不能直接创建在没有Looper.prapare这种函数的线程中
——3通过对handler指定Looper才能在不同的线程中运行
——4 一旦message被开始处理,removeMessage是无效的
跨进程通信的几种模式?
- Activity:需要指定的是要访问的Activity所对应的Action,用在跳转拨号界面等
- Service:通过AIDL服务,通过binder机制
- Broadcast: 广播是一种被动跨进程通讯的方式
- ContentProvider:通过binder机制,提供了一种在多个应用程序之间数据共享的方式
ContentProver共享数据机制?
MVC与MVP的优缺点?
MVC有助于管理复杂的应用程序,可以让开发人员单独进行某一层的开发。
优点:
1.耦合性低:M、V、C的分离所以很容易改变应用程序的数据层和业务规则
2.重用性高:允许不同样式的视图共享一个模型
3.生命周期成本低:是开发和维护用户接口的技术含量降低
4.部署快:他可以让一个开发人员专注于业务逻辑的开发,而另一个开发人员专注于界面的开发。
5.维护性高:因为M、V、C层的分离所以更易于维护和修改
缺点:
1.不适合小型和中型规模的应用程序
2.视图和控制器之间过于紧密的连接
MVP:MVP是从经典的模式MVC演变而来
特点:
MVP和MVC最大的区别就是:在MVP中View并不会直接使用Model,他们的之间的所有通信都是通过Presenter层进行,所有的交互都发生的presenter内部,在MVC中View会直接从Model中读取数据而不是通过Controller.
在MVC里,View是可以直接访问Model的,从而View里会包含Model信息,不可避免的还要包括一些业务逻辑。导致更改View也是比较困难的。
优点:
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)
缺点:
由于对View的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。一旦View需要变更,那么Presenter也需要变更了。
后台进程保活?
黑色保活:不同的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒)
白色保活:系统应用白名单(如QQ、微信)、可见的前台服务。用在音乐播放器。
灰色保活:利用系统漏洞。
Activity的启动模式及应用?
- standard:不管有没有已存在的实例,都生成新的实例。
- singleTop:否有一个MainActivity实例正位于栈顶,如果有则不再生成新的,而是直接使用。
- singleTask:如果发现有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈。用在主Activity
- singleInstance:它会启用一个新的栈结构,将Activity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。 用在全局拨号界面
String,Stringbuffer,Stringbuilder 区别?
String:字符串常量,字符串长度不可变。Java中String是immutable(不可变)的。
StringBuffer:字符串变量(Synchronized,即线程安全)。如果要频繁对字符串内容进行修改,出于效率考虑最好使用StringBuffer,如果想转成String类型,可以调用StringBuffer的toString()方法。可将字符串缓冲区安全地用于多个线程
StringBuilder:字符串变量(非线程安全)。在内部,StringBuilder对象被当作是一个包含字符序列的变长数组。用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)
java中Serializable与Parcelable的使用
-
Serializable序列化的空接口,这个序列化很简单,类直接实现就可以了,记住要添加或者保留序列化版本号serialVersionUID,否则可能会造成反序列化失败.
-
Parcelable接口,android特有的序列化存储,传输效率高,需要实现里面的抽象方式,实现起来比较麻烦.
Java 容器(list, set, map)
-
list: 有序排列
ArrayList 内部实现是用数组, 随机访问速度快, 删除或插入元素速度慢。 LinkedList 内部实现是用链表, 随机访问速度慢,删除和插入元素相对较快。 Queue(先进先出) 各种Queue以及栈的行为,有LinkedList提供支持 Vector、Stack(先进后出) 是过去遗留下来的类,目的只是为了支持老程序,应该在编写程序时尽量避免使用它们。
-
Set: 默认无序排列、不接受重复元素
HashSet: 提供最快的查询速度。为快速查找而设计,可以认为是基于哈希表的实现。 存入HashSet的元素必须定义hashCode(). TreeSet:保持元素处于排序状态。保持次序的Set,底层为树结构(红黑树)。使用它可以从Set提取有序的序列。元素必须实现Comparable接口或者在构造TreeSet时传入Comparator参数。 LinkedHashSet: 具有HashSet的查询速度且内部使用链表维护元素的顺序(按插入的顺序或最近最少使用顺序)
-
Map: 默认无序排列,键值对存储机制,Map的key不允许重复。如果是线程安全容器key-value都不可为null
HashMap 基于散列表的实现(它取代了HashTable, HashTable是过时的类)。key、value可以为null TreeMap 基于红黑树的实现。查看key 或 key-value是,它们会被排序(次序由Comparator或Comparable决定),key不可为null,value可以为null LinkedHashMap 类似与HashMap, 但是迭代遍历时,取得“键值对”的顺序是其插入的顺序,或最近最少使用的顺序(如果在构造LiskedHashMap传入参数accessOrder=true)。比HashMap慢一点,但是在迭代访问时更快,因为它使用链表维护内部的次序。
-
SAX(Simple API XML)
SAX(Simple API for XML)解析器是一种基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档的结束,不可暂停或倒退。 优点:解析速度快,占用内存少。非常适合在Android移动设备中使用。 缺点:不会记录标签的关系,而要让你的应用程序自己处理,这样就增加了你程序的负担。 工作原理:对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档 (document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
-
DOM(Document Object Model)
DOM,即对象文档模型,它是将整个XML文档载入内存(所以效率较低,不推荐使用),每一个节点当做一个对象,结合代码分析。 优点:由于DOM在内存中以树形结构存放,因此检索和更新效率会更高 缺点:但是对于特别大的文档,解析和加载整个文档将会很耗资源。 当然,如果XML文件的内容比较小,采用DOM是可行的。 工作原理:使用DOM对XML文件进行操作时,首先要解析文件,将文件分为独立的元素、属性和注释等,然后以节点树的形式在内存中对XML文件进行表示,就可以通过节点树访问文档的内容,并根据需要修改文档。
-
Pull
PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。 优点: PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器,Android官方推荐开发者们使用Pull解析技术
总结:三种方式的总结比较
DOM方式最直观和容易理解,但是只适合XML文档较小的时候使用,而SAX方式更适合在移动终端系统中使用,因为相比DOM占用内存少,适合处理比较大的XML文档,最后的Pull方式使用场合和SAX类似,但是更适合需要提前结束XML文档解析的场合。
手写冒泡算法
public static void BubbleSort(int[] arr) {
int temp;//临时变量
for (int i = 0; i < arr.length - 1; i++) { //表示趟数,一共arr.length-1次。
for (int j = arr.length - 1; j > i; j--) {
if (arr[j] < arr[j - 1]) {
temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
}