2019 初级Android开发工程师面经

1. Java基础

1.1 什么是乐观锁?

  • 乐观锁:假设每次去拿数据都认为别人不会修改,因此不会上锁.可是在更新的时候会判断一下此期间别人有没有去更新这个数据. 通常用在读比较多,写比较少的状况.
  • 悲观锁:假设每次都是最坏状况,每次去拿数据时别人都会修改,因此每次拿数据的时候都会上锁,这样别人想拿这个数据就会被阻塞直到它拿到锁. 多写少读时使用.

1.2 volatile关键字

  1. 保证可见性,不保证原子性
  2. 禁止指令重排序
  3. 不缓存,每次都是从主存中取

1.3 hashmap 原理,红黑树是什么?

  • 1.7 数组+链表,链表过长时,会致使查询效率退化
  • 1.8 数组+链表+红黑树,当链表长度大于8转为红黑树
  • HashMap 的默认初始大小为 16,初始化大小必须为 2 的幂,最大大小为 2 的 30 次方。数组中存储的链表节点 Entry 类实现于 Map.Entry 接口,它实现了对节点的通用操做。HashMap 的阈值默认为 “容量 * 0.75f”,当存储节点数量超过该值,则对 map 进行扩容处理。
  • 线程不安全的容器,解决并发问题使用ConcurrentHashMap(高效)或者是Collections.synchronizedMap().Collections.synchronizedMap()其实就是每一个方法加一个synchronize,其实和HashTable 差很少.

红黑树android

  • 平衡的二叉查找树
  • 节点是红色或者是黑色
  • 根节点是黑色
  • 每一个叶子的节点都是黑色的空节点(NULL)
  • 每一个红色节点的两个子节点都是黑色的
  • 从任意节点到其每一个叶子的全部路径都包含相同的黑色节点
  • 插入时会涉及到变色和旋转

1.4 jvm内存分配

Java虚拟机书中第二章面试

  • 程序计数器
  • Java虚拟机栈
  • 本地方法栈
  • Java堆
  • 方法区
  • 运行时常量池
  • 直接内存

1.5 String,StringBuffer,StringBuilder 区别

  • String,StringBuffer,StringBuilder最终底层存储与操做的都是char数组.可是String里面的char数组是final的,而StringBuffer,StringBuilder不是,也就是说,String是不可变的,想要新的字符串只能从新生成String.而StringBuffer和StringBuilder只须要修改底层的char数组就行.相对来讲,开销要小不少.
  • String的大多数方法都是从新new一个新String对象返回,频繁从新生成容易生成不少垃圾.
  • StringBuffer是线程安全的,StringBuilder是线程不安全的.由于StringBuffer的方法是加了synchronized锁起来了的,而StringBuilder没有.
  • 增删比较多时用StringBuffer或StringBuilder(注意单线程与多线程)。实际状况按需而取吧,既然已经知道了里面的原理。

2. 安卓基础

2.1 安卓各版本大变化(Android 6.0到10.0有哪些大点变化),兼容适配

Android 5.0数据库

  • Material Design
  • ART虚拟机

Android 6.0api

  • 应用权限管理
  • 官方指纹支持
  • Doze电量管理
  • 运行时权限机制->须要动态申请权限

Android 7.0数组

  • 多窗口模式
  • 支持Java 8语言平台
  • 须要使用FileProvider访问照片
  • 安装apk须要兼容

Android 8.0缓存

  • 通知,渠道
  • 画中画
  • 自动填充
  • 后台限制
  • 自适应桌面图标
  • 隐式广播限制
  • 开启后台Service限制

Android 9.0安全

  • 利用 Wi-Fi RTT 进行室内定位
  • 刘海屏 API 支持
  • 多摄像头支持和摄像头更新
  • 不容许调用hide api
  • 限制明文流量的网络请求 http

