比较高级的Android面试题

写在前面:别人面试遇到的问题,没给答案,尝试写回答,查缺补漏。原博地址

天猫

一面

1. Retrofit的实现与原理
2. 应用详细启动过程,设计的进程,fork新进程(Linux)

3. HashMap原理,Hash冲突,并发集合

  • HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组,Entry就是数组中的元素,每一个Map.Entry其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表。
  • 当需要存储一个Entry对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组中链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组的存储位置,在根据equals方法从该位置的链表中取出该Entry。
  • Hash冲突:HashMap执行put方法添加元素时,会哈希key的hashcode值来解决该元素的存储位置。判断当前确定的索引位置时候存在相同hashCode和相同的key的元素,如果存在相同的hashcode和相同的key的元素,那么新值将覆盖原来的旧值,并返回旧值。如果存在相同的hashcode,那么他们确定的索引位置就相同,这是判断他们的key是否相同,如果不同,这时候就会产生hash冲突。HashMap解决散列值冲突问题采用的是链表法,也就是hash冲突后,HashMap的单个bucket里存储的不是一个Entry,而是一个Entry链。调用get()方法,系统只能必须按顺序每一个Entry,直到找到想要搜索的Entry位置,最早放入bucket的Entry放在Entry链的末端。
  • ConcurrentHashMap。
    1. ConcurrentHashMap主要解决HashTable,在多线程环境下读写操作效率低下的问题,因为HashTable使用synchronized来锁住整张Hash表来实现线程安全,即每次操作锁住整张表让一个线程独占。
    2. ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行修改,其内部使用了Segment来表示不同的部分,每一个部分其实就是一个小的hashtabl,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就能并发进行。
    3. ConcurrentHashMap中主要有三个实体类:ConcurrentHashMap,整个hash表;Segment,段;HashEntry,节点。Segment继承了ReentrantLock,表明每一个segment都可以当做一个锁,ReentrantLock是synchronized的替代者。这样对每一个segment中的数据需要同步操作的话都是使用每一个segment容器对象自身锁来实现。只是对size()、containsValue()等全局操作可能要锁定所有的segemnt。
    4. Segment下面包含很多个HashEntry列表数组。对于存储的每一个key,需要经过三次hash操作,第二次hash确定该元素放在哪一个segment。第三次hash来确定该元素放在哪一个HashEntry中。
    5. ConcurrentHashMap的构造函数,一般传入三个参数:initialCapacity,表示新创建hash表的初始容量,默认是16;loadFactor表示负载因子,当ConcurrentHashMap中的元素个数大于loadFactor * initialCapacity时,就会rehash扩容。其默认值是0.75;concurrentLevel表示并发级别,用来确定Segment的个数,Segment的个数是大于等于concurrentLevel的第一个2的n次方的数。

