Android面试题整合(Android篇)

各位同学,这是一篇面试总结文,把面试过程中遇到的问记录下来,并附上如何回答。希望对正在面试或者将要面试的你有一些小帮助。
7f670aa6198e7af7bc8e4d11623154bc.jpeg
1.Glide中怎么实现图片的加载进度条,Glide的缓存是怎么设计的?为什么要用弱引用?
1.将HTTP通讯组件替换成OkHttp
2.向OkHttp中添加一个自定义的拦截器
3.新建一个ProgressResponseBody类,并让它继承自OkHttp的ResponseBody,
然后在这个类当中去编写具体的监听下载进度的逻辑
---
1.内存缓存:防止应用重复将图片数据读取到内存当中,
  其中内存缓存又包含 弱引用 和 LruCache 
2.硬盘缓存:防止应用重复从网络下载数据。
  硬盘缓存就是 DiskLruCache
---
1.可以保护当前使用的资源不会被 LruCache 算法回收
2.即可以缓存正在使用的强引用资源,又不阻碍系统需要回收的无引用资源。
2.implementation 和 api的区别是什么?
加入我们项目中有A、B、C
第一种情况:
A implementation B
B implementation C
A可以直接访问B中资源,但不能直接访问C
第二种情况:
A implementation B
B api C
A 可以直接访问B和C中的资源
---
使用implementation 依赖项配置代替 api 可以显著缩短构建时间,
因为这样可以减少构建系统需要重新编译的模块数。
3.事件分发的流程,以及怎么解决滑动冲突?mFirstTarget 为什么是一个链表?
---
Android事件分发流程 = Activity -> ViewGroup -> View
---
1,从外部拦截机制考虑
  例如:PullToRefreshView嵌套了ViewPager
2.子View如果不希望其父View拦截Touch事件时,
  调用getParent().requestDisallowInterceptTouchEvent
---
插入和删除速度快,保留原有的物理顺序
使内存的利用率变高
4.自定义View需要经历哪几个过程?
measure: 判断是否需要重新计算View的大小,需要的话则计算;
layout: 判断是否需要重新计算View的位置,需要的话则计算;
draw: 判断是否需要重新绘制View,需要的话则重绘制。
5.A 跳转到 B页面,两个页面的生命周期怎么走?什么情况下A的stop()不会执行。
1.A先执行onPause
2.然后是B执行onCreate -> onStart -> onResume
3.最后A执行onStop
---
ActivityB为dialog式或者半透明背景 
6.Activity的4中启动模式分别是什么,有什么不同?
standard:无论该活动有没有加入栈,活动就会被创建。
singleTop:只要被创建的活动不位于栈的顶部,该活动就会被创建入栈。
singleTask:单任务模式。
singleInstance:被设置成singleInstance的Activity将会放入单独的栈中。
7.okhttp中有几个队列?分别干什么用的?怎么取消一个请求?
两个队列
---
等待队列和执行任务的队列
---
OkHttp提供了cancel方法
ps:不同的失败类型返回的IOException e 不一样,
可以通过e.toString 中的关键字来区分不同的错误类型
8.Rxjava中map和flatMap有什么区别,都用过什么操作符。
map:对数据进行变换后,可以返回任意值。是1对1进行的。
flatMap:对数据变换后,返回ObservableSource对象。
        可以对数据进行一对多,多对多的变换。并不保证数据
        有序。
concatMap:与flatMap使用基本一致,它可以保证数据有序
---(用过的操作符)---
1.创建操作符:
  just():此操作符的作用是将传入的数据依次发送出去.最多可以传10个参数
  fromIterable() :此操作符的作用是将传入的数组集合按脚标依次发送出去
  fromArray() :对一个数组集合进行观察,把数组一次性发给观察者
  Range():作用发送指定范围的序列,可指定范围.作用类似intervalRange,但不同的是range是无延迟发送
  interval():这个相当于定时器,用它可以取代CountDownTimer。
    它会按照设定的间隔时间,每次发送一个事件,发送的事件序列:默认从0开始,无限递增的整数序列
2.转换操作符:
  map():被观察者转换器,通过指定一个Funcation对象,将Observable
    转换成新的Observable对象并发射,观察者会收到新的Observable并处理
  flatMap():把Observable的发射事件集合转换成新的Observable集合
  concatMap() :与flatMap唯一不同的是concat能保证Observer接收到Observable集合发送事件的顺序
  buffer():把发射数据按照一定间隔分成若干段。按每段的数据转换成新的Observable,
    这个Observable把一段数据一次性发射出去。