Android 10服务器

  • 暗黑模式
  • 隐私加强(后台可否访问定位)
  • 限制程序访问剪贴板
  • 应用黑盒
  • 权限细分需兼容
  • 后台定位单独权限需兼容
  • 设备惟一标示符需兼容
  • 后台打开Activity 需兼容
  • 非 SDK 接口限制 需兼容

2.2 热修复原理

原理网络

  1. 安卓在加载class时会经过双亲委托机制去加载一个类,先让父类去加载,若是找不到再让子类去加载某个类.
  2. 经过查看ClassLoader源码发现findClass方法是由每一个子类本身实现的,好比BootClassLoader或者BaseDexClassLoader.而PathClassLoader是继承自BaseDexClassLoader的,它的findClass也是在BaseDexClassLoader里面实现的.
  3. BaseDexClassLoader的findClass里面使用了另外一个对象DexPathList去查找对应的class,这是安卓里面特有的实现.在DexPathList对象里面有一个属性dexElements,dexElements是用于存放加载好了的dex数组的,查找class是从这个dexElements数组里面去找的.
  4. dexElements里面存放的是Element对象,findClass最终会交给Element去实现,Element又会交给Element里面的一个属性DexFile去实现.我看了下,最终是用native实现的.
  5. 回到上面的第3步中的DexPathList对象从dexElements数组里面查找class,从数组的前面日后找,找到了就返回结果,再也不继续查找.
  6. 因此当咱们把修复好bug了的class,搞成dex,而后经过反射等技术放到dexElements的最前面,这样系统在经过PathClassLoader找到class时,就能先找到咱们放置的修复好bug的class,而后就不会再日后找了,至关于实现了热修复.这样有bug的class就不会被用了.应了一句古话,近水楼台先得月.
  7. 第6点中的反射,流程是:获取到PathClassLoader,而后反射获取到父类中的DexPathList对象,而后再反射到DexPathList对象中的dexElements数组.而后将补丁(dex)转为Element对象,插入到dexElements数组的前面(先复制出来,再合并,再经过反射放回去).

一句话总结多线程

将修复好的类放在dexElements的最前面,这样在加载类的时候就会被优先加载到而达到修复的目的.

2.3 MVC,MVP,MVVM

首先须要知道的是为何要进行技术框架的设计? 确定是为了低耦合,提升开发效率是吧.因此不要为了设计而设计.

MVC

在Android中View和Controller通常就是被Activity充当了,当逻辑很是多,操做很是复杂时,Activity代码量很是庞大,不易维护.

  • Model : 模型层,业务逻辑+数据存储等
  • View : 用户界面,通常就是xml+Activity
  • Controller : 控制层,通常就是Activity

MVP

我我的角度,如今(2019年10月29日20:02:49)大可能是使用这种方式,既不复杂也解耦合了.

  • Model:模型层,业务逻辑+数据存储+网络请求
  • View:视图层,View绘制和用户交互等,通常是Activity
  • Presenter:呈现层,链接V层和M层,完成他们之间的交互

MVVM

为了更加分离M,V层,因此有了MVVM.

  • Model:模型层,业务逻辑+数据存储+网络请求
  • View:视图层,View绘制和用户交互等,通常是Activity
  • ViewModel:其实就是Presenter和View的数据模型的合体.双向绑定,View的变更会反应到ViewModel中,数据的变更也会反应到View上.

推荐阅读:

Android开发四年面试相关知识整理

安卓虚拟定位实现与反做弊思路

Android应届毕业生“过五关斩六将”,怒刷千题,让你面试一路畅通!

2.4 组件化的好处

  1. 任意修改都须要编译整个工程,效率低下。
  2. 解耦,有利于多人团队协做开发
  3. 功能复用

2.5 app启动流程

  1. Launcher startActivity
  2. AMS startActivity
  3. Zygote fork进程
  4. Activity main()
  5. ActivityThread 进程loop循环
  6. 开启Activity,开始生命周期回调...

