133

你不应该错过的 Android 全方位面试总结

 6 years ago
source link: http://mp.weixin.qq.com/s/yKsgPWx1WBvQg5geiW5PPg
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
本问是帅比张的小密圈友「Xueli」的投稿,对我们的 Android 面试知识点从小到大进行了相对全面的总结。

仅用作与记录自己看到的知识点总结,图片来源于网络 blog。


1. Activity 与 Fragment 的生命周期?

Activity:

  • 启动 Activiy:onCreate -> onStart() -> onResume(), Activity 进入运行状态.

  • Activity 退居后台 ( Home 或启动新 Activity ): onPause() -> onStop().

  • Activity 返回前台: onRestart() -> onStart() -> onResume().

  • Activity 后台期间内存不足情况下当再次启动会重新执行启动流程。

  • 锁屏: onPause() -> onStop().

  • 解锁: onStart() -> onResume().  

    Image

Fragment:

  • 创建 Fragment: onAttach() -> onCreate() -> onCreateView() -> onActivityCreate() -> onStart() -> onResume()

  • 销毁 Fragment: onPause() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach().

  • 从返回栈中回到上一个 Fragment: onDestroyView() -> onCreateView() -> onActivityCreated() -> onStart() -> onResume().

Activity 和 Fragment 关系:

Image

2. Activity 的四种启动模式和特点?

  • standard ( 标准模式 ) : 每次启动都会创建一个新的实例,并放入栈顶。(无法使用非 Activity 类的 Context 启动该模式的 Activity,如果启动需要加上 FlAG_ACTIVITY_NEW_TASK标记创建一个栈)。

  • singleTop ( 栈顶复用模式 ) : 如果启动的 Activity 在栈顶,则该 Activity 不会重建,同时 Activity 的 onNewIntent() 方法会被调用,如果不在栈顶则通 standard 模式。

  • singleTask ( 栈内复用模式 ) : 若启动的 Activity 在栈内,则不会创建新的实例调用 onNewIntent() 方法,并将该 Activity 上面所以的 Activity 清空(如果其他应用启动该 Activity,若不存在则简历新的 Task,若存在在后台,则后台 Task 也切换到前台)。

  • singleInstance ( 单例模式 ) : 新的 Activity 直接创建新的任务栈,当该模式的 Activity 存在于某个栈中,后面任何激活该 Activity 都会重用该实例。

3. Activity 的缓存方法?

Activity 由于异常终止时,系统会调用 onSaveInstanceState()来保存 Activity 状态 ( onStop() 之前和 onPause() 没有既定的时序关系 )。当重建时,会调用 onRestoreInstanceState(),并且把 Activity 销毁时 onSaveInstanceState() 方法所保存的 Bundle 对象参数同时传递给 onSaveInstanceState() 和 onCreate() 方法。因此,可通过 onRestoreInstanceState() 方法来恢复 Activity 的状态,该方法的调用时机是在 onStart() 之后。onCreate()和 onRestoreInstanceState() 的区别:onRestoreInstanceState()回调则表明其中 Bundle 对象非空,不用加非空判断。onCreate() 需要非空判断。建议使用onRestoreInstanceState().

4. Service 的生命周期及两种启动方法。

  • startService 启动服务: 用于启动一个服务执行后台任务,停止服务使用 stopService().

  • bindService 启动服务: 启动服务进行通信,停止服务使用 unbindService().

    Image
  • startService / stopServiceonCreate() -> onStartCommand() -> onDestroy()第一次调用会触发 onCreate() 和 onStartCommand,之后每次启动服务只触发 onStartCommand()无论 startService 多少次,stopService 一次就会停止服务

  • bindService/unbindServiceonCreate() -> onBind() -> onUnbind() -> onDestroy()第一次 bindService 会触发 onCreate() 和 onBind(),以后每次 bindService 都不会触发任何回调bindService 启动的生命周期依附于启动它的 Context

