2024上半年百度Android岗(初级到高级)面试真题全收录+解析,备战金九银十!(中篇)

48 阅读18分钟

Android基础篇

1.Application

1.1、OnLowMemory 和 OnTrimMemory 的区别比较?

1、OnLowMemory 被回调时,已经没有后台进程;而 onTrimMemory 被回调时,还有后台 进程。 2、OnLowMemory 是在最后一个后台进程被杀时调用,一般情况是 low memory killer 杀进程后触发;而 OnTrimMemory 的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发。 3、通过一键清理后,OnLowMemory 不会被触发,而 OnTrimMemory 会被触发一次。OnTrimMemory 的参数是一个 int 数值,代表不同的内存状态: TRIM_MEMORY_COMPLETE:内存不足,并且该进程在后台进程列表最后一个,马上就要被 清理TRIM_MEMORY_MODERATE:内存不足,并且该进程在后台进程列表的中部。 TRIM_MEMORY_BACKGROUND:内存不足,并且该进程是后台进程。 TRIM_MEMORY_UI_HIDDEN:内存不足,并且该进程的 UI 已经不可见了。

1.2、Application 的生命周期

相比 Activity ,Application 的生命周期简直不要太简单。首先创建的时候会调用构造函数,然后系统准备好 ContextImpl 通过 attachBaseContext( Context ) 方法注入到 Application,接着调用我们最熟悉的 onCreate 方法。API 里还有一个 onTerminate 方法在进程被杀死的时候会回调,不过仅在模拟器生效,就不需要关注了。

1.3、说一下 Application 的初始化流程

Application 的初始化是在应用进程创建完成后:

ActivityThread 调用 AMS 的 Binder 对象( IActivityManager )的 attachApplication 方法 AMS 收到请求后再去调用 ActivityThread 的 bindApplication 方法 ActivityThread 这边收到请求再组装一个 AppBindData 对象,把所有参数封装进去,再通过 handler 发到主线程执行

主线程 loop 到这条消息,调用 handleBindApplication 来真正处理初始化 Application

handleBindApplication 和我们谈 “Context” 那次,Activity 的初始化差不多。回顾一下:

ClassLoader 加载 Application 类,实例化 初始化 Applicaction 用的 ContextImpl 通过 Application.attach( Context ) 方法,调用 attachBaseContext( Context ) 将 ContextImpl 注入到 Application 最后调用 Application.OnCreate() 这样 Application 就初始化完成了

2.Context

2.1、Context理解

1、Activity和Service以及Application的Context是不一样的,Activity继承自ContextThemeWraper.其他的继承自ContextWrapper。 2、每一个Activity和Service以及Application的Context是一个新的ContextImpl对象。 3、getApplication()用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。那也许在绝大多数情况下我们都是在Activity或者Servic中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法,getApplicationContext()比getApplication()方法的作用域会更广一些,任何一个Context的实例,只要调用getApplicationContext()方法都可以拿到我们的Application对象。 4、创建对话框时不可以用Application的context,只能用Activity的context。 5、Context的数量等于Activity的个数 + Service的个数 +1,这个1为Application。

2.2、ApplicationContext和ActivityContext的区别

这是两种不同的context,也是最常见的两种.第一种中context的生命周期与Application的生命周期相关的,context随着Application的销毁而销毁,伴随application的一生,与activity的生命周期无关.第二种中的context跟Activity的生命周期是相关的,但是对一个Application来说,Activity可以销毁几次,那么属于Activity的context就会销毁多次.至于用哪种context,得看应用场景。还有就是,在使用context的时候,小心内存泄露,防止内存泄露,注意一下几个方面:

  • 不要让生命周期长的对象引用activity context,即保证引用activity的对象要与activity本身生命周期是一样的。
  • 对于生命周期长的对象,可以使用application context。
  • 避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化。

3.Activity

3.1、Activity和Fragment生命周期有哪些?

3.2、横竖屏切换时候Activity的生命周期

1)、Android 3.2 (API 13) 之前:

  • 不设置 Activity 的 android:configChanges 时,切屏会重新调用生命周期,切横屏会调用一次,切竖屏>会调用两次。
  • 设置 Activity 的 android:configChanges="orientation" 时,切屏会重新调用生命周期,且横竖屏都是调>用一次生命周期。
  • 设置 Activity 的 android:configChanges="orientation|keyboardHidden" 时,切屏不会重新调用 Activity >的生命周期,但是会调用 onConfigurationChanges() 方法。

2)、从Android 3.2 (API 13) 开始

  • 不设置 Activity 的 android:configChanges 时、设置 Activity 的 android:configChanges="orientation"
  • 设置 Activity 的 android:configChanges="orientaion|keyboardHidden"时切换横屏和竖屏都会重新调用>一次生命周期。 *设置 Activity 的 android:configChanges="orientation|screenSize"时不会重新调用 Activity 的生命周期,>但是会调用 onConfigurationChanges() 方法。