3.合并操作符 :
  merge():把多个Observable合并成一个进行发射(并行无序)
  concat ():把多个Observable合并成一个进行发射(串行有序)
  concatDelayError()/mergeDelayError():可以使onError事件推迟到其它被观察者发送事件结束后在再触发
  zip():把多个Observable合并后,并且把这些Observable的数据进行转换再发射出去。
    转换之后的数据数目由最短数据长度的那个Observable决定。
  collect():把 Observable(被观察者)发送的事件收集到一个数据结构中
  startWith()/startWithArray():在一个被观察者发送时间前,追加发送一些数据/一个新的被观察者
  count():统计被观察者发送事件数量
4.功能操作符:
  subscribe():连接被观察者和观察者
  delay():延迟发送事件
  do :系列操作符
     *doOnEach() :当Observable每发送一次事件就会调用一次(包含onNext(),onError(),onComplete())
     * doOnNext(): 执行 onNext()前调用
     * doAfterNext(): 执行onNext()后调用
     * doOnComplete():执行onComplete()前调用
     * doOnError():执行 onError()前调用
     * doOnTerminate(): 执行终止(无论正常发送完毕/异常终止)
     * doFinally(): 最后执行
     * doOnSubscribe() :观察者订阅是调用
     * doOnUnScbscribe(): 观察者取消订阅时调用
  onErrorReturn() :可以捕获错误。发送一个特殊事件,并且正常终止.注意后面的事件不会再发送
  onExceptionResumeNext():遇到错误时发送一个新的Observable 。并且正常终止.注意原Observable后面的事件不会再发送
  retry() :出现错误时,让被观察者重新发送数据
  repeat() :repeat操作符的作用是重复发射 observable的数据序列,可以使无限次也可以是指定次数.不传时为重复无限次
  debounce() :一定的时间内没有操作就会发送事件(只会发送最后一次操作的事件)。
  subscribeOn():发送事件的线程
  observerOn():接收事件的线程
5.过滤操作符:
  filter() :对被观察者发送的事件做过滤操作。只有符合筛选条件的事件才会被观察者所接收。
  distinct():去重
  skip():把Observable发射的数据过滤点掉前n项。
  take():只能接收n事件
  elementAt():只发射第n项数据.(第n项,第N项不存在时默认值)
  ignoreElements() :不管发射的数据.只希望在它完成时和遇到错误时收到通知
  ofType() :只发送指定类型数据。
  sample():在某段时间内,只发送该段时间内最新(最后)1次事件
  firstElement()/lastElement():选取第一个元素/最后一个元素
9.如果Rxjava组合发送任务,中间任务出现异常,其他任务该怎么处理。
使用concatError()/mergeDelayError()事件,
可以使onError事件推迟到其它被观察者发送事件结束后在再触发
10.哪个场景会发生内存泄露?内存泄露怎么检测?以及leak cannery内部原理是什么?为什么新版本的不需要在Application中注册了?
---哪个场景会发生内存泄露---
1. 资源性对象未关闭(比如 Cursor、File 文件等)
2. 注册对象未注销
3. 类的静态变量持有大数据对象(静态变量长期维持对象的引用,阻止垃圾回收,如果静态变量持有大的数据对象,
   如Bitmap 等,就很容易引起内存不足等问题。)
4. 非静态内部类的静态实例(非静态内部类会维持一个到外部类实例的引用,如果非静态内部类的实例是静态的,
   就会间接长期维持着外部类的引用,阻止被系统回收。)
5. Handler临时性内存泄漏
    (Message 发出之后存储在 MessageQueue 中,有些 Message 也不是马上就被处理到。
    在Message 中存在一个 target,它是 Handler 的一个引用,Message 在 Queue 中存在的时间过长,
    就会导致 Handler 无法被回收。如果 Handler 是非静态的,
    则会导致 Activity 或者 Service不会被回收。)
    解决方法:
    1)使用一个静态 Handler 内部类,然后对 Handler 持有的对象使用弱引用,
       这样在回收时,也可以回收 Handler 持有的对象。
    2)在 Activity 的 Destroy 或者 Stop 时,应该移除消息队列中的消息,
       避免 Looper 线程的消息队列中有待处理的消息需要处理。
6. 容器中的对象没清理造成内存泄漏
    通常把一些对象的引用加入集合中,在不需要该对象时,如果没有把它的引用从集合中清理掉,
    这个集合就会越来越大。如果这个集合是 static,情况就更严重。
7. WebView
    通常解决这个问题的办法是为 WebView 开启独立的一个进程,
    使用AIDL 与应用的主进程进行通信,WebView 所在的进程可以根据业务的需要选择
    合适的时机进行销毁,达到正常释放内存的目的。