5. Service 保活。

  • 在 onStartCommond() 中将返回值设置为 START_STICKY

  • 在 onDestroy() 中重启 service

  • 黑色保活:不同 app 进程利用广播相互唤起。

  • 白色保活:启动前台 service

  • 灰色保活:利用系统漏洞启动前台 service。

6. 广播的两种注册方法,有什么区别。

  • 动态注册:通过调用 Context.registerReceiver() 进行注册,跟随组件的生命周期,特定时刻监听广播。

  • 静态注册:在 AndroidMainfest.xml 中声明,程序关闭后仍会调用,常驻广播,时刻监听广播。

7. Intent的使用方法及所传递的数据类型。

  • 显示 Intent:按名指定要启动的组件

  • 隐示 Intent:不指定特定组件,声明要执行的常规操作,从而允许其他应用中组件来处理。

  • Intent 可传递的数据类型:基本类型及其数组数据,String / CharSequence 类型的数据及其数组数据,也可以传递 Parcelable 和 Serializable 类型的数据,以及它们的数组/列表数据。

8. ContentProvider使用方法。

进行跨进程通信,实现进程间得数据交互和共享。通过 Context 中 getContentResolver() 获得实例,通过 Uri 匹配进行数据的增删改查。ContentProvider 使用表的形式来组织数据,无论数据的来源是什么,ContentProvider 都会认为是一种表,然后把数据组织成表格

9. Thread、AsyncTask、IntentService的使用场景与特点。

  • Thread线程,独立运行与于 Activity 的,当 Activity 被 finish 后,如果没有主动停止 Thread 或者 run 方法没有执行完,其会一直执行下去。

  • AsyncTask 封装了两个线程池和一个 Handler,(SerialExecutor 用于排队,THREAD_POOL_EXECUTOR 为真正的执行任务,Handler 将工作线程切换到主线程),其必须在 UI 线程中创建,execute 方法必须在 UI 线程中执行,一个任务实例只允许执行一次,执行多次将抛出异常,用于网络请求或者简单数据处理。

  • IntentService:处理异步请求,实现多线程,在 onHandleIntent 中处理耗时操作,多个耗时任务会依次执行,执行完毕后自动结束。

10. 五种布局: FrameLayout、LinearLayout、 AbsoluteLayout、RelativeLayout、TableLayout 各自特点及绘制效率对比。

  • FrameLayout : Android 中最简单布局,所有控件默认出现在左上角,可以使用 layout_margin,layout_gravity 等属性控制子控件的相对布局位置。

  • LinearLayout : 一行或者一列布局空间,可使用 orientation = horizontal | vertical 来控制布局方式。

  • AbsoluteLayout : 自定义空间的 x,y 来调整位置。

  • RelativeLayout : 相对布局,通过相关属性来定义空间的位置

android:layout_centerInParent = "true | false"android:layout_centerHorizonta = "true | false"android:layout_alignParentRight = "true | false"

  • TableLayout: 将字元素的位置以表格形式进行布局。

11. Android的数据存储形式。

  • SQLite 数据库存储

  • SharedPreference 存储

12. SQlite 的基本操作

Android 提供了 SQLiteOpenHelper 类,通过该类可以简单控制数据库的创建和升级。

  • onCreate() : 创建数据库

  • onUpgrade() : 升级数据库

两个方法必须重写,然后实现创建和升级数据库的逻辑。

  • getReadableDatabase() 和 getWritableDatabase() : 创建或打开一个现有数据库,返回一个可对数据库进行读写操作的对象,当数据库不可写入的时候getReadableDatabase() 只读的方式返回数据库对象,getWritableDatabase() 返回异常。

  • insert(): 插入数据

  • update(): 更新数据

  • delete(): 删除数据

  • query(): 查询数据

13. Android中的MVC模式。

  • Model(模型)

  • View(视图)

  • Controler(控制器)

View 传送指令到 Controller,其完成逻辑后通知 Model 改变状态,Model 将新数据更新到 View。所有通信都为单向。接受指令时,一是通过 View 接受指令并传递给 Controller;另一种是直接通过 Controller 接受指令。