3.3、activity的startActivity和context的startActivity区别?

(1)、从Activity中启动新的Activity时可以直接mContext.startActivity(intent)就好 (2)、如果从其他Context中启动Activity则必须给intent设置Flag:

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ; mContext.startActivity(intent);

3.4、怎么加速启动Activity?

1、onCreate() 中不执行耗时操作 把页面显示的 View 细分一下,放在 AsyncTask 里逐步显示,用 Handler 更好。这样用户的看到的就是有层次有步骤的一个个的 View 的展示,不会是先看到一个黑屏,然后一下显示所有 View。最好做成动画,效果更自然。 2、利用多线程的目的就是尽可能的减少 onCreate() 和 onReume() 的时间,使得用户能尽快看到页面,操作页面。 3、减少主线程阻塞时间。 4、提高 Adapter 和 AdapterView 的效率。 5、优化布局文件。

3.5、直接在Activity中创建一个thread跟在service中创建一个thread之间的区别?

  • 在Activity中被创建:该Thread的就是为这个Activity服务的,完成这个特定的Activity交代的任务,主动通知该Activity一些消息和事件,Activity销毁后,该Thread也没有存活的意义了。
  • 在Service中被创建:这是保证最长生命周期的Thread的唯一方式,只要整个Service不退出,Thread就可以一直在后台执行,一般在Service的onCreate()中创建,在onDestroy()中销毁。所以,在Service中创建的Thread,适合长期执行一些独立于APP的后台任务,比较常见的就是:在Service中保持与服务器端的长连接。

3.6、Activity 与 Service 通信的四种方式

1、Binder 2、Intent 3、接口 Interface 4、Broadcast 广播接收

3.7、Activity 之间的几种通信方式

1、Intent 2、借助类的静态变量 3、借助全局变量/Application 4、借助外部工具 5、 借助 SharedPreference 6、使用 Android 数据库 SQLite 7、 赤裸裸的使用 File 8、Android 剪切板 9、借助 Service

4.Service

4.1、服务启动一般有几种,服务和activty之间怎么通信,服务和服务之间怎么通信

方式: 1、startService: onCreate()--->onStartCommand() ---> onDestory() 如果服务已经开启,不会重复的执行onCreate(), 而是会调用onStartCommand()。一旦服务开启跟调用者(开启者)就没有任何关系了。 开启者退出了,开启者挂了,服务还在后台长期的运行。 开启者不能调用服务里面的方法。

2、bindService: onCreate() --->onBind()--->onunbind()--->onDestory() bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。 绑定者可以调用服务里面的方法。 通信: 1、通过Binder对象。 2、通过broadcast(广播)。

4.2、如何保证Service不被杀死?

Android 进程不死从3个层面入手:

A.提供进程优先级,降低进程被杀死的概率 方法一:监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。 方法二:启动前台service。 方法三:提升service优先级: 在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。

B. 在进程被杀死后,进行拉活 方法一:注册高频率广播接收器,唤起进程。如网络变化,解锁屏幕,开机等 方法二:双进程相互唤起。 方法三:依靠系统唤起。 方法四:onDestroy方法里重启service:service + broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service;

C. 依靠第三方 根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送;其他手机可以考虑接入腾讯信鸽或极光推送与小米推送做 A/B Test。

5.BroadcastReceiver

5.1、广播注册一般有几种,各有什么优缺点?

第一种是常驻型(静态注册):当应用程序关闭后如果有信息广播来,程序也会被系统调用,自己运行。 第二种不常驻(动态注册):广播会跟随程序的生命周期。 动态注册 优点: 在android的广播机制中,动态注册优先级高于静态注册优先级,因此在必要情况下,是需要动态注册广播接收者的。 缺点: 当用来注册的 Activity 关掉后,广播也就失效了。 静态注册 优点: 无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器就是打开着的。

6.Fragmengt

6.1、activty和Fragmengt之间怎么通信,Fragmengt和Fragmengt怎么通信?

(一)Handler (二)广播 (三)事件总线:EventBus、RxBus、Otto (四)接口回调 (五)Bundle和setArguments(bundle)

7.View

7.1、自定义view效率高于xml定义吗?说明理由。

自定义view效率高于xml定义: 1、少了解析xml。 2.、自定义View 减少了ViewGroup与View之间的测量,包括父量子,子量自身,子在父中位置摆放,当子view变化时,父的某些属性都会跟着变化。

7.2、ListView卡顿原因