---内存泄露怎么检测---
集成Android LeakCanary
直接在Application中使用,然后运行APP就会自动检测,检测到会在另一个APP上通知
---内部原理---
监听Activity生命周期->onDestroy以后延迟5秒判断Activity有没有被回收
->如果没有回收,调用GC,再此判断是否回收,如果还没回收,则内存泄露了,反之,没有泄露。
整个框架最核心的问题就是在什么时间点如何判断一个Activity是否被回收了。
---为什么新版本的不需要在Application中注册了---
1,由于我们添加了LeakCanary2的依赖,而在他依赖的leakcanary-object-watcher-android工程中,注册了一个provider。
2,当app进程启动的时候,会自动创建注册这个provider,并执行其中的onCreate方法。
3,在这个onCreate方法中,调用了LeakCanary的install方法,创建各自的watcher,并注册监听了application的ActivityLifecycleCallback。
11.手机适配问题怎么处理,都有什么方案。
1.使用布局元素自适应尺寸
RelativeLayout + LinearLayout + ConstraintLayout
2.屏幕分辨率限定符适配(简单说,就是穷举市面上所有的Android手机的宽高像素值)
3.smallestWidth限定符适配(sw限定符适配)(插件 ScreenMatch)
  sw限定符适配是宽高限定符的升级版,他解决了宽高限定符容错机制差的问题,
  是目前主流的适配方式之一。
4.使用 Nine-Patch 图片。
5.代码动态设置尺寸。
12.Android9 10 11 都更新了什么新特性?新版本中无法获取IMEI怎么处理。
Android9:
  1.自适应电池
  2.黑暗模式
  3.新截图快捷方式
  4.一个新的Home按钮
  5.新的手势导航
Android10:
  1. 暗黑模式
  2. 隐私增强(如您可以选择应用程序在后台运行时是否可以访问该位置)
  3.超级锁定模式
  4.屏幕录制
  5.面部识别
  6.限制程序访问剪贴板
Android11:
1.短信更新改进
2.隐私和权限(新增了关于位置、麦克风和摄像头的一次性权限许可)
3.内置屏幕录制
4.适配不同设备(折叠手机)
5.网络优化(通过5G连接到无线网络)
---
Android 10 中官方明确说明第三方应用无法获取到IMEI码
Android 10 以下的版本,需要申请READ_PHONE_STATE权限。
13.数据序列化有那俩种方式,Serialization和Parcelable区别,如果持久化需要用哪一个?
序列化:将对象的状态信息转换为可以存储或传输的形式的过程
Serializable:是Java的实现方式,会频繁的IO操作,所以消耗比较大,但是实现方式简单
Parcelable:是Android提供的方式,效率比较高,但是实现起来复杂一些 
持久化需要用Serializable
14.组件化怎么分层,各个组件之间怎么通信。
1.宿主层:不做具体的项目功能实现,只负责集成业务模块,组装成一个完整的APP;
2.业务模块层:将项目的每个大功能模块拆分成的一个一个单独的module,可独立运行,同产品的不同项目也可复用;
3.业务组件层:用于业务模块间调用,例如支付组件 、地图组件、分享组件等等;
4.基础组件层:基础组件层,与业务无关,与项目也无关,所有项目都可以全部复用,
  包含了各种开源库以及和业务无关的各种自研工具库;
---
各个组件之间通过`阿里的ARouter`通信
15.怎防止程序崩溃,如果已经到了Thread.UncaughtExceptionHandler是否可以让程序继续运行。
我们可以给应用设置我们自定义的UncaughtExceptionHandler
---
子线程发生了未捕获异常不会导致Crash(子线程被终止了,主线程还在运行)
主线程发生了未捕获异常会导致ANR(主线程已经被终止了)。
16.Handler Looper mesaageQueue message 之间的关系。
 Looper:相当于消息的载体
 • 它的内部有一个消息队列,也就是MessageQueue,Handler发送的所有消息都会走向这个消息队里。
 • 它的Looper.loop方法是一个死循环,不断的从消息队列MessageQueue中取出消息。如果有消息存在就处理该消息,否则就阻塞。
 messageQueue :就是一个消息队列,可以向其中添加消息并处理消息。
 handler:其实就是发送消息处理消息的封装。它与Looper相关联,也就是说在Handler的内部可以找到Looper,
    找到了Looper就找到了相应的消息队列。因此Handler发送的消息都会走向MessageQueue。