14. Merge、ViewStub 的作用。

  • Merge: 减少视图层级,可以删除多余的层级,优化 UI。

  • ViewStub: 按需加载,减少内存使用量、加快渲染速度。不支持 merge 标签

15. Json有什么优劣势。

  • 轻量级的数据交换格式

  • 读写更加容易

  • 易于机器的解析和生成

  • 语义性较差,不如 xml 直观

16. 动画有哪两类,各有什么特点?

  • 传统动画:帧动画和补间动画。

  • 属性动画。

  • 属性动画才是真正的实现了 view 的移动,补间动画对 view 的移动更像是在不同地方绘制了一个影子,实际的对象还是处于原来的地方。

  • 当动画的 repeatCount 设置为无限循环时,如果在 Activity 退出时没有及时将动画停止,属性动画会导致 Activity 无法释放而导致内存泄漏,而补间动画却没有问题。

  • xml 文件实现的补间动画,复用率极高。在 Activity 切换,窗口弹出时等情景中有着很好的效果。

  • 使用帧动画时需要注意,不要使用过多特别大的图,容易导致内存不足。

17. Handler、Loop消息队列模型,各部分的作用。

Android消息机制包含: MessageQuene、Handler、Looper、Message.

  • Message: 需要春娣的消息,可以传递数据。

  • MessageQuene: 消息队列,通过单链表数据结构来维护消息列表,(投递和删除消息)。

  • Handler: 消息辅助类,向消息池发送各种消息事件

Handler.sendMessage 发送消息Handler.handleMessage 处理消息

  • Looper: 不断从消息队里中读取消息,按分发机制将消息分发给目标处理者。

MessageQuene、Handler 和 looper 三者之间的关系:

每个线程中只能存在一个 Looper,保存在 ThreadLocal 中。主线程(UI线程)已经创建了一个 Looper,所以在主线程中不需要再创建 Looper,其他线程中需要创建 Looper。每个线程中可以有多个 Handler,即一个 Looper 可以处理来自多个 Handler 的消息。 Looper 中维护一个 MessageQueue,来维护消息队列,消息队列中的 Message 可以来自不同的 Handler。

当调用 handler.sendMessage() 发送 message 时,实际上发送到与当前线程绑定的 MessageQuene 中,然后当前线程绑定的 Looper 不断从 MessageQueue 取出新的 Message,调用 msg.target.disspatchMessage(msg) 方法将消息分发到与 Message 绑定的 handler.handleMessage()中。

18. 怎样退出终止App。

  • 创建一个集合类对所有活动进行管理,ActivityCollector,通过 list 来管理 Activity。

  • killProcess(android.os.Process.myPid()) 杀死当前程序进程。

19. Asset目录与res目录的区别。

  • assets:不会在 R 文件中生成相应标记,存放到这里的资源在打包时会打入程序安装包中。(通过 AssetManager 类访问这些文件)

  • res:会在 R 文件中生成 id 标记,资源在打包时如果使用到则打包到安装包中,未使用到不会打入安装包中。

res/anim: 存放动画资源res/raw:和 asset 下文件一样打包时直接打入程序安装包中(会映射到 R 文件中)

Tablesassetsres/rawres/drawable
获取资源方式文件路径+文件名R.raw.xxxR.drawable.xxx
是否被压缩NONOYES(失真压缩)
能否获取子目录下的资源YESNONO

20. Android怎么加速启动Activity。

  1. onCreate() 中不执行耗时操作

  • 把页面显示的 View 细分一下,放在 AsyncTask 里逐步显示,用 Handler 更好。这样用户的看到的就是有层次有步骤的一个个的 View 的展示,不会是先看到一个黑屏,然后一下显示所有 View。

  • 最好做成动画,效果更自然。

  • 利用多线程的目的就是尽可能的减少 onCreate() 和onReume() 的时间,使得用户能尽快看到页面,操作页面。

  1. 减少主线程阻塞时间

  2. 提高 Adapter 和 AdapterView 的效率

  3. 优化布局文件