Adapter的getView方法里面convertView没有使用setTag和getTag方式; 在getView方法里面ViewHolder初始化后的赋值或者是多个控件的显示状态和背景的显示没有优化好,抑或是里面含有复杂的计算和耗时操作; 在getView方法里面 inflate的row 嵌套太深(布局过于复杂)或者是布局里面有大图片或者背景所致; Adapter多余或者不合理的notifySetDataChanged; listview 被多层嵌套,多次的onMessure导致卡顿,如果多层嵌套无法避免,建议把listview的高和宽设置为match_parent. 如果是代码继承的listview,那么也请你别忘记为你的继承类添加上LayoutPrams,注意高和宽都mactch_parent的;

7.3、LinearLayout、FrameLayout、RelativeLayout性能对比,为什么?

RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,也会调用子 View 2次onMeasure RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。 在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout。

中场休息

8.数据传输与序列化

8.1Bunder传递对象为什么需要序列化?Serialzable和Parcelable的区别?

因为bundle传递数据时只支持基本数据类型,所以在传递对象时需要序列化转换成可存储或可传输的本质状态(字节流)。序列化后的对象可以在网络、IPC(比如启动另一个进程的Activity、Service和Reciver)之间进行传输,也可以存储到本地。 Serializable(Java自带): Serializable 是序列化的意思,表示将一个对象转换成存储或可传输的状态。序列化后的对象可以在网络上进传输,也可以存储到本地。 Parcelable(android专用): 除了Serializable之外,使用Parcelable也可以实现相同的效果,不过不同于将对象进行序列化,Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这也就实现传递对象的功能了。

8.2、android中有哪几种解析xml的类,官方推荐哪种?以及它们的原理和区别?

DOM解析 优点: 1.XML树在内存中完整存储,因此可以直接修改其数据结构. 2.可以通过该解析器随时访问XML树中的任何一个节点. 3.DOM解析器的API在使用上也相对比较简单. 缺点: 如果XML文档体积比较大时,将文档读入内存是非消耗系统资源的. 使用场景:

  • DOM 是与平台和语言无关的方式表示 XML文档的官方 W3C 标准.
  • DOM 是以层次结构组织的节点的集合.这个层次结构允许开人员在树中寻找特定信息.分析该结构通常需要加载整个文档和构造层次结构,然后才能进行任何工作.
  • DOM 是基于对象层次结构的.

SAX解析 优点: SAX 对内存的要求比较低,因为它让开发人员自己来决定所要处理的标签.特别是当开发人员只需要处理文档中包含的部分数据时,SAX 这种扩展能力得到了更好的体现. 缺点: 用SAX方式进行XML解析时,需要顺序执行,所以很难访问同一文档中的不同数据.此外,在基于该方式的解析编码程序也相对复杂. 使用场景: 对于含有数据量十分巨大,而又不用对文档的所有数据行遍历或者分析的时候,使用该方法十分有效.该方法不将整个文档读入内存,而只需读取到程序所需的文档标记处即可.

Xmlpull解析 android SDK提供了xmlpullapi,xmlpull和sax类似,是基于流(stream)操作文件,后者根据节点事件回调开发者编写的处理程序.因为是基于流的处理,因此xmlpull和sax都比较节约内存资源,不会像dom那样要把所有节点以对象树的形式展现在内存中.xmpull比sax更简明,而且不需要扫描完整个流.

9.Android进程

9.1、android中进程的优先级?

1.前台进程: 即与用户正在交互的Activity或者Activity用到的Service等,如果系统内存不足时前台进程是最晚被杀死的 2.可见进程: 可以是处于暂停状态(onPause)的Activity或者绑定在其上的Service,即被用户可见,但由于失了焦点而不能与用户交互 3.服务进程: 其中运行着使用startService方法启动的Service,虽然不被用户可见,但是却是用户关心的,例如用户正在非音乐界面听的音乐或者正在非下载页面下载的文件等;当系统要空间运行,前两者进程才会被终止 4.后台进程: 其中运行着执行onStop方法而停止的程序,但是却不是用户当前关心的,例如后台挂着的QQ,这时的进程系统一旦没了有内存就首先被杀死 5.空进程: 不包含任何应用程序的进程,这样的进程系统是一般不会让他存在的

9.2、Android中跨进程通讯的几种方式

1:访问其他应用程序的Activity 如调用系统通话应用 2:Content Provider 如访问系统相册 3:广播(Broadcast) 如显示系统时间 4:AIDL服务

9.3、为什么要用多进程?有哪些方式?怎么使用多进程

我们都知道,android 平台对应用都有内存限制,其实这个理解有点问题,应该是说 android 平台对每个进程有内存限制,比如某机型对对进程限制是 24m,如果应用有两个进程,则该 应该的总内存限制是 2*24m。使用多进程就可以使得我们一个 apk 所使用的内存限制加大 几倍。所以可以借此图片平台对应用的内存限制,比如一些要对图片、视频、大文件进程处理的好 内存的应用可以考虑用多进程来解决应用操作不流畅问题。