4. Android两种虚拟机区别与联系

  • Android从5.0开始默认使用ART虚拟机执行程序,抛弃使用Dalvik虚拟机。因为肯定是前者比后者提高了Android的运行效率,提高了系统的流畅性。
  • Dalvik虚拟机执行的是dex字节码,ART虚拟机执行的本地机器码。Dalvik虚拟机有一个解释器,用来执行dex字节码,Android从2.2开始,通过JIT(Just In Time)进行Dalvik虚拟机的优化,将使用频率较高的字节码翻译成机器码,就可以提高Dalvik虚拟机的知心效率。因为Dalvik虚拟机翻译工作是在程序运行时的,而且ART在APK安装时通过AOT预编译技术,对其包含的Dex字节码进行翻译,得到对应的本地机器码,Android在运行时就可以执行了。
    5. GC机制
  • GC机制其实就是垃圾回收,当一个对象不再被程序引用时,它所占的堆空间就可以回收,以便分配给新对象使用
  • GC机制主要是了解三个方面:1. JVM是怎样分配内存;2. JVM如何哪些内存是垃圾;3. 怎样回收这些垃圾内存
  • JVM的内存模型:JVM将其管辖的内存大致分为三部分:方法区、Java栈和Java堆。
    1. 方法区是静态分配的,编译器将变量绑定在某个存储位置上,而且这些绑定不会再运行时改变。常数池、static变量和String常量保存在方法区
    2. Jav Stack是一个逻辑概念,特点是后进先出。一个栈空间可能是连续,也可能是不连续的。最典型的的栈应用是方法的调用,java虚拟机没调用一次方法就创建一个方法帧,退出该方法则对应的帧被弹出。栈中储存的数据也是在运行时确定的。
    3. Java堆分配,是随意顺序,在运行时进行储存空间分配和回收内存管理。堆中储存的数据常常是大小、数量和生命周期在编译器是无法确定的。java对象的内存总在heap中分配,垃圾回收器在这里工作回收无用对象。
  • 确定无用对象。常用的搜索算法有:
    1. 引用计数器算法,如果有地方引用一个对象,该对象计数器+1,引用失效-1;当计数器为0的时候,jvm就认为该对象不再被引用,可以当做垃圾回收了。该算法有一个缺点,就是不能解决循环引用的问题,所以不再被使用
    2. 根搜索法,该算法是通过一些GC roots对象为起点,从这个节点出发开始往下搜索,搜索通过路径成为引用链,当一个对象不再被GC roots的引用链链接的时候,说明这个对象不再使用了,可以回收。
  • 当通过某种算法确定无用对象之后,JVM就可以回收垃圾了。常用的回收算法有:
    1. 标记-清除法。该算法分成两个阶段:标记阶段、清除阶段。在标记阶段,确定所要回收的对象,并标记;清除阶段紧跟标记阶段,将标记的无用对象进行清除处理。该算法效率不高,清除后会产生大量不连续的空间,当需要分配大对象时,可能无法找到足够连续大空间使用。
    2. 复制算法。该算法将内存分成两块。每次使用其中的一块,当垃圾回收时,将存活的对象复制到另一块上,然后清除这一块的对象。该算法实现简单,运用效率高。如果内存块分配合理,比如8:1,则内存的利用率很高。JVM用复制算法收集新生代的对象。
    3. 标记-整理算法。标记整理算法和标记清除算法类似。只是标记整理算法把存活的对象往内存的一端移动,然后直接回收边界以外的内存。jvm用该算法收集存活时间比较长的老年代。
    4. 分代算法。根据对象的存活时间把内存分为新生代和老年代。然后在各个代采用不同的垃圾回收算法。新生代采用复制算法,老年代采用标记整理算法。

6. Activity的onNewIntent

  • 该方法被启动模式设置为“singleTop、singleTask”的Activity回调,或者当通过设置Intent.FLAG_ACTIVITY_SINGLE_TOP、Intent.FLAG_ACTIVITY_SINGLE_TASK的Intent启动Activity时被回调。也就是说,只要栈顶Activity被重新启动时没有重建一个新的Activity实例,而是依然使用栈顶的Activity实例,那么onNewIntent方法就会被回调。
  • 如果我们在Activity中调用了getIntent方法,那么返回的Intent对象还是老的Intent,但是如果想让getIntent()返回最新的Intent,那么我们可以通过setIntent()方法设置。

7. View的绘制原理
8. 类的加载机制

**9. requestLayout,invalidate,postInvalidate区别与联系

  • 共同点:三个方法都可以刷新view
  • 区别:View绘制分为三个步骤:onMeasure、onLayout、onDraw。当View的内容发生改变而大小、位置不变时,只需调用invalidate或者postInvalidate方法,会执行View的onDraw()方法;当View的大小、位置发生改变而显示内容不变时,调用requestLayout方法,会执行View的onMeasure、onLayout方法。invalidate和postInvalidate的区别是:前者只能用在UI线程,后者在非UI线程使用。
    二面

项目架构
项目详细优化
项目难点与亮点
Glide缓存源码,加载原理
网络优化,服务端与客户端两方面

三面

1. ActivityThread工作原理
2. 设计模式与实际应用
3. adb常用命令行

  • adb,即Android Debug Bridge,可以用来管理或者手机模拟器的状态。
  • 查看当前连接的设别
    1. adb devices
  • 安装和卸载apk程序
    1. 安装apk:adb install apk_name
    2. 卸载apk:adb uninstall
  • 上传和下载文件
    1. 上传文件:adb push <本地路径> <远程路径>
    2. 下载文件:adb pull <远程路径>
  • 启动adb命令行
    1. adb shell
  • 截屏并下载到电脑
    1. adb exec-out screencap -p> xxx.png
      4. Android消息机制