21. Android性能优化方法

  1. 布局优化:尽量减少布局文件的层级

  • 删除布局中无用的控件和层次,有选择的使用性能较低的 ViewGroup

  • 采用标签,ViewStub,布局重用可降低布局的层级(ViewStub提供了按需加载的功能,当需要时才会将 ViewStub 中布局加载到内存,提高了初始化效率)

  • 避免过度绘制

  1. 绘制优化:View 的 onDraw() 方法避免执行大量操作。

  • onDraw() 中不要创建新的布局对象。

  • onDraw() 中不做耗时任务,大量循环十分抢占 cpu 时间片,造成 View 的绘制不流畅。

  1. 内存泄漏优化

  • 避免写出内存泄漏代码

  • 通过分析工具(MAT、LeakCannary)找出潜在的内存泄漏方法,然后解决。

导致内存泄漏的原因:

  1. 集合类的泄漏

  2. 单例 / 静态变量造成内存泄漏

  3. 匿名内部类/非静态内部类造成内存泄漏

  4. 资源未关闭

  1. 响应速度的优化:避免在主线程做耗时的操作。

  2. ListView / RecyclerView优化:

  1. 使用 ViewHolder 模式来提高效率

  2. 异步加载:耗时操作放在异步线程

  3. 滑动时停止加载和分页加载

  1. 线程优化:采用线程池,避免程序中存在大量 Thread 。

  2. 其他性能优化:

  1. 不要过多的创建对象。

  2. 不要过多使用枚举类,枚举占用内存空间要比整型大。

  3. 常量使用 static final 修饰。

  4. 使用 Android 特有的数据结构。

  5. 适当采用软引用和弱引用。

  6. 采用内存缓存和磁盘缓存。

  7. 尽量采用静态内部类,可避免潜在由于内部类导致的内存泄漏。

22. Android中软引用与弱引用的应用场景。

Java 引用类型分类:

Image

在 Android 应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。

软/弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。利用这个队列可以得知被回收的软/弱引用的对象列表,从而为缓冲器清除已失效的软 / 弱引用。

如果只是想避免 OOM 异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。

可以根据对象是否经常使用来判断选择软引用还是弱引用。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。

23. Bitmap 压缩策略

  1. 加载 Bitmap 的方式:

  • BitmapFactory 四类方法:

  • decodeFile( 文件系统 )

  • decodeResourece( 资源 )

  • decodeStream( 输入流 ) 

  • decodeByteArray( 字节数 )

  1. BitmapFactory.opeions 参数

  • inSampleSize 采样率,对图片高和宽进行缩放,以最小比进行缩放(一般取值为 2 的指数)。通常是根据图片宽高实际的大小/需要的宽高大小,分别计算出宽和高的缩放比。但应该取其中最小的缩放比,避免缩放图片太小,到达指定控件中不能铺满,需要拉伸从而导致模糊。

  • inJustDecodeBounds 获取图片的宽高信息,交给  inSampleSize 参数选择缩放比。通过 inJustDecodeBounds = true,然后加载图片就可以实现只解析图片的宽高信息,并不会真正的加载图片,所以这个操作是轻量级的。当获取了宽高信息,计算出缩放比后,然后在将 inJustDecodeBounds = false,再重新加载图片,就可以加载缩放后的图片。

  1. 高效加载 Bitmap 的流程

  • 将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 true 并加载图片

  • 从 BitmapFactory.Options 中取出图片原始的宽高信息,对应于 outWidth 和 outHeight 参数

  • 根据采样率规则并结合目标 view 的大小计算出采样率 inSampleSize

  • 将 BitmapFactory.Options 的 inJustDecodeBounds 设置为 false 重新加载图片

24. 自定义View过程:onMeasure()、onLayout()、onDraw()

  • onMeasure() 方法:单一 View,一般重写此方法,针对 wrap_content 情况,规定 View 默认的大小值,避免于 match_parent 情况一致。ViewGroup,若不重写,就会执行和单子View 中相同逻辑,不会测量子 View。一般会重写 onMeasure() 方法,循环测量子 View。

  • onLayout()方法:单一 View,不需要实现该方法。ViewGroup 必须实现,该方法是个抽象方法,实现该方法,来对子 View 进行布局。

  • onDraw() 方法:无论单一View,或者 ViewGroup 都需要实现该方法,因其是个空方法.