开启多进程模式: 在 Android 中 使 用多 进程 只 有 一 种方 法,那 就是 在 AndroidManifest 中 给 四 大 组件(Activity,Service,Receiver,ContentProvider)指定 android:process 属性.除此之外没有其他的办法,也就是说我们无法给一个线程活一个实体类指定其运行时所在的进程.其实还有另一种非常规的多进程方法,那就是通过 JNI 在 native 层去 fork 一个新的进程,但这种方法属于特殊情况,并不是常用的创建多进程的方式,所以我们也暂不考虑这种情况进程名以":"开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中,而进程名不以":"开头的进程属于全局进程,其他应用通过 ShareUID 方式可以和它跑在同一个进程中.用多进程的好处与坏处

好处 1)分担主进程的内存压力。 当应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的 内存空间了。当然还有其他好处,有心人会发现 2)使应用常驻后台,防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方 被杀就重新启动它。Android 后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。 典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情, 或者开机启动这个进程,然后做监听等。

坏处:消耗用户的电量。 多占用了系统的空间,若所有应用都这样占用,系统内存很容易占满而导致卡顿。 应用程序架构会变得复杂,因为要处理多进程之间的通信。这里又是另外一个问题了。 多进程的缺陷进程间的内存空间是不可见的。开启多进程后,会引发以下问题: 1)Application 的多次重建。 2)静态成员的失效。 3)文件共享问题。 4)断点调试问题。

10.Android各版本新特性

10.1、Android5.0新特性

1.MaterialDesign设计风格 2.支持64位ART虚拟机(5.0推出的ART虚拟机,在5.0之前都是Dalvik。他们的区别是: Dalvik,每次运行,字节码都需要通过即时编译器转换成机器码(JIT)。 ART,第一次安装应用的时候,字节码就会预先编译成机器码(AOT)) 3.通知详情可以用户自己设计

10.2、Android6.0新特性

1.动态权限管理 2.支持快速充电的切换 3.支持文件夹拖拽应用 4.相机新增专业模式

10.3、Android7.0新特性

1.多窗口支持 2.V2签名 3.增强的Java8语言模式 4.夜间模式

10.4、Android8.0(O)新特性

1.通知渠道 (Notification Channel) 通知标志 休眠 通知超时 通知设置 通知清除 2.画中画模式:清单中Activity设置android:supportsPictureInPicture 3.后台限制 4.自动填充框架 5.系统优化 6.等等优化很多

10.5、Android9.0(P)新特性

1.室内WIFI定位 2.“刘海”屏幕支持 3.安全增强

10.6、Android10.0新特性

夜间模式:包括手机上的所有应用都可以为其设置暗黑模式。 桌面模式:提供类似于PC的体验,但是远远不能代替PC。 屏幕录制:通过长按“电源”菜单中的"屏幕快照"来开启。

11.Bitmap

11.1、Bitmap 使用时候注意什么?

1、要选择合适的图片规格(bitmap类型) 2、降低采样率。BitmapFactory.Options 参数inSampleSize的使用,先把options.inJustDecodeBounds设为true,只是去读取图片的大小,在拿到图片的大小之后和要显示的大小做比较通过calculateInSampleSize()函数计算inSampleSize的具体值,得到值之后。options.inJustDecodeBounds设为false读图片资源。 3、复用内存。即,通过软引用(内存不够的时候才会回收掉),复用内存块,不需要再重新给这个bitmap申请一块新的内存,避免了一次内存的分配和回收,从而改善了运行效率。 4、使用recycle()方法及时回收内存。 5、压缩图片。

11.2、如何计算一个Bitmap占用内存的大小,怎么保证加载Bitmap不产生内存溢出?

在Bitmap里有两个获取内存占用大小的方法。 (1)getByteCount():API12 加入,代表存储 Bitmap 的像素需要的最少内存。 getAllocationByteCount():API19 加入,代表在内存中为 Bitmap 分配的内存大小,代替了 getByteCount() 方法。 在不复用 Bitmap 时,getByteCount() 和 getAllocationByteCount 返回的结果是一样的。在通过复用 Bitmap 来解码图片时,那么 getByteCount() 表示新解码图片占用内存的大 小,getAllocationByteCount() 表示被复用 Bitmap 真实占用的内存大小(即 mBuffer 的长度)。 (2)BitmapFactory.Options.inPreferredConfig:将ARGB_8888改为RGB_565,改变编码方式,节约内存。 BitmapFactory.Options.inSampleSize:缩放比例,可以参考Luban那个库,根据图片宽高计算出合适的缩放比例。 BitmapFactory.Options.inPurgeable:让系统可以内存不足时回收内存。