**5. 多线程同步

  • 为什么要同步?当多个线程同时操作一个可共享的变量时,将会导致数据不准确,相互之间产生冲突,因为加入同步锁是为了避免在该线程没有完成操作之前,不被其他线程调用,从而保证了该共享变量的唯一性和准确性。
  • 有7种方式实现多线程同步
    1. 同步方法。
      • 用synchronized关键字修饰的方法,由于java每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。注意,当用synchronized修饰静态方法时,将会锁住整个类。
    2. 同步代码块
      • 用synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动加上内置锁,从而实现同步。同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块可以减小开销。
    3. 使用特殊变量volatile实现线程同步。
      • valatile关键字为域变量的访问提供了一种免锁机制。
      • 使用volatile修饰域相当于告诉该域虚拟机可能被其他线程更新。
      • 因此每次使用该域就要重新计算,而不是使用寄存器中的值
      • volatile不会提供任何原子操作,它也不能用来修饰final类型的变量。
    4. 使用重入锁实现线程同步
      • ReentrantLock类是可重入、互斥实现了Lock接口的锁。
      • 使用重入锁,注意及时释放锁,否则会出现死锁,通常在finally代码块释放锁
    5. 使用原子变量实现线程同步
      • 多线程需要同步的根本原因在于对普通变量的操作不是原子的,原子操作就是指读取变量值,修改变量值、保存变量值看成一个整体来操作。
      • java的util.concurrnt,atomic包中提供了创建原子类型变量的工具,使用该包的类可以简化线程同步.
        6. GC机制
        7. 内存泄漏与分析
        8. 博客与GitHub
        9. Android P新特性

四面

微信小程序实现原理
Java反射
Binder机制,共享内存实现原理
Android动画
混合开发
笔试(5道题,两小时)

五面

业务需求分析与建议
黑盒排序算法

51信用卡

一面

项目介绍
Retrofit的实现与原理,封装
Java泛型
设计模式与实际应用
RecyclerView与ListView(缓存原理,区别联系,优缺点)
ButterKnife实现原理
EventBus实现原理
AOP与APT
RxJava
自定义LayoutManager
嵌套滑动实现原理
混合开发
微信小程序

二面

View的绘制原理
第三方框架选择标准
项目难点与亮点
插件化,热修复
项目优化

美术宝

笔试

具体场景分析Activity的生命周期
Android消息机制
Bitmap内存计算,如何压缩
SurfaceView与View
Application生命周期
Http与Https
手写单例模式
手写算法代码

面试

EventBus实现原理
自定义View里,onDraw详细优化
SurfaceView替换方案
高清加载巨图且支持手势缩放的实现方案
算法题目其他思路

贝贝

一面

项目介绍
架构的搭建与原理
MVC,MVP,MVVM模式理解与使用
微信小程序实现原理
ArrayList与LinkList区别与联系
HashMap源码,JDK1.8前后详细区别,负载因子,Fail-Fast机制
线程安全的集合及各自实现原理
Synchronized原理
Volatile实现原理
详细描述应用从点击桌面图标到首页Activity展示的流程(应用启动流程,Activity的Window创建过程)
Glide加载原理
组件化

二面

Fragment的懒加载实现,参数传递与保存
ViewPager的缓存实现
Android消息机制,post与postDelay
ActivityThread工作原理
子线程访问UI的验证与后果
主线程Looper.loop为什么不会造成死循环
Android里的多线程与实现原理
热修复实现方案
EventBus实现原理
Android内存优化与分析
类加载机制,加载过程
组件化实现方案,路由原理
单链表添加具体实现

三面

进程与线程
项目详细优化
设计模式与实际应用
ListView与RecyclerView的对比
自定义View的优化
具体场景分析高并发访问服务器的解决方案

小影

项目介绍,整体框架搭建
Glide加载原理,缓存方案,LRU算法
Glide加载长图,图片背景变色
ArrayList与LinkList区别与联系
RecyclerView缓存原理,局部刷新原理
Android事件分发机制
结合具体场景处理滑动冲突事件
Android消息机制
ActivityThread工作原理

二面

EventBus实现原理
View的绘制原理
热修复实现原理,解决方案
组件化具体实现
项目详细优化
Android权限管理
编码风格与规范
项目管理

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 142,825评论 18 610
  • 无题 是秋了 死亡和睡眠已在后台 血管上的蚊子垂死挣扎 我的血液里也有秋天 催促着凋零 我在这临产的大地上寻觅 被...
    颜族长阅读 35评论 -1 6
  • 目录 上一章 喝醉酒的客人(6) 01 瞧着小李这神情,自是明白其中的厉害关系,不免后退了几步,找些安全感来。...
    正晓孩阅读 85评论 0 7
  • 有人问:结婚最重要的条件是什么? 现在这社会,不必为了父母之命媒妁之言而结婚。 但仍有一部分人,遭到父母逼迫相亲、...
    木子默06阅读 188评论 0 0
  • 本书由陕西中智文化策划发行,由新华出版社出版,翻开的这本小册子,作者:赵澜波,定价:9.90元,出版时间:2016...
    刘糖糖_f3e3阅读 563评论 0 0