其实就是:
Handler负责发送消息和接收Looper传过来的消息,并根据消息处理相应逻辑
Looper负责接收Handler发送过来的消息,并将该消息回传给Handler自己。
MessageQueue只是相当于一个消息容器
17.子线程一定不能更新ui么?什么时候可以?什么时候不可以。检测逻辑是在什么阶段初始化的。
ViewRootImpl对象是在onResume方法回调之后才创建,那么就说明了为什么在生命周期的onCreate方法里,
甚至是onResume方法里都可以实现子线程更新UI,因为此时还没有创建ViewRootImpl对象,
并不会进行是否为主线程的判断;
18.ANR发生的原理是什么, 怎么排查。
1.5秒内无法对输入事件(按键及触摸)做出响应
2.广播接收器无法在10秒内结束运行
3.前台服务20秒内,后台服务在200秒内没有执行完毕。
4.ContentProvider的publish在10s内没进行完。
---怎么排查
分析办法一:Log
分析办法二:traces.txt
---原因
1.主线程阻塞或主线程数据读取
  解决办法:避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。
2.CPU满负荷,I/O阻塞
  解决办法:文件读写或数据库操作放在子线程异步操作。
3.内存不足
  解决办法:防止内存泄漏,优化内存使用
4.各大组件ANR
  各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、
  后台Service和ContentProvider也不要执行太长时间的任务。
19.程序怎么保活。
1:开启系统电池白名单。
2:引导用户在设置里,将app加入后台运行白名单。
3:开启前台服务。
20.说下路由ARoute的实现原理,怎么处理页面过多内存占用过大问题。
1.路由框架会在项目的编译期通过注解处理器扫描所有添加@Route注解的Activity类,
2.然后会在编译时期通过apt将Route注解中的path地址和Activity.class文件映射关系保存到它自己生成的java文件中
3.然后app进程启动的时候会加载这些类文件,把保存这些映射关系的数据读到内存里(保存在map里)
4.然后在进行路由跳转的时候,通过build()方法传入要到达页面的路由地址,
  ARouter会通过它自己存储的路由表找到路由地址对应的Activity.class,
  然后new Intent(context, activity.Class)
5.当调用ARouter的withString()方法它的内部会调用intent.putExtra(String name, String value)
6.调用navigation()方法,它的内部会调用startActivity(intent)进行跳转
---怎么处理页面过多内存占用过大问题
知道的帮我贴个链接,谢了。
21.线程池都什么时候用,怎么创建,构造函数中的参数分别代表什么意思?
线程频繁地创建与销毁

---怎么创建---
Executors.newCachedThreadPool();
*执行很多短期异步的小程序或者负载较轻的服务器
Executors.newFixedThreadPool(5);
*执行长期的任务,性能好很多
Executors.newSingleThreadExecutor();
*一个任务一个任务执行的场景
Executors.newScheduledThreadPool(3);
*周期性执行任务的场景

---构造函数中的参数---
corePoolSize:该线程池中核心线程的数量。
maximumPoolSize:该线程池中最大线程数量。
keepAliveTime:非核心线程空闲时间
unit:上面时间属性的单位
workQueue:任务队列,后面详述。
threadFactory:线程工厂,可用于设置线程名字等等,一般无须设置该参数。
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory) {
}
22.进程优先级。
前台进程
可见进程
服务进程
后台进程
空进程
23.本地广播和正常广播的区别。
本地广播:发送的广播事件不被其他应用程序获取,也不能响应其他应用程序发送的广播事件。
  本地广播只能被动态注册,不能静态注册。动态注册或方法时需要用到LocalBroadcastManager。
全局广播:发送的广播事件可被其他应用程序获取,也能响应其他应用程序发送的广播事件
  (可以通过 exported–是否监听其他应用程序发送的广播在清单文件中控制)
  全局广播既可以动态注册,也可以静态注册。
23.Activity启动流程,Launcher启动流程。
---android中activity启动流程
一.Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity;
二.ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;
三.Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态,
    于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行;
四.ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,以便以后ActivityManagerService能够通过这个Binder对象和它进行通信;
五.ActivityManagerService通过Binder进程间通信机制通知ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。
---Launcher的启动由三部分启动:
    SystemServer完成启动Launcher Activity的调用
    Zygote()进行Launcher进程的Fork操作
    进入ActivityThread的main(),完成最终Launcher的onCreate操作
24.mvvp 和mvp的区别,细节里怎么实现的双向绑定。
MVC:View触发事件,controller响应并处理逻辑,调用Model,Model处理完成后将数据发送给View,View更新。
MVP:以Presenter为核心,负责从model获取数据,并填充到View中。
MVVM:View与VM保持同步,View绑定到VM的属性上,如果VM数据发生变化,通过数据绑定的方式,View会自动更新视图;VM同样也暴露出Model中的数据。
---
可通过databing实现双向绑定。

你可能感兴趣的:(Android面试题整合(Android篇))