预备知识
1. Intent
Intent
是信使,负责完成Android
四大组件之间的信息传递,同类、不同类的组件无法直接传递对象,一旦需要沟通只能通过Intent
(不建议通过静态变量或静态方法传递数据,容易造成数据异常、内存泄露等问题)。
2. 生命周期
生命周期是指组件的实例对象从创建到销毁可能会被系统调用的一些方法,每个方法的调用都有特定的条件,可以根据需要重写生命周期方法来达到在某些特定时刻执行特定任务的目的。生命周期方法不建议自行调用,应由系统管理。
3. 注册组件
四大组件都需要通过项目中的AndroidManifest.xml
文件进行静态注册后才可正常使用,其中BroadcastReceiver
可以在其他组件中动态注册(Android
自6.0
版本开始限制静态注册,除少部分广播如开机广播之外,大部分广播的静态注册是无效的)。
4. 响应时间
应用主线程未在规定的时间内执行完任务,系统会报ANR(应用程序无响应)错误,因此应将耗时任务交由子线程完成,具体的限定时间和执行位置如下:
- Activity:5秒内执行完成,包括各个生命周期方法、View的触摸点击事件、输入输出操作等。
- Service:20秒内执行完成,主要是各个生命周期方法。
- BroadcastReceiver:10秒内执行完成,主要是onReceive()方法。
1. Activity
1.1 基本知识
概念
Activity
直译为“活动”,在应用中的一个Activity
可以理解为一个界面,可以包含各类布局与控件展现丰富的可视化效果,主要用于和用户进行交互。一个应用必须通过Activity
来启动和运行,一个应用可以只有一个Activity
,也可以由多个Activity
组成。
应用中的所有Activity
都被系统保存在TaskStack
任务栈中,一般一个应用只有一个TaskStack
任务栈,但也可以调整Activity
的启动模式来开启多个TaskStack
任务栈。既然叫栈,自然是有栈的特性,当前可交互的Activity
在栈顶,先启动的Activity
在栈底,栈顶Activity
出栈则底层Activity
被用户可见并开始交互,任务栈清空时应用结束。
作用
Activity
是应用最直观的入口,一个应用可以没有其他组件,但是不能没有Activity
。用户的IO操作都由Activity
进行处理,应用的数据展示、吸引人的动画、优秀的界面设计等都需要Activity
进行展示,用户只关心看得见的东西,看不见效果再好的应用都没用户量,再牛逼的应用最少也得有文字展示一下不是吗?
生命周期
Activity
的生命周期已经是翻烂的书了,最基础的知识点,千万不可忽视。首先就得上个图:
生命周期大多是成对出现的:
- onCreate()和onDestroy(),生命的起始与终结。
- onStart()和onStop(),界面的可见与否。
- onResume()和onPause(),是否可进行交互。
详细介绍:
- onCreate():第一个调用的方法,通常在该方法中加载布局文件、初始化资源、注册事件与
BroadcastReceiver
等较重的任务。 - onStart():在
onCreate()
方法后调用,通常在该方法中处理UI的显示控制逻辑,任务不可过重,用户此时无法交互也无法直接看见当前Activity
。 - onResume():在
onStart()
方法后调用,通常在该方法中进行简单的数据处理与UI控制,如开始动画,只执行轻量任务,方法执行后用户可进行交互并可直接看见当前Activity
。此时,当前Activity
进入Resumed
状态(运行状态),当一个Paused
状态的Activity
重新位于栈顶时(从其他Activity
返回),会再次调用该方法。 - onPause():当前
Activity
被其他Activity
覆盖时调用,让当前Activity
进入Paused
状态(暂停状态),此时当前Activity
可见但不可交互。通常在此方法中进行简单的数据处理与UI控制,如暂停动画,只执行轻量任务。内存不足且无其他优先级更低的Activity
时,当前Activity
可能会被销毁。当前Activity
重新位于栈顶时(从其他Activity
返回),会调用onResume()
方法。 - onStop(): 当前
Activity
被其他Activity
完全覆盖(若上层Activity
透明或半透明时,只调用onPause()
方法)时调用,当前Activity
进入Stopped
状态(停止状态),当前Activity
不可见。通常在此方法中处理UI的显示控制逻辑,任务不可过重。内存不足且无其他优先级更低的Activity
时,当前Activity
可能会被销毁。还行当前Activity
重新位于栈顶时(从其他Activity
返回),会调用onRestart()
方法。 - onRestat(): 当一个
Stopped
状态的Activity
被返回时,该方法被调用,之后再调用onStart()
方法和onResume()
方法进入运行状态。 - onDestroy(): 当前
Activity
被销毁时调用,通常在该方法中用来执行释放资源等较重的任务。
启动
启动模式
- Standard:标准模式,
Activity
的默认启动模式。在这种模式下,Activity
可以有多个实例,每次启动Activity
,无论任务栈中是否已经存在这个Activity
的实例,系统都会创建一个新的Activity
实例。 - SingleTop:栈顶模式,栈顶
Activity
复用的模式。当一个SingleTop
模式的Activity
已经位于栈顶时,再去启动它则不再创建新的实例;如果不在栈顶,无论任务栈中是否已经存在这个Activity
的实例,系统都会创建一个新的Activity
实例。 - SingleTask:单任务模式,栈内
Activity
复用的模式。如果启动的Activity
已经存在于任务栈中,则会将此Activity
上层的其他Activity
出栈,使此Activity
位于栈顶;否则创建一个新的Activity
实例。 - SingleInstance:单实例模式。在该模式下,系统会启动一个新的栈来管理该
Activity
,不同应用之间可以共享该Activity
。
Activity跳转
- 显式启动:
Intent
直接指定要启动的Activity
所对应的的class
,可以通过Intent
构造方法、setClass
、setClassName
三种方式指定。 - 隐式启动:
Intent
指定匹配条件,由Activity
注册在AndroidManifest.xml
文件中的intent-filter
节点进行匹配,条件有action
、category
、data
三种,全部或者部分匹配都可以。
1.2 疑难点
两个Activity跳转时的生命周期
常见的场景就是两个Activity跳转,如A跳转到B,此时A和B的生命周期交织在一起,并不是简单的A执行完所有的生命周期再从头执行B的生命周期。
- 当B无实例,且不是透明或半透明时:A.onPause() > B.onCreate() > B.onStart() > B.onResume() > A.onStop()。
此时从B返回A:B.onPause() > A.onRestart() > A.onStart() > A.onResume() > B.onStop() > B.onDestroy()。 - 当B无实例,且为透明或半透明时:A.onPause() > B.onCreate() > B.onStart() > B.onResume()。
此时从B返回A:B.onPause() > A.onResume() > B.onStop() > B.onDestroy()。
透明的B将导致A被B覆盖时不执行A.onStop()方法,B返回A时也不执行A.onRestart() > A.onStart()这两个方法。接下来的就不考虑透明的情况了,举一反三。
- 当B有实例,B为SingleTop模式,且B位于栈顶(B不位于栈顶时相当于无实例,参考1、2两点):此时A不在栈顶,其生命周期并不执行,B无生命周期变化,但系统会调用B.onNewIntent()方法告知B接收到了新的Intent。
- 当B有实例,B为SingleTask模式:A.onPause() > B.onRestart() > B.onStart() > B.onNewIntent() > B.onResume()(onNewIntent()与onResume()无确定先后顺序,位置可能交换)> A.onStop() > 原本栈内位于B之上的Activity进入销毁流程。
1.3 面试常考
说一下Activity的生命周期 / 各个生命周期的作用 ?
考察基础,必背知识点。答案见上
onStart()和onResume()/onPause()和onStop()的区别?
是否位于前台,对用户是否可见的区别。
Activity A启动另一个Activity B会回调哪些方法?如果Activity B是完全透明呢?如果启动的是一个Dialog呢?
参考本文《1.2 疑难点 - 两个Activity跳转时的生命周期》,对话框则不会调用生命周期方法。
如何将一个 Activity 设置成窗口的样式?
只需要给我们的 Activity 配置如下属性即可。
android:theme="@android:style/Theme.Dialog"
谈谈onSaveInstanceState()方法?何时会调用?
当Activity意外销毁时再重新创建时会调用此方法,比如横竖屏切换,会导致重新创建Activity,onSaveInstanceState()方法的调用在onStop()之前,用于保存当前Activity的状态,当Activity被重新创建后,会调用onRestoreInstanceState()来恢复Activity的状态,onRestoreInstanceState()的调用在onStart()之前。
onSaveInstanceState()被执行的场景有哪些:
系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity是否会被销毁,因此系统都会调用onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则:
- 当用户按下HOME键时
- 长按HOME键,选择运行其他的程序时
- 锁屏时
- 从activity A中启动一个新的activity时
- 屏幕方向切换时
onSaveInstanceState()与onPause()的区别?
两者执行没有固定的先后顺序。
onSavedinstance()方法的触发时机,其典型的情景是按Home键或者切换Activity,这样的Activity可能被销毁的场合,但是按Back键退出程序,则不会调用此方法,适合保存一些非持久性的数据(即程序运行期间需要储存的数据)。
而onPause(),不管是可能销毁还是退出程序,都必须调用,适合保存持久性的数据,但是Android本身没有为此方法提供Bundle参数,因此我们可以选用做一个静态变量或者是提供一个SharedPreference作为数据载体。
如何避免配置改变 / 横竖屏切换时Activity重建?
在清单文件下每个activity注册时写上android:configChanges=“XXX”
比如横竖屏切换:android:configChanges=“orientation”
优先级低的Activity在内存不足被回收后怎样做可以恢复到销毁前状态?
- Task和Activity栈以及相应的Intent和数据会被系统保存起来。当APP被切回前台时,系统会恢复Task和Activity栈以及相应的Intent和数据。Activity之间传数据应该用系统提供的intent机制。
- 不要在Application类和全局单例类中存放数据,会导致app无法正确恢复状态。运行时的临时数据应存放在SharedPreference、临时文件或数据库中。
- 充分利用onSaveInstanceState()和onRestoreInstanceState()方法,当Activity被重新创建后方便恢复Activity的状态。
说下Activity的四种启动模式?(有时会出个实际问题来分析返回栈中Activity的情况)
参考本文《1.1 基本知识 - 启动》,《1.2 疑难点 - 两个Activity跳转时的生命周期》。
谈谈singleTop和singleTask的区别以及应用场景?onNewIntent()调用时机?
参考本文《1.1 基本知识 - 启动》,《1.2 疑难点 - 两个Activity跳转时的生命周期》。
了解哪些Activity启动模式的标记位?
Intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//指定singleTask模式,与在AndroidManifest.xml中指定android:launchMode"singleTask"效果相同
Intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//指定singleTop模式,与在AndroidManifest.xml中指定android:launchMode"singleTop"效果相同
Intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//具有这个标志的Activity启动时,在同一个任务栈中所以位于它上面的Activity都要出栈,一般会和singleTask模式一起出现
Intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
//具有这个标志的Activity不会出现在历史的Activity列表中,它等同于在AndroidManifest.xml中指定android:excludeFromRecents=“true”
标记位优先级比在AndroidManifest中指定优先级高
如何启动其他应用的Activity?
SingleTask和taskAfiinity配合使用。
谈一谈Fragment的生命周期?
Activity和Fragment的异同?
1、从最基础的开始说—>生命周期
Activity有7个生命周期:onCreate(); onStart(); onResume(); onPause(); onStop(); onDestroy(); onRestart();
Fragment有11个生命周期:onAttach(); onCreate(); onCreateView(); onActivityCreate(); onStart(); onResume(); onPause(); onStop(); onDestroyView(); onDestroy(); onDetach();
所以Fragment比较与Activity来说会更加灵活,因为生命周期多了,你可以控制的地方也就多了。
2、从灵活性上来说
Activity是四大组件之一,是每个页面的承载,一个就是一个,Fragment的显示要依赖于Activity,从Fragment的生命周期中就可以了解到。
Fragment是一个一个的小碎片
(1)相比较与Activity来说更加灵活,可以在XML文件中直接进行写入,也可以在Activity中动态添加;
(2)可以使用show()/hide()或者replace()随时对Fragment进行切换,并且切换的时候不会出现明显的效果,用户体验会好;Activity虽然也可以进行切换,但是Activity之间切换会有明显的翻页或者其他的效果,在小部分内容的切换上给用户的感觉不是很好
Activity和Fragment的关系?
Fragment的显示要依赖于Activity,从Fragment的生命周期中就可以了解到。
何时会考虑使用Fragment?
类似微信下方菜单栏,以及手机和平板适配等。
2. Service
2.1 基本知识
概念
Service直译为“服务”,主要用于在后台处理一些耗时的逻辑,或者执行一些需要长期运行的任务。必要的时候,甚至可以让服务在程序退出的情况下继续运行。
作用
承担大部分的数据处理工作,为其他组件提供繁重耗时的后台服务,可监控其他组件的运行状态。
生命周期
Service的生命周期得分两种情况,根据Service的启动方式分为Unbounded和Bounded两种,如下图:
- Unbounded是Service被startService()方法启动时经历的生命周期,Service不处于活动状态时,经历onCreate() > onStartCommand()流程进入活动状态,此时再次启动Service只调用onStartCommand()方法。
- Bounded是Service被bindService()方法启动时经历的生命周期,Service不处于绑定状态时,经历onCreate() > onBind()流程进入绑定状态,此时再次绑定Service只调用onBind()方法。
启动
Service的启动方式有两种:startService()和bindService()。
- 使用startService()方法启动Service,调用者与Service之间没有关系,即使调用者退出了,Service仍然运行。Service不会自动销毁,需要外部调用stopService()方法或在Service内部调用stopSelf()方法,此时Service的onDestroy()方法被调用。
- 使用bindService()方法启动Service,调用者与Service绑定在了一起,调用者一旦销毁,Service也就终止了,调用者需要解绑时可调用unBindService()方法。Service被解绑或调用者销毁时,Service经历onUnbind() > onDestroy()的过程。
2.2 疑难点
Service超时ANR/启动子线程
通常出现ANR时,应用在主线程执行了较重的耗时任务,所以需要将耗时任务交给子线程进行。Service面临需要启动子线程的情况时,直接使用多线程也是可以的,但是如果Service必然是需要子线程的情况,建议使用IntentService完成任务。
IntentService是Service的子类,它和Service的不同点在于:
- 内部有一个工作线程来完成耗时的操作,只需实现onHandleIntent方法即可。
- 完成全部工作任务后,会自动终止服务。
- 如果同时执行多个任务时,会以工作队列的方式依次执行。
IntentService非常适合用于处理应用中的耗时任务,当然如果需要并行执行还是考虑线程池,建议将应用中可串行执行的耗时任务都交由IntentService来完成。
2.3 面试常考
谈一谈Service的生命周期?
参考本文《2.1 基本知识 - 生命周期》。
Service的两种启动方式?区别在哪?
参考本文《2.1 基本知识 - 生命周期》,《2.1 基本知识 - 启动》。
一个Activty先start一个Service后,再bind时会回调什么方法?此时如何做才能回调Service的destory()方法?
如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。
Service如何和Activity进行通信?
- Binder:
通过 Binder 接口的形式实现,当 Activity 绑定 Service 成功的时候 Activity 会在 ServiceConnection 的类 的 onServiceConnected()回调方法中获取到 Service 的 onBind()方法 return 过来的 Binder 的子类,然后通过对象调用方法。 - Aidl:
aidl 比较适合当客户端和服务端不在同一个应用下的场景。 - Messenger:
它引用了一个Handler对象,以便others能够向它发送消息(使用mMessenger.send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Message进行通信),在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。一个Messeger不能同时双向发送,两个就就能双向发送了。 - 广播,具体逻辑同3.。
用过哪些系统Service?
如进行窗口相关的操作会用到窗口管理服务WindowManager,进行电源相关的操作会用到电源管理服务PowerManager,还有很多其他的系统管理服务,如通知管理服务NotifacationManager、振动管理服务Vibrator、电池管理服务BatteryManager……
是否能在Service进行耗时操作/网络操作?如果非要可以怎么做?
Service是运行在主线程中的,一般不能在Service进行耗时操作。参考本文《2.2 疑难点 - Service超时ANR/启动子线程》。
IntentService与Service的区别(intentservice的优点)
- 内部有一个工作线程来完成耗时的操作,只需实现onHandleIntent方法即可,其他方法提供默认实现。
- 完成全部工作任务后,会自动终止服务。
- 如果同时执行多个任务时,会以工作队列的方式依次执行。
前台服务是什么?和普通服务的不同?如何去开启一个前台服务?
前台服务是那些被认为用户知道(用户所认可的)且在系统内存不足的时候不允许系统杀死的服务。
前台服务必须给状态栏提供一个通知,前台服务的系统优先级更高、不易被回收。
服务启动时,创建一个Notification,再调用setForground()方法。
是否了解ActivityManagerService,谈谈它发挥什么作用?
ActivityManagerService(简称AMS)是Android中最核心的服务 , 主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块类似。
Activity、Intent、Service 是什么关系?
Activity 和 Service 都是 Android 四大组件之一。Activity 负责用户界面的显示和交互,Service 负责后台任务的处理。Activity 和 Service 之间可以通过 Intent 传递数据。
Service 和 Activity 在同一个线程吗?
对于同一 app 来说默认情况下是在同一个线程中的,都在main Thread (UI Thread)。
如何保证Service不被杀死?
- 在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = “1000”这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时实用于广播。
- 在onStartCommand里面调用 startForeground()方法把Service提升为前台进程级别,然后再onDestroy里面要记得调用stopForeground ()方法。
- onStartCommand方法,手动返回START_STICKY。
- 在onDestroy方法里发广播重启service。service +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service。(第三方应用或是在setting里-应用-强制停止时,APP进程就直接被干掉了,onDestroy方法都进不来,所以无法保证会执行)可以通过双Service互相唤起,提高成功率。
- 监听系统广播判断Service状态。
通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活。 - Application加上Persistent属性。
Service 的 onStartCommand 方法有几种返回值?各代表什么意思?
有四种返回值,不同值代表的意思如下:
- START_STICKY:如果 service 进程被 kill 掉,保留 service 的状态为开始状态,但不保留递送的 intent 对象。随 后系统会尝试重新创建 service,由于服务状态为开始状态,所以创建服务后一定会调用 onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到 service,那么参数 Intent 将为 null。
- START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异常 kill 掉,系统不会自动重启该服务。
- START_REDELIVER_INTENT:重传 Intent。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异 常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。
- START_STICKY_COMPATIBILITY: START_STICKY 的兼容版本,但不保证服务被 kill 后一定能重启。
Service 的 onRebind(Intent)方法在什么情况下会执行?
在 onUnbind()方法返回 true 的情况下会执行。
3. BroadcastReceiver
3.1 基本知识
概念
BroadcastReceiver直译为"广播接收者”,主要用来接收来自系统和应用中的广播。在Android系统中,广播体现在方方面面,例如开机广播、网络状态广播、电池电量广播等等。
作用
广播可以用于异步处理消息,此时作用类似于Handler,比Handler更强大的是广播可以在整个设备内互通消息,基于这个特性可以用来解决进程间通信的问题。
生命周期
广播的生命周期从调用开始到onReceive()执行完毕结束,需要注意的是,一般广播的生命周期都极短,需要在10秒内处理完onReceive()中的所有工作,所以,一般不进行耗时长的工作,如果有耗时长的工作,应当通过Intent传递给Service进行处理。
注意,不要在onReceive()中开启线程进行耗时任务处理,否则,在10s后,该线程会变成空线程,从而导致任务的丢失。同样的,也不要使用bindService来绑定服务。
启动
BroadcastReceiver的启动过程要从注册说起了:
- 静态注册:静态注册的BroadcastReceiver在应用启动时自动创建一个实例,应用销毁时自动销毁,完全由系统控制,无需过多关注。
- 动态注册:BroadcastReceiver通常在Activity中通过registerReceiver()方法动态注册,此时应在Activity销毁之前调用unregisterReceiver()方法注销。
BroadcastReceiver将在收到对应广播时自动启动,执行完onReceive()方法后自动结束,生命周期短暂而有效。
3.2 疑难点
3.3 面试常考
广播的两种注册形式?区别在哪?
广播接收者的注册有两种方法,分别是程序动态注册和AndroidManifest文件中进行静态注册。
动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。
如何判断当前BroadcastReceiver接受到的是有序广播还是无序广播?
在BroadcastReceiver类中onReceive()方法中,可以调用boolean b=isOrderedBroadcast();该方法是BroadcastReceiver类中提供的方法,用于判断我们当前的接收到的广播是否为有序广播。
4. ContentProvider
4.1 基本知识
概念
ContentProvider直译为“内容提供者”,主要用于共享本应用的数据给其他应用使用,根据此特性也可将ContentProvider用于进程间通信。提供数据的应用注册ContentProvider,获取数据的应用创建ContentResolver获取对应ContentProvider的数据。
作用
Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。开发人员通常不会使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。
生命周期
ContentProvider的生命周期主要就是类似数据库的CRUD,ContentResolver也是类似的提供了相应的方法调用。
启动
ContentProvider并不通过实例使用,一般通过Context的getContentResolver()方法获得ContentResolver实例,ContentResolver提供了与ContentProvider一致的方法,可通过URI唤起相应的ContentProvider对提供的数据进行CRUD操作。
4.2 疑难点
4.3 面试常考
ContentProvider了解多少?
(1)android平台提供了ContentProvider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。
(2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。
(3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。
(4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。
(5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。
参考文章:
Android面试题(四大组件篇)
Android 面试题之四大组件相关
来源:oschina
链接:https://my.oschina.net/u/4409432/blog/3224802