1、四大组件
1.1 Activity
- Q:在两个 Activity 之间传递对象还需要注意什么呢?
对象的大小。Intent 中的 Bundle 是使用 Binder 机制进行数据传送的。能使用的 Binder 的缓冲区是有大小限制的(有些手机是 2 M),而一个进程默认有 16 个 Binder 线程,所以一个线程能占用的缓冲区就更小了(有人以前做过测试,大约一个线程可以占用 128 KB)。可以使用 EventBus 来传递数据,而不是直接使用 Intent 进行传递。
- Q:onSaveInstanceState() 和 onRestoreInstanceState()
当 Activity 被销毁的时候回调用 onSaveInstanceState()
方法来存储当前的状态。这样当 Activity 被重建的时候,可以在 onCreate()
和 onRestoreInstanceState()
中恢复状态。
对于 targetAPI 为 28 及以后的应用,该方法会在 onStop()
方法之后调用,对于之前的设备,这方法会在 onStop()
之前调用,但是无法确定是在 onPause()
之前还是之后调用。
onRestoreInstanceState()
方法用来恢复之前存储的状态,它会在 onStart()
和 onPostCreate()
之间被调用。此外,你也可以直接在 onCreate()
方法中进行恢复,但是基于这个方法调用的时机,如果有特别需求,可以在这个方法中进行处理。
- Q:SingleTask 启动模式
- Q:Activity 启动模式
- standard:默认,每次启动的时候会创建一个新的实例,并且被创建的实例所在的栈与启动它的 Activity 是同一个栈。比如,A 启动了 B,那么 B 将会与 A 处在同一个栈。假如,我们使用 Application 的 Context 启动一个 Activity 的时候会抛出异常,这是因为新启动的 Activity 不知道自己将会处于哪个栈。可以在启动 Activity 的时候使用
FLAG_ACTIVITY_NEW_TASK
。这样新启动的 Acitivyt 将会创建一个新的栈。 - singleTop:栈顶复用,如果将要启动的 Activity 已经位于栈顶,那么将会复用栈顶的 Activity,并且会调用它的
onNewIntent()
。常见的应用场景是从通知打开 Activity 时。 - singleTask:单例,如果启动它的任务栈中存在该 Activity,那么将会复用该 Activity,并且会将栈内的、它之上的所有的 Activity 清理出去,以使得该 Activity 位于栈顶。常见的应用场景是启动页面、购物界面、确认订单界面和付款界面等。
- singleInstance:这种启动模式会在启动的时候为其指定一个单独的栈来执行。如果用同样的intent 再次启动这个 Activity,那么这个 Activity 会被调到前台,并且会调用其
onNewIntent()
方法。
- Q:下拉状态栏是不是影响 Activity 的生命周期,如果在 onStop() 的时候做了网络请求,onResume() 的时候怎么恢复
- Q:前台切换到后台,然后再回到前台,Activity 生命周期回调方法。弹出 Dialog,生命值周期回调方法。
- Q:Activity 生命周期
- Q:Activity 上有 Dialog 的时候按 Home 键时的生命周期
- Q:横竖屏切换的时候,Activity 各种情况下的生命周期
Android 下拉通知栏不会影响 Activity 的生命周期方法。
弹出 Dialog,生命周期:其实是否弹出 Dialog,并不影响 Activity 的生命周期,所以这时和正常启动时 Activity 的生命回调方法一致: onCreate() -> onStart() -> onResume()
。
这里我们总结一下在实际的使用过程中可能会遇到的一些 Acitivity 的生命周期过程:
- 当用户打开新的 Activity 或者切换回桌面:会经过的生命周期为
onPause()->onStop()
。因为此时 Activity 已经变成不可见了,当然,如果新打开的 Activity 用了透明主题,那么 onStop() 不会被调用,因此原来的 Activity 只是不能交互,但是仍然可见。 - 从新的 Activity 回到之前的 Activity 或者从桌面回到之前的 Activity:会经过的生命周期为
onRestart()->onStart()-onResume()
。此时是从 onStop() 经 onRestart() 回到 onResume() 状态。 - 如果在上述 1 的情况下,进入后台的 Activity 因为内存不足被销毁了,那么当再次回到该 Activity 的时候,生命周期方法将会从 onCreate() 开始执行到 onResume()。
- 当用户按下 Back 键时:如果当前 Activity 被销毁,那么经过的生命周期将会是
onPause()->onStop()->onDestroy()
。
具体地,当存在两个 Activity,分别是 A 和 B 的时候,在各种情况下,它们的生命周期将会经过:
- Back 键 Home 键
- 当用户点击 A 中按钮来到 B 时,假设 B 全部遮挡住了 A,将依次执行:
A.onPause()->B.onCreate()->B.onStart()->B.onResume->A.onStop()
。 - 接1,此时如果点击 Back 键,将依次执行:
B.onPause()->A.onRestart()->A.onStart()->A.onResume()->B.onStop()->B.onDestroy()
。 - 接2,此时如果按下 Back 键,系统返回到桌面,并依次执行:
A.onPause()->A.onStop()->A.onDestroy()
。 - 接2,此时如果按下 Home 键(非长按),系统返回到桌面,并依次执行
A.onPause()->A.onStop()
。由此可见,Back 键和 Home 键主要区别在于是否会执行 onDestroy()。 - 接2,此时如果长按 Home 键,不同手机可能弹出不同内容,Activity 生命周期未发生变化。
- 当用户点击 A 中按钮来到 B 时,假设 B 全部遮挡住了 A,将依次执行:
- 横竖屏切换时 Activity 的生命周期
- 不设置 Activity 的
android:configChanges
时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次。 - 设置 Activity 的
android:configChanges=“orientation”
时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次。 - 设置 Activity 的
android:configChanges=“orientation|keyboardHidden”
时,切屏不会重新调用各个生命周期,只会执行 onConfiguration() 方法。
- 不设置 Activity 的
- Q:Activity 之间的通信方式
- Intent +
onActivityResult()
+setResult()
- 静态变量(跨进程不行)
- 全局通信,广播或者 EventBus
- Q:AlertDialog, PopupWindow, Activity 区别
AlertDialog 是 Dialog 的子类,所以它包含了 Dialog 类的很多属性和方法。是弹出对话框的主要方式,对话框分成支持包的和非支持包的,UI 效果上略有区别。
AlertDialog 与 PopupWindow 之间最本质的差异在于:
AlertDialog 是非阻塞式对话框;而PopupWindow 是阻塞式对话框
。AlertDialog 弹出时,后台还可以做事情;PopupWindow 弹出时,程序会等待,在PopupWindow 退出前,程序一直等待,只有当我们调用了dismiss()
方法的后,PopupWindow 退出,程序才会向下执行。我们在写程序的过程中可以根据自己的需要选择使用 Popupwindow 或者是 Dialog.两者最根本的区别在于有没有新建一个 window
,PopupWindow 没有新建,而是通过 WMS 将 View 加到 DecorView;Dialog 是新建了一个 window (PhoneWindow),相当于走了一遍 Activity 中创建 window 的流程。
Activity 与 Dialog 类似,都会使用 PhoneWindow 来作为 View 的容器。Activity 也可以通过设置主题为 Dialog 的来将其作为对话框来使用。Dialog 也可以通过设置 Theme 来表现得像一个 Activity 一样作为整个页面。但 Activity 具有生命周期,并且它的生命周期归 AMS 管,而 Dialog 不具有生命周期,它归 WMS 管。
- Q:Activity 与 Service 通信的方式
前提是是否跨进程,如果不跨进程的话,EventBus 和 静态变量都能传递信息,否则需要 IPC 才行:
- Binder 用于跨进程的通信方式,AIDL 可以用来进行与远程通信,绑定服务的时候可以拿到远程的 Binder,然后调用它的方法就可以从远程拿数据。那么如果希望对远程的服务进行监听呢?可以使用 AIDL 中的
oneway
来定义回调的接口,然后在方法中传入回调即可。也可以使用 Messenger,向远程发送信息的时候,附带本地的 Messenger,然后远程获取本地的 Messenger 然后向其发送信息即可,详见 IPC 相关一文:《Android 高级面试-2:IPC 相关》 - 广播:使用广播实现跨进程通信
- 启动服务的时候传入值,使用
startService()
的方式
关于 Activity 相关的内容可以参考笔者的文章:《Android 基础回顾:Activity 基础》
1.2 Service
- Q:怎么启动 Service
- Q:Service 的开启方式
- Q:Service 生命周期
其他,
- Service 有绑定模式和非绑定模式,以及这两种模式的混合使用方式。不同的使用方法生命周期方法也不同。
- 非绑定模式:当第一次调用
startService()
的时候执行的方法依次为onCreate()->onStartCommand()
;当 Service 关闭的时候调用onDestory()
。 - 绑定模式:第一次
bindService()
的时候,执行的方法为onCreate()->onBind()
;解除绑定的时候会执行onUnbind()->onDestory()
。
- 非绑定模式:当第一次调用
- 我们在开发的过程中还必须注意 Service 实例只会有一个,也就是说如果当前要启动的 Service 已经存在了那么就不会再次创建该 Service 当然也不会调用 onCreate() 方法。所以,
- 当第一次执行
startService(intent)
的时候,会调用该 Service 中的onCreate()
和onStartCommand()
方法。 - 当第二次执行
startService(intent)
的时候,只会调用该 Service 中的onStartCommand()
方法。(因此已经创建了服务,所以不需要再次调用onCreate()
方法了)。
- 当第一次执行
bindService()
方法的第三个参数是一个标志位,这里传入BIND_AUTO_CREATE
表示在Activity 和 Service 建立关联后自动创建 Service,这会使得 MyService 中的onCreate()
方法得到执行,但onStartCommand()
方法不会执行。所以,在上面的程序中当调用了bindService()
方法的时候,会执行的方法有,Service 的onCreate()
方法,以及 ServiceConnection 的onServiceConnected()
方法。- 在 3 中,如果想要停止 Service,需要调用
unbindService()
才行。 - 如果我们既调用了
startService()
,又调用bindService()
会怎么样呢?这时不管你是单独调用stopService()
还是unbindService()
,Service 都不会被销毁,必须要将两个方法都调用 Service 才会被销毁。也就是说,stopService()
只会让 Service 停止,unbindService()
只会让 Service 和 Activity 解除关联,一个 Service 必须要在既没有和任何 Activity 关联又处理停止状态的时候才会被销毁。
- 进程保活
- App 中唤醒其他进程的实现方式
1.3 Broadcast
- Q:BroadcastReceiver,LocalBroadcastReceiver 区别
- Q:广播的使用场景
- Q:广播的使用方式,场景
- Q:广播的分类?
- Q:广播(动态注册和静态注册区别,有序广播和标准广播)
分类
- 按照注册方式:静态注册和动态注册两种:
- 静态广播直接在 manifest 中注册。限制:
- 在 Android 8.0 的平台上,应用不能对大部分的广播进行静态注册,也就是说,不能在 AndroidManifest 文件对有些广播进行静态注册;
- 当程序运行在后台的时候,静态广播中不能启动服务。
- 动态广播与静态广播相似,但是不需要在 Manifest
- 静态广播直接在 manifest 中注册。限制: