一、Activity 与 Fragment 之间常见的几种通信方式?
viewModel 做数据管理,activity 和 fragment 公用同个viewModel 实现数据传递
二、LaunchMode 的应用场景?
LaunchMode 有四种,分别为 Standard, SingleTop,SingleTask 和 SingleInstance,每种模式的实现原理一楼都做了较详细说明,下面说一下具体使用场景:
-
Standard:
Standard模式是系统默认的启动模式,一般我们 app 中大部分页面都是由该模式的页面构成的,比较常见的场景是:社交应用中,点击查看用户A信息->查看用户A 粉丝->在粉丝中挑选查看用户B信息->查看用户A粉丝... 这种情况下一般我们需要保留用户操作 Activity栈的页面所有执行顺序。 -
SingleTop:
SingleTop 模式一般常见于社交应用中的通知栏行为功能,例如:App 用户收到几条好友请求的推送消息,需要用户点击推送通知进入到请求者个人信息页,将信息页设置为 SingleTop 模式就可以增强复用性。 -
SingleTask:
SingleTask 模式一般用作应用的首页,例如浏览器主页,用户可能从多个应用启动浏览器,但主界面仅仅启动一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。 -
SingleInstance:
SingleInstance 模式常应用于独立栈操作的应用,如闹钟的提醒页面,当你在A应用中看视频时,闹钟响了,你点击闹钟提醒通知后进入提醒详情页面,然后点击返回就再次回到A的视频页面,这样就不会过多干扰到用户先前的操作了。
三、BroadcastReceiver 与LocalBroadcastReceiver 有什么区别?
- BroadcastReceiver 是跨应用广播,利用Binder机制实现,支持动态和静态两种方式注册方式。
- LocalBroadcastReceiver 是应用内广播,利用Handler 实现,利用了IntentFilter的match功能,提供消息的发布与接收功能,实现应用内通信,效率和安全性比较高,仅支持动态注册。
四、谈一谈startService和bindService的区别,生命周期以及使用场景?
-
生命周期上的区别
执行startService时,Service会经历onCreate- >onStartCommand。当执行stopService时,直接调用onDestroy方法。调用者如果没有stopService,Service 会一直在后台运行,下次调用者再起来仍然可以stopService。执行bindService时,Service会经历onCreate- >onBind。这个时候调用者和Service绑定在一起。调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind- >onDestroy。这里所谓的绑定在一起就是说两者共存亡了。多次调用startService,该Service只能被创建一次,即该Service的onCreate方法只会被调用一次。但是每次调用startService,onStartCommand方法都会被调用。Service的onStart方法在API 5时被废弃,替代它的是onStartCommand方法。第一次执行bindService时,onCreate和onBind方法会被调用,但是多次执行bindService时,onCreate和onBind 方法并不会被多次调用,即并不会多次创建服务和绑定服务。 -
调用者如何获取绑定后的Service的方法
onBind回调方法将返回给客户端一个IBinder接口实例,IBinder允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。我们需要IBinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象。 -
既使用startService又使用bindService的情况
如果一个Service又被启动又被绑定,则该Service会一直在后台运行。首先不管如何调用,onCreate始终只会调用一次。对应startService调用多少次,Service的onStart 方法便会调用多少次。Service的终止,需要unbindService和stopService同时调用才行。不管startService与bindService的调用顺序,如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止。
那么,什么情况下既使用startService,又使用bindService呢?
如果你只是想要启动一个后台服务长期进行某项任务,那么使用startService便可以了。如果你还想要与正在运行的Service取得联系,那么有两种方法:一种是使用broadcast,另一种是使用bindService。前者的缺点是如果交流较为频繁,容易造成性能上的问题,而后者则没有这些问题。因此,这种情况就需要startService和bindService一起使用了。
另外,如果你的服务只是公开一个远程接口,供连接上的客户端(Android的Service是C/S架构)远程调用执行方法,这个时候你可以不让服务一开始就运行,而只是bindService,这样在第一次bindService的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是远程服务,那么效果会越明显(当然在Servcie创建的是偶会花去一定时间,这点需要注意)。 -
本地服务与远程服务
本地服务依附在主进程上,在一定程度上节约了资源。本地服务因为是在同一进程,因此不需要IPC,也不需要AIDL。相应
bindService会方便很多。缺点是主进程被kill后,服务变会终止。远程服务是独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被kill的是偶,该服务依然在运行。缺点是该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。对于startService来说,不管是本地服务还是远程服务,我们需要做的工作都一样简单。
五、谈谈你对 Activity.runOnUiThread 的理解?
一般是用来将一个runnable绑定到主线程,在runOnUiThread源码里面会判断当前runnable是否是主线程,如果是直接run,如果不是,通过一个默认的空构造函数handler将runnable post 到looper里面,创建构造函数handler,会默认绑定一个主线程的looper对象
六、子线程能否更新UI?为什么?
子线程是不能直接更新UI的
注意这句话,是不能直接更新,不是不能更新(极端情况下可更新)绘制过程要保持同步(否则页面不流畅),而我们的主线程负责绘制ui,极端情况就是,在Activity的onResume(含)之前的生命周期中子线程都可以进行更新ui,也就是onCreate,onStart和onResume,此时主线程的绘制还没开始。
七、 谈谈 Handler 机制和原理?
首先在UI线程我们创建了一个Handler实例对象,无论是匿名内部类还是自定义类生成的Handler实例对象,我们都需要对handleMessage方法进行重写,在handleMessage方法中我们可以通过参数msg来写接受消息过后UIi线程的逻辑处理,接着我们创建子线程,在子线程中需要更新UI的时候,新建一个Message对象,并且将消息的数据记录在这个消息对象Message的内部,比如arg1,arg2,obj等,然后通过前面的Handler实例对象调用sendMessge方法把这个Message实例对象发送出去,之后这个消息会被存放于MessageQueue中等待被处理,此时MessageQueue的管家Looper正在不停的把MessageQueue存在的消息取出来,通过回调dispatchMessage方法将消息传递给Handler的handleMessage方法,最终前面提到的消息会被Looper 从MessageQueue中取出来传递给handleMessage方法。
八、简述一下 Android 中 UI 的刷新机制?
1、界面刷新的本质流程
- 通过ViewRootImpl的 scheduleTraversals()进行界面的三大流程。
- 调用到scheduleTraversals()时不会立即执行,而是将该操作保存到待执行队列中。并给底层的刷新信号注册监听。
- 当 VSYNC信号到来时,会从待执行队列中取出对应的scheduleTraversals()操作,并将其加入到主线程 的消息队列中。
- 主线程从 消息队列中取出并执行三大流程:onMeasure()-onLayout()-onDraw()
2、同步屏障的作用
- 同步屏障用于阻 塞 住所有的同步消息(底层VSYNC的回调onVsync方法提交的消息是异步消息)
- 用于保证界面刷新功能的performTraversals()的优先执行。
3、同步屏障的原理?
- 主线程的Looper会一直循环调用MessageQueue的next方法并且取出队列头部的Message执行,遇到同步屏障(一种特殊消息)后会去寻找异步消息执行。如果没有找到异步消息就会一直阻塞下去,除非将同步屏障取出,否则永远不会执行同步消息。
- 界面刷新操作是异步消息,具有最高优先级
- 我们发送的消息是同步消息,再多耗时操作也不会影响UI的刷新操作
九、谈谈Android的事件分发机制?
当点击的时候,会先调用顶级viewgroup的dispatchTouchEvent,如果顶级的viewgroup拦截了此事件(onInterceptTouchEvent返回true),则此事件序列 由顶级viewgroup处理。如果顶级viewgroup设置setOnTouchListener,则会回调接口中的onTouch,此时顶级的viewgroup中的onTouchEvent不再回调,如果不设 置setOnTouchListener则onTouchEvent会回调。如果顶级viewgroup设置setOnClickListener,则会回调接口中的onClick。如果顶级viewgroup不拦截事件,事件就会向下传递给他的子view,然后子view就会调用它的dispatchTouchEvent方法。
十、谈谈如何优化ListView?
ViewHolder什么的持有View预加载/懒加载数据什么的
大招:用RecyclerView替换ListView
绝招:直接删除控件
十一、谈谈自定义LayoutManager的流程?
- 确定Itemview的LayoutParams generateDefaultLayoutParams
- 确定所有itemview在recyclerview的位置,并且回收和复用itemview onLayoutChildren
- 添加滑动canScrollVertically
十二、谈一谈获取View宽高的几种方法?
- OnGlobalLayoutListener获取
- OnPreDrawListener获取
- OnLayoutChangeListener获取
- 重写View的onSizeChanged()
- 使用View.post()方法
十三、Kotlin 中infix 关键字的原理和使用场景?
使用场景是用来修饰函数,使用了 infix 关键字的函数称为中缀函数,使用时可以省略 点表达式和括号。让代码看起来更加优雅,更加语义化。原理不过是编译器在语法层面给与了支持,编译为 Java代码后可以看到就是普通的函数调用。kotlin 的很多特性都是在语法和编译器上的优化。
十四、Kotlin中的可见性修饰符有哪些?相比于Java有什么区别?
kotlin存在四种可见性修饰符,默认是public。 private、protected、internal、public
- private、protected、public是和java中的一样的。
- 不同的是java中默认是default修饰符(包可见),而kotlin存在internal修饰符(模块内部可见)。
- kotlin可以直接在文件顶级声明方法、变量等。其中protected不能用来修饰在文件顶级声明的类、方法、变量等。构造方法默认是public修饰,可以使用可见性修饰符修饰constructor关键字来改变构造方法的可见性。
十五、请谈谈你对Binder机制的理解?
Binder机制:
- 为了保证进程空间不被其他进程破坏或干扰,Linux中的进程是相互独立或相互隔离的。
- 进程空间分为用户空间和内核空间。用户空间不可以进行数据交互;内核空间可以进行数据交互,所有进程共用一个内核空间。
- Binder机制相对于Linux内传统的进程间通信方式:
(1)性能更好;Binder机制只需要拷贝数据一次,管道、 消息队列、Socket等都需要拷贝数据两次;而共享内存虽然不需要拷贝,但实现复杂度高。
(2)安全性更高;Binder机制通过UID/PID在内核空间添加了身份标识,安全性更高。 - Binder跨进程通信机制:基于C/S架构,由Client、Server、ServerManager和Binder驱动组成。
- Binder驱动实现的原理:通过内存映射,即系统调用了mmap()函数。
- Server Manager的作用:管理Service的注册和查询。
- Binder驱动的作用:
(1)传递进程间的数据,通过系统 调用mmap()函数;
(2)实现线程的控制,通过Binder 驱动的线程池,并由Binder驱动自身进行管理。 - Server进程会创建很多线程处理Binder请求,这些线程采用Binder驱动的线程池,由Binder驱动自身进行管理。一个进程的Binder线程池默认最大是16个,超过的请求会 阻塞等待空闲的线程。
- Android中进行进程间通信主要通过Binder类(已经实现了IBinder接口),即具备了跨进程通信的能力
最后
我整理了一套Android面试题合集,也包括以上面试题,有需要完整面试题和答案解析的朋友可以关注哇哇,以上均可分享哦~!!!