2.6 Activity启动流程

  1. Activity startActivityForResult
  2. Instrumentation execStartActivity
  3. AMS startActivity
  4. ApplicationThread scheduleLaunchActivity
  5. ActivityThread.H handleMessage -> performLaunchActivity 
  6. Activity attach
  7. Instrumentation callActivityOnCreate

2.7 app体积优化

  • 可使用lint工具,检测出没有用的文件.同时能够开启资源压缩,自动删除无用的资源.
  • 尽可能多使用可绘制对象,某些图像不须要静态图像资源,框架能够在运行时动态绘制图像.尽可能本身写Drawable,能不用UI切图就不用,占用空间小.
  • 重用资源. 好比一个三角按钮,点击前三角朝上表明收起的意思,点击后三角朝下,表明展开,通常状况下,咱们会用两张图来切换,咱们其实彻底能够用旋转的形式去改变.
  • 好比同一图像的着色不一样,咱们能够用android:tint和tintMode属性,低版本可使用ColorFilter
  • 压缩PNG和JPEG文件.能够减小PNG文件的大小,而不会丢失图像质量.
  • 使用WebP文件格式,可使用WebP文件格式,而不是使用PNG或JPEG文件. 可使用AS将现有的BMP,JPG,PNG或静态GIF图像转换成WebP格式.
  • 使用矢量图形. svg
  • 代码混淆. 使用proGuard代码混淆器工具,它包括压缩,优化,混淆等功能.这个你们太熟悉.
  • 插件化. 将功能模块放服务器上,按需下载,能够减小安装包大小.

2.8 app启动优化

  • 利用提早展现出来的Window,快速展现出来一个节目,给用户快速反馈的体验.障眼法,治标不治本.
  • 避免在启动时作密集沉重的初始化(Heavy app initialization).某些SDK初始化放在异步去加载(好比友盟,bugly这样的业务非必要能够异步加载),好比地图,推送等,非第一时间须要的能够在主线程作延时启动(好比闪屏页),当程序已经启动起来以后,再进行初始化. 对于网络,图片请求框架就必须在主线程中初始化了.
  • 启动时 避免I/O操做,反序列化,网络操做,布局嵌套等耗时操做

2.9 app布局优化

  • 若是父控件有颜色,也是本身须要的颜色,那么就没必要在子控件加背景颜色
  • 若是子控件有背景颜色,而且能彻底覆盖父控件,那么父控件不用设置背景颜色
  • 尽可能减小没必要要的嵌套
  • 能用LinearLayout和FrameLayout,就不要用RelativeLayout,由于RelativeLayout相对比较复杂,测绘也相对耗时.
  • include和merge一块儿使用,增长复用,减小层级
  • ViewStub按需加载,更加轻便
  • 复杂界面选择ConstraintLayout,可有效减小层级

2.10 app内存优化

  • 频繁使用字符串拼接用StringBuilder或者StringBuffer
  • ArrayMap、SparseArray替换HashMap
  • 避免内存泄漏
    • 集合类泄漏(集合一直引用着被添加进来的元素对象)
    • 单例/静态变量形成的内存泄漏(生命周期长的持有了生命周期短的引用)
    • 匿名内部类/非静态内部类
    • 资源未关闭形成的内存泄漏
  • 检测内存泄漏的几个工具: LeakCanary,TraceView,Systrace,Android Lint,Memory Monitor+mat

2.11 内存泄漏有哪些

  • 集合类泄漏(集合一直引用着被添加进来的元素对象)
  • 单例/静态变量形成的内存泄漏(生命周期长的持有了生命周期短的引用)
  • 匿名内部类/非静态内部类
  • 资源未关闭形成的内存泄漏
    • 网络,文件等流忘记关闭
    • 手动注册广播时,退出时忘记unregisterReceiver()
    • Service执行完成后忘记stopSelf()
    • EventBus等观察者模式的框架忘记手动解除注册

2.12 app线程优化

