让 Android 面试官百问不厌不过时的知识点(已整理)
Posted 冬天的毛毛雨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了让 Android 面试官百问不厌不过时的知识点(已整理)相关的知识,希望对你有一定的参考价值。
开头
为了准备面试复习了android的一些原理知识,并陆陆续续的总结了一些面试相关的东西,因为太久没写面试之类的博客了,今天就想做一个Android面试知识的分享。
但是无奈本人太蔡了(灬ꈍ ꈍ灬),在北京、深圳(远程视频面试)面试了十多家大厂就只拿到了oppo和小米的offer,综合来说小米的薪资比要比oppo高一点。听了朋友建议,选择了小米,打算着以后准备往音视频方向转型。
首先从Android原理基础入手复习,然后就是一些面试进阶涨薪需要掌握的知识整理,(包括了大厂的面试官百问不厌的Android拓展的知识点)已收集整理成PDF,分享出来希望能帮到大家在金三银四之间找到一个好的工作。
一. Android相关
1. mvc mvp mvvm三种架构模式(腾讯)
mvc:业务逻辑、数据、界面分离的一种模式,简单的来说,就是通过controller来操作model层的数据,并且返回给view显示。
activity不是标准的controller,随着界面逻辑交互的复杂度提升,activity类的职责不断增加,变得臃肿。
view和model相互耦合,不利于开发。
mvp:主要是提出了presenter层,作为view和model之间沟通的桥梁。
程序逻辑放在presenter中处理,完全将view和model进行了分离,不允许他们之间沟通。
mvvm:主要是将presenter改为了viewmodel,和mvp类似,不同的是viewmodel跟view和model进行双向绑定。
使用了data binding
2. Android系统结构层次
Android系统架构分为5层,从下到上依次为 Linux内核层,硬件抽象层,系统运行库层(Native),应用框架层,应用层。
-
Linux内核层:Android的核心基于Linux内核,在此基础上添加了Android的专用驱动(比如Binder)、系统的安全性、内存管理、进程管理等等。
-
硬件抽象层(HAL):有了核心还不行,你得需要运行到相应的硬件上才能实现自己的价值吧。而硬件抽象层就是硬件和Linux内核之间的接口,目的就是将硬件抽象化,保护硬件厂商的知识产权(Linux是有开源协议的)
系统运行库层:怎么操纵硬件,显示图像到屏幕?这一层就是干这个的,它分为两部分,分别是C++程序库和Android运行时。
- C++程序库:被Android系统中的不同组件使用,可以通过应用框架层被开发者使用,下面是主要的程序库:
-
Android Runtime:ART虚拟机(5.0之后,Dalvik虚拟机被ART取代),ART在应用第一次安装的时候,就会将字节码预编译成机器码存储到本地,这样应用每次运行就无须执行编译了(Dalvik是每次打开都要即时编译),典型的以空间换时间
-
应用框架层:Framework层,这层代码是用java编写的,为开发人员提供了API。
-
应用层
3. Activity活动的启动模式及应用场景(网易、百度)
- standard: 默认的模式,新建一个Activity就在栈中新建一个activity实例。
- singleTop:栈顶复用模式,与standard相比栈顶复用可以有效减少activity重复创建对资源的消耗 。 登录页面 ,wxpay等支付页面
- singleTask:栈内单例模式,栈内只有一个activity实例,栈内已存activity实例,在其他activity中start这个 activity,Android直接把这个实例上面其他activity实例踢出栈GC掉。主页面 ,WebView页面、扫一扫页面 ,付款界面
- singleInstance:开辟一个新的栈存放activity。系统Launcher、锁屏键、来电显示等系统应用 。
4. Android进程间通信的方式(小米、滴滴)
1.Broadcast广播,当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据
2.Content Provider,多个应用程序之间数据共享的方式(跨进程共享数据) ,应用程序可以完成对数据的增删改查。Android系统本身也提供了很多的Content Provider,比如音频,视频,联系人等信息。
3.通过AIDL文件,其中AIDL也是通过binder实现进程间通信的。
4.socket
1、传统的IPC通信方式
Android系统是基于Linux内核的,Linux提供了管道、消息队列、共享内存和socket等IPC机制。那为什么Android还要提供Binder来实现IPC呢?主要是基于性能、稳定性和安全性方面的考虑。
性能:socket作为通用接口,传输效率低,开销大,主要用到跨网络进程通信。消息队列、共享内存和管道采用存储-转发模式,数据拷贝至少需要两次,共享内存虽然无需拷贝,但是控制复杂,难以使用。而binder只需要拷贝一次,性能上只次于共享内存。
稳定性:Binder 基于 C/S 架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好。
安全性:Android 为每个安装好的 APP 分配了自己的 UID,故而进程的 UID 是鉴别进程身份的重要标志。传统的 IPC 只能由用户在数据包中填入 UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标识只有由 IPC 机制在内核中添加。其次传统的 IPC 访问接入点是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。
2、传统IPC通信原理
通常的做法是消息发送方将要发送的数据存放在内存缓存区中,通过系统调用进入内核态。然后内核程序在内核空间分配内存,开辟一块内核缓存区,调用 copy_from_user() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输,我们称完成了一次进程间通信。
一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝
接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。
3、Binder IPC实现
Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。
5. ContentProvider的设计模式
6. 多线程的实现方法(synchronized和lock的异同)
- 继承Thread类创建线程
- 实现Runnable接口创建线程,推荐使用这种方式,可以复用runnable
- 实现Callbale接口,通过FutureTask包装器来创建一个带返回值的线程
synchronized
在用法上,它是java的关键字,一般我们不太需要关注他的锁的释放,代码执行完毕或者报错会自动释放锁,并且无法判断锁的状态。
lock
是一个接口,我们使用ReentrantLock 比较多,有多个获取锁的方式,可以trylock直接返回获取成功或者失败,线程不用一直等待。在finally中必须要释放该锁。
7. 说一下View的事件分发机制
为什么要有事件分发
注:引用G神的博客
Android中的view是树形结构的,view可能会重叠在一起,当我们点击的地方有多个view的时候,这个时间该给谁,这就是为什么要有事件分发。
先来看看view的树形结构:
上面多出来两个东西是phonewindow
和decorview
,其中,主题颜色和标题栏内容等主要就是decorview来负责显示的,那PhoneWindow
是做什么的呢?
PhoneWindow
继承window
,并且是window
唯一的实现类,window
是一个抽象类,是所有视图的最顶层容器,视图的外观和行为都归他管,不论是背景显示,标题栏还是事件处理都是他管理的范畴,它其实就像是View界的太上皇。
DecorView
是 PhoneWindow
的一个内部类,其职位相当于小太监,就是跟在 PhoneWindow
身边专业为 PhoneWindow
服务的,除了自己要干活之外,也负责消息的传递,PhoneWindow
的指示通过 DecorView
传递给下面的 View,而下面 View 的信息也通过 DecorView
回传给 PhoneWindow
事件分发、拦截、消费
类型 | 相关方法 | Activity | ViewGroup | View |
---|---|---|---|---|
事件分发 | dispatchTouchEvent | √ | √ | √ |
事件拦截 | onInterceptTouchEvent | X | √ | X |
事件消费 | onTouchEvent | √ | √ | √ |
Activity作为原始的事件分发者,不需要拦截事件,如果需要这个事件不分发下去就行了。
同样的,view在事件传递的最末端,也不需要拦截事件,不处理回传回去就行了。
事件在收集之后最先传递给Activity,然后依次向下传递:
Activity -> PhoneWindow -> DectorView -> ViewGroup -> ... -> view
如果没有任何View消费掉事件,那么这个事件会按照反方向回传,最终传回给Activity,如果最后 Activity 也没有处理,本次事件才会被抛弃 :
Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View
上面的模式是一个非常经典的责任链模式
8. 说一下View从app启动到显示在界面上的绘制流程
在activity的attach方法里面,会创建一个PhoneWindow。
在onCreate中调用setContentView,setContentView
是window
的一个抽象方法,真正实现类是PhoneWindow
:
@Override
public void setContentView(int layoutResID)
if (mContentParent == null)
//1.初始化
//创建DecorView对象和mContentParent对象 ,并将mContentParent关联到DecorView上
installDecor();
else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS))
mContentParent.removeAllViews();//Activity转场动画相关
//2.填充Layout
if (hasFeature(FEATURE_CONTENT_TRANSITIONS))
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);//Activity转场动画相关
else
//将Activity设置的布局文件,加载到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
//让DecorView的内容区域延伸到systemUi下方,防止在扩展时被覆盖,达到全屏、沉浸等不同体验效果。
mContentParent.requestApplyInsets();
//3\\. 通知Activity布局改变
final Callback cb = getCallback();
if (cb != null && !isDestroyed())
//触发Activity的onContentChanged方法
cb.onContentChanged();
mContentParentExplicitlySet = true;
核心方法就两个:installDecor() 和 mLayoutInflater.inflate(layoutResID, mContentParent) ;
installDecor会创建一个DecorView
对象,该对象将作为整个应用窗口的根视图。然后配置不同窗口修饰属性(style theme等)。
mLayoutInflater.inflate就是解析xml,深度优先地递归解析xml,一层层添加到root view上,最终返回root view.解析的部分大致包含两点:
1.解析出View对象
2.解析View对应的Params,并设置给View。
9. 知道什么会引起ANR吗 怎么避免
有四种情况会造成ANR发生:
- 5秒内无法响应屏幕触摸事件或键盘输出
- 在执行前台广播的
onReceive()
函数时10秒没有处理完成,后台为20秒 - 前台服务20秒内,后台服务在200秒内没有执行完成
ContentProvider
的publish
在10s内没进行完
如何避免:
尽量避免在主线程中做耗时操作。 多线程==>引出如何实现多线程,线程池的使用
如何分析ANR:
- 产生anr之后,会在
data/anr/
目录下生成一个文件traces.txt
Logcat
中查看- Java线程调用分析
- DDMS分析
10. 有做过app的性能优化吗
1.app启动加速:一个app的启动分为三种不同的状态,其中,我们只需要对第一种状态做优化。对于App来说, 我们可以控制的启动时间线无外乎Application的onCreate
,首屏Activity的渲染。
- 冷启动:App没有启动过或者App进程被kill,系统中不存在该App进程。此时App的启动需要创建App进程,加载相关资源,启动main thread,初始化首屏Activity等。
- 热启动:App进程只是处于后台, 系统只是将其从后台带到前台, 展示给用户。屏幕会显示一个空白的窗口(颜色基于主题), 直至activity渲染完毕。
- 温启动:介于冷启动和热启动之间, 一般来说在以下两种情况下发生,
1.用户back退出了App, 然后又启动. App进程可能还在运行, 但是activity需要重建
2.用户退出App后, 系统可能由于内存原因将App杀死, 进程和activity都需要重启, 但是可以在onCreate中将被动杀死锁保存的状态(saved instance state)恢复
2.布局优化 减少不必要的嵌套
- 尽量不要嵌套使用RelativeLayout
- 尽量不要在嵌套的LinearLayout中都使用weight属性
- ConstraintLayout
- 善用TextView的Drawable减少布局层级
3.响应优化
4.内存优化 bitmap的使用,options的jusdecodeBounds属性,设置只解析bitmap的宽高等,然后使用insimplesize对bitmap进行压缩。在android2.3的时代,bitmap的回收需要调用recycler方法,并且置空,但是之后只需要进行置空操作。
加载一张大图:使用BitmapRegionDecode进行局部解码
-
decodeRegion(Rect rect, BitmapFactory.Options options) 指定rect区域获取图像,options参数不支持inPurgeable,其他都支持
lru算法的实现=> LinkedHashMap
5.电池使用优化
6.网络优化
最后
因文章篇幅长度限制,无法完整展现 Android 面试常见知识点,为了能帮助到大家参考进行学习,小编已将Android 面试知识点进行的整理PDF 文档形式。
如有需要请去我 GitHub 中查阅,望这些干货能够帮助到大家!!!
以上是关于让 Android 面试官百问不厌不过时的知识点(已整理)的主要内容,如果未能解决你的问题,请参考以下文章
阿里技术官耗时3年写的《MySQL笔记》解决了70%面试难题
牛B!鹅厂千页《MySQL笔记》资源泄露,涵盖MySQL所有高级知识点!