10 道阿里 Android 岗必问题摆这儿了,你爱刷不刷!(附参考回答解析
**START_REDELI
VER_INTENT:** 如果返回 START_REDELIVER_INTENT,其返回情况与 START_STICKY 类似,但不同的是系统会保留最后一次传入 onStartCommand 方法中的 Intent 再次保留下来并再次传入到重新创建后的 Service 的 onStartCommand 方法中
3.2 提高 Service 的优先级 在 AndroidManifest.xml 文件中对于 intent-filter 可以通过 android:priority ="1000"这个属性设置最高优先级,1000 是最高值,如果数字越小则优先级越低,同时适用于广播;
3.3 在 onDestroy 方法里重启 Service 当 service 走到 onDestroy()时,发送一个自定义广播,当收到广播时,重新启动 service;
3.4 提升 Service 进程的优先级 进程优先级由高到低:前台进程 一 可视进程 一 服务进程 一 后台进程 一空进程 可以使用 startForeground 将 service 放到前台状态,这样低内存时,被杀死的概率会低一些;
3.5 系统广播监听 Service 状态;
3.6 将 APK 安装到/system/app,变身为系统级应用;
注意:以上机制都不能百分百保证 Service 不被杀死,除非做到系统白名单,与系统同生共死。
4. 描述一下 Android 数据持久存储方式
参考回答: Android 平台实现数据持久存储的常见几种方式:
SharedPreferences 存储:一种轻型的数据存储方式,本质是基于 XML 文件存储的 key-value 键值对数据,通常用来存储一些简单的配置信息(如应用程序的各种配置信息);
SQLite 数据库存储:一种轻量级嵌入式数据库引擎,它的运算速度非常快,占用资源很少,常用来存储大量复杂的关系数据;
ContentProvider:四大组件之一,用于数据的存储和共享,不仅可以让不同应用程序之间进行数据共享,还可以选择只对哪一部分数据进行共享,可保证程序中的隐私数据不会有泄漏风险;
File 文件存储:写入和读取文件的方法和 Java 中实现 I/O 的程序一样;网络存储:主要在远程的服务器中存储相关数据,用户操作的相关数据可以同步到服务器上;
5. Android 中 IPC 方式、各种方式优缺点,为什么选择 Binder?
参考回答:
与 Linux 上传统的 IPC 机制,比如 System V,Socket 相比,Binder 好在哪呢?
传输效率高、可操作性强
传输效率主要影响因素是内存拷贝的次数,拷贝次数越少,传输速率越高。从 Android 进程架构角度分析:对于消息队列、Socket 和管道来说,数据先从发送方的缓存区拷贝到内核开辟的缓存区中,再从内核缓存区拷贝到
接收方的缓存区,一共两次拷贝,如图:
而对于 Binder 来说,数据从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同
一块物理地址的,节省了一次数据拷贝的过程,如图:
由于共享内存操作复杂,综合来看,Binder 的传输效率是最好的。
实现 C/S 架构方便: Linux 的众 IPC 方式除了 Socket 以外都不是基于 C/S 架构,而 Socket 主要用于网络间的通信且传输效率较低。Binder 基于 C/S 架构 ,Server 端与 Client 端相对独立,稳定性较好。
安全性高: 传统 Linux IPC 的接收方无法获得对方进程可靠的 UID/PID,从而无法鉴别对方身份;而 Binder 机制为每个进程分配了 UID/PID 且在 Binder 通信时会根据 UID/PID 进行有效性检测。
6. Bundle 传递对象为什么需要序列化?Serialzable 和 Parcelable 的区别?
参考回答:
因为 bundle 传递数据时只支持基本数据类型,所以在传递对象时需要序列化转换成可存储或可传输的本质状态(字节流)。序列化后的对象可以在网络、IPC(比如启动另一个进程的 Activity、Service 和 Reciver)之间进行传输,也可以存储到本地。
序列化实现的两种方式:实现 Serializable/Parcelable 接口。不同点如图:
7. 如何解决 View 的事件冲突 ? 举个开发中遇到的例子?
参考回答:
常见开发中事件冲突的有 ScrollView 与 RecyclerView 的滑动冲突、RecyclerView 内嵌同时滑动同一方向。
滑动冲突的处理规则:
1.对于由于外部滑动和内部滑动方向不一致导致的滑动冲突,可以根据滑动的方向判断谁来拦截事件。
2.对于由于外部滑动方向和内部滑动方向一致导致的滑动冲突,可以根据业务需求,规定何时让外部 View 拦截事件,何时由内部 View 拦截事件。
3.对于上面两种情况的嵌套,相对复杂,可同样根据需求在业务上找到突破点。
滑动冲突的实现方法:
1.外部拦截法:指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,否则就不拦截。具体方法:需要重写父容器的 onInterceptTouchEvent 方法,在内部做出相应的拦截。
2.内部拦截法:指父容器不拦截任何事件,而将所有的事件都传递给子容器,如果子容器需要此事件就直接消耗,否则就交由父容器进行处理。具体方法:需要配合 requestDisallowInterceptTouchEvent 方法。
8. Looper 死循环为什么不会导致应用卡死?
参考回答:
主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生 ANR 异常。
造成 ANR 的不是主线程阻塞,而是主线程的 Looper 消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新 UI。
阻塞与程序无响应没有必然关系,虽然主线程在没有消息可处理的时候是阻塞的,但是只要保证有消息的时候能够立刻处理,程序是不会无响应的
9. Bitmap 如何处理大图,如一张 30M 的大图,如何预防 OOM?
参考回答:避免 OOM 的问题就需要对大图片的加载进行管理,主要通过缩放来减小图片的内存占用。
BitmapFactory 提供的加载图片的四类方法(decodeFile、decodeResource、decodeStream、decodeByteArray)都支持 BitmapFactory.Options 参数,通过 inSampleSize 参数就可以很方便地对一个图片进行采样缩放
比如一张 10241024 的高清图片来说。那么它占有的内存为 102410244,即 4MB,如果 inSampleSize 为 2,那么采样后的图片占用内存只有 512512*4,即 1MB(注意:根据最新的官方文档指出,inSampleSize 的取值应该总是为 2 的指数,即 1、2、4、8 等等,如果外界输入不足为 2 的指数,系统也会默认选择最接近 2 的指数代替,比如 2)
综合考虑。通过采样率即可有效加载图片,流程如下:
1.将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 true 并加载图片;
2.从 BitmapFactory.Options 中取出图片的原始宽高信息,它们对应 outWidth 和 outHeight 参数;
3.根据采样率的规则并结合目标 View 的所需大小计算出采样率 inSampleSize;
4.将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 false,重新加载图片;
10. 组件化中路由、埋点的实现?
参考回答:
因为在组件化中,各个业务模块之间是各自独立的, 并不会存在相互依赖的关系, 所以一个业务模块是访问不了其他业务模块的代码的, 如果想从 A 业务模块的 A 页面跳转到 B 业务模块的 B 页面, 光靠模块自身是不能实现的,这就需要一种跨组件通信方案—— 路由(Router)
路由
主要有以下两种场景
第一种是:组件之间的页面跳转 (Activity 到 Activity, Fragment 到 Fragment, Activity 到 Fragment, Fragment 到 Activity) 以及跳转时的数据传递 (基础数据类型和可序列化的自定义类类型)
第二种是:组件之间的自定义类和自定义方法的调用(组件向外提供服务)
其原理在于将分布在不同组件 module 中的某些类按照一定规则生成映射表(数据结构通常是 Map,Key 为一个字符串,Value 为类或对象),然后在需要用到的时候从映射表中根据字符串从映射表中取出类或对象,本质上是类的查找。
埋点则是在应用中特定的流程收集一些信息,用来跟踪应用使用的状况:
代码埋点: 在某个事件发生时调 SDK 里面相应的接口发送埋点数据,百度统计、友盟、TalkingData、Sensors Analytics 等第三方数据统计服务商大都采用这种方案。
全埋点: 全埋点指的是将 Web 页面/App 内产生的所有的、满足某个条件的行为,全部上报到后台服务器
可视化埋点: 通过可视化工具(例如 Mixpanel)配置采集节点,在 Android 端自动解析配置并上报埋点数据,从而实现所谓的自动埋点
评论