线程池 避免存在大量的Thread,重用线程池内部的线程,从而避免了线程的建立和销毁带来的性能开销,同时能有效控制线程池的最大并发数,避免大量线程因互相抢占系统资源而致使阻塞线现象发生. 推荐阅读 《Android开发艺术探索》 第11章.

分类

  • FixedThreadPool 数量固定的线程池
  • CachedThreadPool 只有非核心线程,数量不定,空闲线程有超时机制,比较适合执行大量耗时较少的任务
  • ScheduledThreadPool 核心线程数量固定,非核心线程没有限制.主要用于执行定时任务和具备固定中周期的重复任务.
  • SingleThreadPool 只有一个核心线程,确保全部的任务在同一个线程顺序执行,统一外界任务到一个线程中,这使得在这些任务之间不须要处理线程同步 的问题. 优势
  • 减小在建立和销毁线程上所花的时间以及系统资源的开销
  • 不使用线程池有可能形成系统建立大量的线程而致使消耗完系统内存以及"过分切换" 注意点
  1. 若是线程池中的数量未达到核心线程的数量,则直接启动一个核心线程来执行任务
  2. 若是线程池中的数量已经达到或超过核心线程的数量,则任何会被插入到任务队列中等待执行
  3. 若是2中的任务没法插入到任务队列中,因为任务队列已满,这时候若是线程数量未达到线程池规定的最大值,则会启动一个非核心线程来执行任务
  4. 若是3中的线程数量已经达到线程池最大值,则会拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution()方法通知调用者

2.13 Android换肤如何实现,原理

从新设置LayoutInflater的Factory2,从而拦截建立View的过程,而后搞成本身的控件,想怎么换肤就怎么换肤.

2.14 fresco原理,glide原理,二者区别,哪一个更省内存

这块暂时不懂,加入todo

2.15 Handler原理,Android 消息机制

里面介绍得很详细,顺便说了一下为何loop不会阻塞主线程问题.

Handler机制的关键在于对于ThreadLocal原理的理解,线程私有数据.利用ThreadLocal机制将Looper存放到线程内部,perfect !

2.16 Android 系统架构

应用层,应用框架层,系统运行库层,硬件抽象层和Linux内核层

2.17 经常使用布局有哪些

  • FrameLayout,LinearLayout,RelativeLayout,ConstraintLayout,CoordinatorLayout等

2.18 Android数据存储有几种方式

  • SharedPreferences: 小东西,最终是xml文件中,key-value的形式存储的.
  • 文件
  • 数据库
  • ContentProvider
  • 网络

2.19 View,SurfaceView

  • View是Android中全部控件的基类
  • View适用于主动更新的状况,而SurfaceView则适用于被动更新的状况,好比频繁刷新界面。
  • View在主线程中对页面进行刷新,而SurfaceView则开启一个子线程来对页面进行刷新。
  • View在绘图时没有实现双缓冲机制,SurfaceView在底层机制中就实现了双缓冲机制。

2.20 组件之间相互引用 如何解决

  • 调用其余组件的对外提供的方法:以前看到过一种思路,利用"接口+实现"的方式,定义一个ComponentBase 中间层,而后里面有每一个组件对外提供方法调用的Interface,每一个组件在初始化的时候就把这些Interface给实现了,而后其余组件须要用的时候就从ComponentBase里面取.
  • 界面跳转:ARouter

2.21 自定义View 饼状图,点击事件,画文字

这个你们能够跟着hencoder老师的文章系统学习一下.

2.22 Android 数字签名

校验用户身份,校验数据的完整性

2.23 fragment用在哪里,与Activity的区别

  • 当Activity须要模块化的时候
  • 不一样设备上的适配,好比平台和手机
  • Activity相对Fragment而言,很是笨重,通常小界面小模块用Fragment比较合适.或者首页的tab之类的.

今年年初我花一个月的时间收录整理了一套知识体系,若是有想法深刻的系统化的去学习的,能够点击传送门,我会把我收录整理的资料都送给你们,帮助你们更快的进阶。

相关文章
相关标签/搜索