Android面试必问四大组件之Activity
四大组件是Android面试过程中问得最多的知识点,在实际开发中也是无处不在的,因此必须要掌握的重要知识点之一。
Android的四大组件分别是活动(Activity)、服务(Service)、广播接收器(BroadcastReceiver)和内容提供器(ContentProvider)。
活动:点击一个App进去用户能够看到的界面组件并且与用户进行交互。
服务:在后台一直运行,甚至应用退出后也能继续运行。
广播接收器:可以接受系统和其他应用发送古来的广播信息,也可发送广播。
内容提供器:应用之间进行数据交互的桥梁,为数据提供了供外部访问的各种接口。
活动(Activity)
关于活动的面试题五花八门的,但是离不开这3个方面:生命周期、启动模式和碎片。
1. 生命周期
- 当用户点击应用图标进入应用时,Activity启动,生命周期从开始一直到结束:
- onCreate():Activity第一次被创建的时候调用,用于初始化操作,加载布局、组件和绑定事件等。
- onStart():Activity创建后由不可见变成可见状态,但是还没出现在前台,不可交互。
- onResume():处于运行状态(Activity位于工作栈栈顶),出现在前台,可与用户进行交互。
- onPause():Actitivy并不是完全不可见,但是不可交互,可以做一些简单的存储数据和停止动画的操作,不能太耗时,onPause执行完,新的Activity的onResume才能执行。
- onStop():Activity即将处于完全不可见的状态,可以做一些稍微重量级的回收工作,也不能太耗时。
- onDestroy:销毁前调用,调用之后Activity就销毁了,在这里可以做一些回收工作和最终的资源释放。
- onRestart():重新启动时调用,由从不可见重新变为可见继续运行。这种情形一般是用户行为导致,比如按下Home键切换到桌面或重新打开一个Activity,当前Activity就会暂停,onPause或者onStop,重新回到这个Activity就会onRestart。
有些抽象?云里雾里?直接上图:
一些情况的说明:
- 一个Activity第一次启动,回调方法:onCreate() -> onStart() -> onResume()
- 打开一个新Activity或者切换到桌面:onPause() -> onStop(),如果新Activity采用的是透明主题,不会回调onStop()
- 用户再次回到原Activity:onRestart() -> onStart() -> onResume()
- 用户按back键回退:onPause() -> onStop() -> onDestroy()
- Activity被系统回收后再次打开,生命周期回调的方法和原来一样
- 对于整个生命周期来说,onStart和onDestroy是一对,标志着Activity的创建和销毁,只可被回调一次;从Activity是否可见来看,onStart和onStop是一对,随着用户操作或者设备的点亮和熄灭可被多次调用。
两个问题:
- onStart和onResume,onPause和onStop有什么不一样?
onStart和onStop是从Activity是否可见的角度来回调的,onResume和onPause是从Activity是否位于前台来回调的。除此之外,没什么区别。 - 假设当前Activity为A,如果这时用户打开一个新ActivityB,那么B的onResume和A的onPause那个先执行呢?
A的onPause执行之后,B的onResume才能执行。
Activity的启动过程源码相当复杂,简单理解,启动Activity的请求会由Instrumentation来处理,然后它通过Binder向AMS(ActivityManagerService)发请求,AMS内部维护这一个ActivityStack并负责栈内的Activity的状态同步,AMS通过ActivityThread去同步Activity的状态从而完成生命周期方法的调用。
一些特殊情况:
-
资源相关的系统配置发生改变导致Activity被杀死并重新创建(例如横竖屏切换)
在默认情况下不对Activity做特殊处理,Activity会被销毁并重新创建。
系统配置发生改变,Activity的onPause、onStop、onDestroy都会被调用,系统调用onSaveInstanceState保存当前Activity的状态,在onStop之前调用,和onPause无顺序关系,Activity被重建之后会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数传递给onRestoreInstanceState和onCreate方法。可以通过onRestoreInstanceState方法里是否有数据判断Activity是否被重建。
生命周期:onPause->onStop(onSaveInstanceState保存数据)->onDestroy->onCreate(重新加载保存的数据)->onStart->onResume -
资源内存不足导致低优先级的Activity被杀死
生命周期会和上一种情况一样。
Activity按照优先级分为三种:
(1) 前台Activity——正在和用户交互的Activity,优先级最高;
(2) 可见但是非前台Activity——弹出一个对话框,Activity可见但不可交互;
(3) 后台Activity——已经被暂停的Activity,优先级最低。
如果资源配置发生改变,我们不想系统创新创建Activity,可以给Activity指定configChanges属性。
题外话:Android实现屏幕旋转的方法总结:
2. 启动模式(LaunchMode)
启动模式的意思就是一个Activity是以怎样的一种启动方式来跳转到当前页。
四种启动模式:standard、singleTop、singleTask和singleInstance。
设置启动模式有两种方式:
(1) 在AndroidManifest.xml文件中用android:launchMode来指定。(常用)
(2) 通过Intent中设置标志位来指定。
什么是任务栈?
任务栈即Task,它是一种用来保存和管理Activity的数据结构,也是所有Activity的集合。
(1) standard
标准模式,默认的启动模式。
每次启动一个Activity,都会创建一个实例,Activity入栈,处于栈顶的位置。
(2) singleTop
栈顶复用模式。启动Activity时,系统会先检查任务栈栈顶是不是该Activity的实例,如果是则直接使用,调用onNewIntent()方法,不是则创建新的实例位于栈顶。
(3) singleTask
栈内复用模式。这是一种单实例模式。多说一嘴:Activity需要的任务栈是通过taskAffinity属性指定的。
① 如果当前任务栈S1中有ABC三个Activity,Activity D以S2任务栈singleTask模式请求启动,创建S2任务栈并将D入栈;
② 如果S1中有ABC三个Activity,D以S1任务栈singleTask模式请求启动,直接D入栈,S1中有ABCD;
③ 如果任务栈S1中有ADBC,D以S1任务栈singleTask模式请求启动,singleTask具有clearTop的效果,导致S1栈内为AD。
(4) singleInstance
单实例模式。加强版的singleTask。系统会为它创建一个新的任务栈来专门存储和管理该Activity,该Activity具有全局唯一性,任何应用只要启动该Activity,都是用的这一个实例。
3. 启动过程
一个示意图:
当请求启动Activity时:
① Launcher
进程通过Binder
驱动向ActivityManagerService(AMS)
类发起startActivity
请求;
② ActivityManagerService
类接收到请求后,向ActivityStack
类发送启动Activity的请求;
③ ActivityStack
类记录需启动的Activity的信息 & 调整Activity栈,将其置于栈顶
、通过Binder
驱动将Activity的启动信息
传递到ApplicationThread
线程中(即Binder线程
)
④ ApplicationThread
线程通过Handler
将Activity的启动信息
发送到主线程ActivityThread
⑤ 主线程ActivityThread
类接收到该信息 & 请求后,通过ClassLoader机制
加载相应的Activity类,最终调用Activity的onCreate()
,最后启动完毕
4. 卡顿原因
Activity的卡顿原因有如下几种:
5. 加速启动方式
6. 碎片(Fragment)
碎片(Fragment)其实是Activity的缩小版,使用起来灵活度高,也便于复用。
6.1 生命周期
6.2 常见的一些生命周期的变化
- 打开Fragment界面的生命周期:onCreate->onCreateView->onActivityCreated->onStart->onResume
- 按下主屏幕键Fragment的生命周期:onPause->onStop
- 重新打开界面Fragment的生命周期:onStart->onResume
- 按回退键Fragment的生命周期:onPause->onStop->onDestroyView->onDestroy->onDetach
再整点玄乎的 ↓↓↓
6.3 Activity与Fragment的生命周期交织
Fragment的生命周期方法是由Activity托管,而不是操作系统调用的。Activity的生命周期方法都是protected,而Fragment的生命周期方法都是public的。
4. 常见面试真题
① 请描述一下Activity生命周期及各方法。
onCreate->onStart->onResume->onPause->onStop->onDestroy。
onCreate
:Activity被第一次创建,可进行一些初始化工作,加载布局、组件,绑定事件等。
onStart
:Activity在后台,还不可见,还不能与用户进行交互。
onResume
:Activity正在运行,可与用户交互。
onPause
:Activity暂停,不完全可见,不能与用户交互。例如调用了对话框。
onStop
:Activity不可见,不能交互。
onDestroy
:Activity要被销毁了。
② 一个Activity正在运行,从前台切换到后台再切换到前台,生命周期变化过程。
onResume->onPause->onStop->onRestart->onStart->onResume
③ Activity在横竖屏切换的时候生命周期变化过程?
onResume->onPause->(onSaveInstanceState)->onStop->onDestroy->onCreate->(onRestoreInstanceState)->onStart->onResume
如果设置了configChanges属性:
android:configChanges="oritation|screenSize"
onResume
④ Activity的4种启动模式及应用场景
standard
:标准模式,默认的模式。
singleTop
:栈顶复用模式,适用于将具有推送信息展示的Activity指定为singleTop,因为往往实际开发中可能会有很多条推送消息发送与展示,不可能每发一条推送Activity就创建一个新例。
singleTask
:栈内复用模式。一般适用于具有程序入口等启动页面的Activity指定为singleTask,这样可以避免在启动也退出的时候会因存在多实例而反复点击才能退出的问题。
singleInstance
:单实例模式。这种模式的应用场景多用于与其它应用进行交互的情况。
⑤ onNewIntent的使用
总结来说,当Activity被restart或者Activity处于栈顶是被再次start这两种情况就会调用onNewIntent()。onNewIntent()的作用是让开发者在里面对旧的intent进行保存,对新的intent进行相应处理。
⑥ 如何实现Fragment的滑动
ViewPager + Fragment。
⑦ Activity之间的通信方式
Intent、Bundle、工具类等。
Bundle的使用:
通过Intent传递:
Intent intent = new Intent(AActivity.this,BActivity.class);
Bundle bundle = new Bundle(); //得到bundle对象
bundle.putString("sff", "value值"); //key-"sff",通过key得到value-"value值"(String型)
bundle.putInt("iff", 175); //key-"iff",value-175
intent.putExtras(bundle); //通过intent将bundle传到另个Activity
startActivity(intent);
读取Bundle数据:
Intent intent = getIntent();
Bundle bundle = intent.getExtras(); //读取intent的数据给bundle对象
String str = bundle.getString("sff"); //通过key得到value
int num = bundle.getInt("iff");
通过Handler传递:
Handler handler = new Handler();
Message message = enw Message(); // new一个Message对象
message.what = MESSAGE_WHAT_1; // 给消息做标记
Bundle bundle = new Bundle(); // 得到Bundle对象
bundle.putString("text1","消息传递参数的例子~"); // 往Bundle里边传数据
bundle.putInt("text2",22);
message.setData(bundle); // message利用Bundle传数据
handler.sendMessage(message); // Handler将消息放入消息队列
读取数据:
String str = msg.getData().getString("text1");
int num = msg.getData().getString("text2");