25. 事件分发机制

事件分发机制详解

26. Android长连接,怎么处理心跳机制。

  • 长连接:长连接是建立连接之后, 不主动断开. 双方互相发送数据, 发完了也不主动断开连接, 之后有需要发送的数据就继续通过这个连接发送.

  • 心跳包:其实主要是为了防止NAT超时,客户端隔一段时间就主动发一个数据,探测连接是否断开

  • 服务器处理心跳包:假如客户端心跳间隔是固定的, 那么服务器在连接闲置超过这个时间还没收到心跳时, 可以认为对方掉线, 关闭连接. 如果客户端心跳会动态改变,  应当设置一个最大值, 超过这个最大值才认为对方掉线. 还有一种情况就是服务器通过TCP连接主动给客户端发消息出现写超时, 可以直接认为对方掉线.

  • Android微信智能心跳方案

27. Zygote的启动过程

在 Android 系统里面,zygote 是一个进程的名字。Android 是基于 Linux System 的,当你的手机开机的时候,Linux 的内核加载完成之后就会启动一个叫 “init“ 的进程。在 Linux System 里面,所有的进程都是由 init 进程 fork 出来的,我们的zygote进程也不例外。

28. Android IPC:Binder原理。

  • IPC:

    Image
    不同进程之间彼此是不能共享的,而内核空间却是可共享的。Client 进程向 Server 进程通信,利用进程间可共享的内核内存空间来完成底层通信工作的,Client 端与 Server 端进程往往采用 ioctl 等方法跟内核空间的驱动进行交互。
  • Binder 原理:Binder 通信采用 C/S 架构,包含 Client、Server、ServiceManager 以及 binder 驱动,其中 ServiceManager 用于管理系统中的各种服务。架构图如下所示:

    Image

Binder 四个角色:

  1. Client 进程:使用服务的进程

  2. server 进程:提供服务的进程

  3. ServiceManager 进程:将字符型是的 Binder 名字转为 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 名字获取到 Server 中 Binder 实体的引用。

  4. Binder 驱动:进程间 Binder 通信的建立,Binder 在进程间的传递,Binder 引用计数管理,数据包在进程间传递和交互等一系列底层支持。

Binder 运行机制:

  1. 注册服务:Server 在 ServiceManager 注册服务

  2. 获取服务:Client 从 ServiceManager 获取相应的 Service

  3. 使用服务:Client 根据得到的 Service 信息建立与 Service 所在的 Server 进程通信的通路可与 Server 进行交互。

29. Android 5.0,6.0,7.0 8.0新特性。

  1. Material Design

  2. 支持多种设备

  3. 全新通知中心

  4. 支持 64 位 ART 虚拟机

  5. 电池续航改进

  6. 全新“最近应用程序”

  7. 安全性改进

  8. 不同数据独立保存

  9. 支持蓝牙 4.1、USB Audio、多人分享等

  1. 动态权限管理

  2. 系统层支持指纹识别

  3. APP 关联

  4. Android Pay

  5. TF 卡默认存储

  1. 分屏多任务

  2. 下拉快捷开关

  3. 新通知消息

  4. 流量保护模式

  5. 全新设置样式

  6. 改进 Doze 休眠机制

  7. 系统级电话黑名单

  8. 菜单键快速切换应用

  1. 自动填充框架

  2. 等等优化很多Android 8.0


欢迎关注南尘的公众号:nanchen
如果你喜欢,你可以选择分享给大家。如果你有好的文章,欢迎投稿,赞赏全部归你所有。

如果你正巧要加入

 stormzhang 的知识星球

不妨扫描下方二维码加入

Image
Image

  长按上方二维码关注

做不完的开源,写不完的矫情

nanchen 的成长笔记


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK