编程人 cdmana.com

10道阿里Android岗必问题摆这儿了,你爱刷不刷!(附参考回答解析)

敲黑板!送分题呀,同学们。10道面试阿里Android岗面试官必问的面试题,来源于CSDN及牛客网等平台上的一些去阿里面试的同学的高赞真实面经分享,将其高频面试题整合下来,并在我大厂架构师朋友的帮助下,做出了对应的参考回答及解析。废话不多说,大家自己来试试,不看回答参考的话,你会怎么回答?

1. 横竖屏切换的Activity生命周期变化?

参考回答:
不设置Activity的android:configChanges时,切屏会销毁当前Activity,然后重新加载调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次;onPause()→onStop()→onDestory()→onCreate()→onStart()→onResume()设置Activity的android:configChanges=“orientation”,经过机型测试

  • 在Android5.1 即API 23级别下,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次 - 在Android9 即API 28级别下,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法后经官方查正,原话如下:
  • 如果您的应用面向Android 3.2即API 级别 13或更高级别(按照 minSdkVersion 和 targetSdkVersion 属性所声明的级别),则还应声明 “screenSize” 配置,因为当设备在横向与纵向之间切换时, 该配置也会发生变化。即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重新启动 Activity
  • 设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时,机型测试通过,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法;

2. Fragment中add与replace的区别(Fragment重叠)

参考回答:

  • add不会重新初始化fragment,replace每次都会。所以如果在fragment生命周期内获取获取数据,使用replace会重复获取;
  • 添加相同的fragment时,replace不会有任何变化,add会报IllegalStateException异常;
  • replace先remove掉相同id的所有fragment,然后在add当前的这个fragment,而add是覆盖前一个fragment。所以如果使用add一般会伴随hide()和show(),避免布局重叠;
  • 使用add,如果应用放在后台,或以其他方式被系统销毁,再打开时,hide()中引用的fragment会销毁,所以依然会出现布局重叠bug,可以使用replace或使用add时,添加一个tag参数;

3. 如何保证Service不被杀死 ?

参考回答:
3.1 onStartCommand方式中,返回START_STICKY或则START_REDELIVER_INTENT

  • START_STICKY: 如果返回START_STICKY,表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象
  • START_NOT_STICKY: 如果返回START_NOT_STICKY,表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service
  • START_REDELIVER_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端自动解析配置并上报埋点数据,从而实现所谓的自动埋点
    无埋点: 它并不是真正的不需要埋点,而是Android端自动采集全部事件并上报埋点数据,在后端数据计算时过滤出有用数据。



以上就是面试阿里,阿里面试官10道最常问的面试题。另外,在收集整理这些内容的时候,顺便也将其他一些常见的大厂面试真题做了收集归纳和答案解析,如果有需要的朋友,可以顺手点赞+评论文章后私信我,获取领取方式!





最后还有一个面试小技巧:“如何引导面试官提问?”。 一般面试官在面试的候选者的时候,都会先将候选者的简历先过一遍,然后习惯性的根据候选者简历中体现的一些技术点,来由浅到深的提问,所以我们在书写简历的时候,可以将自己一些擅长的技术点,体现到自己的项目经验或者其它内容当中去,让被动的面试,变得相对主动起来。

Tags 面试 sticky
Scroll to Top