阅读前请点击右上角“关注”,每天免费获取知识解析及面试解答。只做职场干货,资料免费分享!
《Android高级开发面试题及答案整理》2.0目录
- 1.Activity
- 2.Service
- 3.BroadcastReceiver
- 4.ContentProvider
- 5.Handler
- 6.View绘制
- 7.View事件分发
- 8.RecycleView
- 9.Viewpager&Fragment
- 10.Webview
- 11.动画
- 12.Bitmap
- 13.mvc&mvp&mvvm
- 14.Binder
- 15.内存泄漏&内存溢出
- 16.性能优化
- 17.Window&WindowManager
- 18.AMS
- 19.系统启动
- 20.App启动&打包&安装
- 21.序列化
- 22.Art & Dalvik及其区别
- 23.模块化&组件化
- 24.热修复&插件化
- 25.AOP
- 26.jectpack
- 27.开源框架
1.Activity
1.1.Activity的启动流程
Activity跨进程启动
启动流程:
点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;
system_server进程接收到请求后,向zygote进程发送创建进程的请求;
Zygote进程fork出新的子进程,即App进程;
App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;
system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进 程发送scheduleLaunchActivity请求;
App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;
主线程在收到Message后,通过发射机制创建目标Activity,并回调Activity.onCreate()等方法。
Activity进程内启动
请求进程A:startActivity—(hook插入点1) (AMP,ActivityManager代理对象)——> system_server进程:AMS(ActivityManagerService)
解析Activity信息、处理启动参数、scheduleLaunchActivity/mH中EXECUTE_TRANSACTION消息处理(Android P)–>
回到请求进程A:ApplicationThread --> ActivityThread -(hook插入点2)-> Activity生命周期
()
1.2.onSaveInstanceState(),onRestoreInstanceState的掉用时机
1.2.1onSaveInstanceState(Bundle outState)会在以下情况被调用:
1.2.1.1、从最近应用中选择运行其他的程序时。
1.2.1.2、当用户按下HOME键时。
1.2.1.3、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。
1.2.1.4、按下电源按键(关闭屏幕显示)时。
1.2.1.5、从当前activity启动一个新的activity时。
onPause -> onSaveInstanceState -> onStop。
1.2.2onRestoreInstanceState(Bundle outState)会在以下情况被调用:
onRestoreInstanceState(Bundle savedInstanceState)只有在activity确实是被系统回收,重新创建activity的情况下才会被调用。
1.2.2.1.屏幕方向切换时,activity生命周期如下
onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
1.2.2.2.在后台被回收
1.2.2.3.按HOME键返回桌面,又马上点击应用图标回到原来页面时不会被回收
onStart -> onRestoreInstanceState -> onResume
1.2.3源码
系统会调用ActivityThread的performStopActivity方法中掉用onSaveInstanceState, 将状态保存在mActivities中,mActivities维护了一个Activity的信息表,当Activity重启时候,会从mActivities中查询到对应的ActivityClientRecord。
如果有信息,则调用Activity的onResoreInstanceState方法,
在ActivityThread的performLaunchActivity方法中,统会判断ActivityClientRecord对象的state是否为空
不为空则通过Activity的onSaveInstanceState获取其UI状态信息,通过这些信息传递给Activity的onCreate方法,
1.3.activity的启动模式和使用场景
1.3.1 android任务栈
我们每次打开一个新的Activity或者退出当前Activity都会在一个称为任务栈的结构中添加或者减少一个Activity组件,一个任务栈包含了一个activity的集合。
android通过ActivityRecord、TaskRecord、ActivityStack,ActivityStackSupervisor,ProcessRecord有序地管理每个activity。
1.3.2 Standard
默认模式,每次启动Activity都会创建一个新的Activity实例。
1.3.3 SingleTop
通知消息打开的页面
如果要启动的Activity已经在栈顶,则不会重新创建Activity,只会调用该该Activity的onNewIntent()方法。
如果要启动的Activity不在栈顶,则会重新创建该Activity的实例。
1.3.4 SingleTask
主界面
如果要启动的Activity已经存在于它想要归属的栈中,那么不会创建该Activity实例,将栈中位于该Activity上的所有的Activity出栈,同时该Activity的onNewIntent()方法会被调用。
1.3.5SingleInstance
呼叫来电界面
要创建在一个新栈,然后创建该Activity实例并压入新栈中,新栈中只会存在这一个Activity实例。
1.4.Activity A跳转Activity B,再按返回键,生命周期执行的顺序
在A跳转B会执行:A onPause -> B onCreate -> B onStart -> B onResume->A onStop
在B按下返回键会执行:B onPause -> A onRestart -> A onStart -> A onResume-> B onStop -> B onDestroy
当A跳转到B的时候,A先执行onPause,然后居然是B再执行onCreate -> onStart -> onResume,最后才执行A的onStop!!!
当B按下返回键,B先执行onPause,然后居然是A再执行onRestart -> onStart -> onResume,最后才是B执行onStop -> onDestroy!!!
当 B Activity 的 launchMode 为 singleInstance,singleTask 且对应的 B Activity 有可复用的实例时,生命周期回调是这样的:
A.onPause -> B.onNewIntent -> B.onRestart -> B.onStart -> B.onResume -> A.onStop -> ( 如果 A 被移出栈的话还有一个 A.onDestory)
当 B Activity 的 launchMode 为 singleTop且 B Activity 已经在栈顶时(一些特殊情况如通知栏点击、连点),此时只有 B 页面自己有生命周期变化:
B.onPause -> B.onNewIntent -> B.onResume
1.5.横竖屏切换,按home键,按返回键,锁屏与解锁屏幕,跳转透明Activity界面,启动一个 Theme 为 Dialog 的 Activity,弹出Dialog时Activity的生命周期
横竖屏切换:
从 Android 3.2 (API级别 13)开始
1、不设置Activity的androidconfigChanges,或设置Activity的androidconfigChanges=“orientation”,或设置Activity的android:configChanges=“orientation|keyboardHidden”,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行一次。
2、配置 android:configChanges=“orientation|keyboardHidden|screenSize”,才不会销毁 activity,且只调用 onConfigurationChanged方法。
竖屏:
启动:onCreat->onStart->onResume.
切换横屏时:
onPause-> onSaveInstanceState ->onStop->onDestory
onCreat->onStart->onSaveInstanceState->onResume.
如果配置这个属性:androidconfigChanges=“orientation|keyboardHidden|screenSize”
就不会在调用Activity的生命周期,只会调用onConfigurationChanged方法
HOME键的执行顺序:onPause->onStop->onRestart->onStart->onResume
BACK键的顺序: onPause->onStop->onDestroy->onCreate->onStart->onResume
锁屏:锁屏时只会调用onPause(),而不会调用onStop方法,开屏后则调用onResume()。
弹出 Dialog: 直接是通过 WindowManager.addView 显示的(没有经过 AMS),所以不会对生命周期有任何影响。
启动theme为DialogActivity,跳转透明Activity
A.onPause -> B.onCrete -> B.onStart -> B.onResume
( Activity 不会回调 onStop,因为只有在 Activity 切到后台不可见才会回调 onStop)
1.6.onStart 和 onResume、onPause 和 onStop 的区别
onStart 和 onResume 从 Activity 可见可交互区分
onStart 用户可以看到部分activity但不能与它交互 onResume()可以获得activity的焦点,能够与用户交互
onStop 和 onPause 从 Activity 是否位于前台,是否有焦点区分
onPause表示当前页面失去焦点。
onStop表示当前页面不可见。
dialog的主题页面,这个时候,打开着一个页面,就只会执行onPause,而不会执行onStop。
1.7.Activity之间传递数据的方式Intent是否有大小限制,如果传递的数据量偏大,有哪些方案
startActivity->startActivityForResult->Instrumentation.execStartActivity
->ActivityManger.getService().startActivity
intent中携带的数据要从APP进程传输到AMS进程,再由AMS进程传输到目标Activity所在进程
通过Binder来实现进程间通信
1.Binder 驱动在内核空间创建一个数据接收缓存区。
2.在内核空间开辟一块内核缓存区,建立内核缓存区和内核空间的数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系。
3.发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核空间的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
为当使用Intent来传递数据时,用到了Binder机制,数据就存放在了Binder的事务缓冲区里面,而事务缓冲区是有大小限制的。普通的由Zygote孵化而来的用户进程,映射的Binder内存大小是不到1M的
Binder 本身就是为了进程间频繁-灵活的通信所设计的, 并不是为了拷贝大量数据
如果非 ipc
单例,eventBus,Application,sqlite、shared preference、file 都可以;
如果是 ipc
1.共享内存性能还不错, 通过 MemoryFile 开辟内存空间,获得 FileDescriptor; 将 FileDescriptor 传递给其他进程; 往共享内存写入数据; 从共享内存读取数据。
2.Socket或者管道性能不太好,涉及到至少两次拷贝。
1.8.Activity的onNewIntent()方法什么时候执行
果IntentActivity处于任务栈的顶端,也就是说之前打开过的Activity,现在处于onPause、onStop 状态的话,其他应用再发送Intent的话,执行顺序为:onNewIntent,onRestart,onStart,onResume。
ActivityA已经启动过,处于当前应用的Activity堆栈中;
当ActivityA的LaunchMode为SingleTop时,如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent()方法;
当ActivityA的LaunchMode为SingleInstance,SingleTask时,如果已经ActivityA已经在堆栈中,那么此时再次启动会调用onNewIntent()方法;
1.9.显示启动和隐式启动
显示启动
1、构造方法传入Component,最常用的方式
2、setComponent(componentName)方法
3、setClass/setClassName方法
隐式启动
隐式Intent是通过在AndroidManifest文件中设置action、data、category,让系统来筛选出合适的Activity
action的匹配规则
Intent-filter action可以设置多条
intent中的action只要与intent-filter其中的一条匹配成功即可,且intent中action最多只有一条
Intent-filter内必须至少包含一个action。
category的匹配规则
Intent-filter内必须至少包含一个category,android:name为
android.intent.category.DEFAULT。
intent-filter中,category可以有多条
intent中,category也可以有多条
intent中所有的category都可以在intent-filter中找到一样的(包括大小写)才算匹配成功
data的匹配规则
intent-filter中可以设置多个data
intent中只能设置一个data
intent-filter中指定了data,intent中就要指定其中的一个data
1.10.scheme使用场景,协议格式,如何使用
scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面
APP根据URL跳转到另外一个APP指定页面
可以通过h5页面跳转app原生页面
服务器可以定制化跳转app页面
Scheme链接格式样式
样式scheme://host/path?query
Uri.parse(“hr://test:8080/goods?goodsId=8897&name=test”)
hr代表Scheme协议名称
test代表Scheme作用的地址域
8080代表该路径的端口号
/goods代表的是指定页面(路径)
goodsId和name代表传递的两个参数
使用
<intent-filter>
<!-- 协议部分配置 ,注意需要跟web配置相同-->
<!--协议部分,随便设置 hr://test:8080/goods?name=test -->
<data android:scheme="hr"
android:host="test"
android:path="/goods"
android:port="8080"/>
<!--下面这几行也必须得设置-->
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<action android:name="android.intent.action.VIEW" />
</intent-filter>
掉用
Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse("hr://test:8080/goods?name=test"));
startActivity(intent);
1.11.ANR 的四种场景
ANR 的四种场景:
Service TimeOut: service 未在规定时间执行完成:前台服务 20s,后台 200s
BroadCastQueue TimeOut: 未在规定时间内未处理完广播:前台广播 10s 内, 后台 60s 内
ContentProvider TimeOut: publish 在 10s 内没有完成
Input Dispatching timeout: 5s 内未响应键盘输入、触摸屏幕等事件
我们可以看到, Activity 的生命周期回调的阻塞并不在触发 ANR 的场景里面,所以并不会直接触发 ANR。
只不过死循环阻塞了主线程,如果系统再有上述的四种事件发生,就无法在相应的时间内处理从而触发 ANR。
1.12.onCreate和onRestoreInstance方法中恢复数据时的区别
onSaveInstanceState 不一定会被调用,因为它只有在上次activity被回收了才会调用。
onCreate()里的Bundle参数可能为空,一定要做非空判断。 而onRestoreInstanceState的Bundle参数一定不会是空值。
1.13.activty间传递数据的方式
通过 Intent 传递(Intent.putExtra 的内部也是维护的一个 Bundle,因此,通过 putExtra 放入的 数据,取出时也可以通过 Bundle 去取)
通过全局变量传递
通过 SharedPreferences 传递
通过数据库传递
通过文件传递
1.14.跨App启动Activity的方式,注意事项
使用intentFilter(隐式跳转)
在Manifest的Activity标签中添加:
启动时:startActivity(new Intent("
com.example.test.action.BActivity"))
如果有两个action属性值相同的Activity,那么在启动时手机系统会让你选择启动哪一个Activity
要解决这个问题,需要给被启动的Activity再加上一个属性,
然后再启动该Activity的Intent中加上一个URI,其中“app”必须与data属性的scheme的值一样,
intent=new Intent(“com.zs.appb.intent.action.BeStartActivity”, Uri.parse(“app://hello”));
共享uid的App
android中uid用于标识一个应用程序,uid在应用安装时被分配,并且在应用存在于手机上期间,都不会改变。一个应用程序只能有一个uid,多个应用可以使用sharedUserId 方式共享同一个uid,前提是这些应用的签名要相同。
在AndroidManifest中:manifest标签中添加android:sharedUserId=“xxxx”
启动时:startActivity(new Intent().setComponent(new ComponentName(“com.example.test”,"
com.example.test.XxxActivity")));
使用exported
一旦设置了intentFilter之后,exported就默认被设置为true了
在Manifest中添加exported属性
启动时:startActivity(new Intent().setComponent(new ComponentName(“com.example.zhu”,"
com.example.zhu.XxxActivity")));
注意(如何防止自己的Activity被外部非正常启动):
如果AppB设置了android:permission=”xxx.xxx.xx”那么, 就必须在你的AppA的AndroidManifast.xml中uses-permission xxx.xxx.xx才能访问人家的东西。
给AppA的manifest中添加权限:
gei AppB中需要启动的Activity添加permission属性:
android:permission=“com.example.test”
1.15.Activity任务栈是什么
1.android任务栈又称为Task,它是一个栈结构,具有后进先出的特性,用于存放我们的Activity组件。
2.我们每次打开一个新的Activity或者退出当前Activity都会在一个称为任务栈的结构中添加或者减少一个Activity组件, 一个任务栈包含了一个activity的集合, 只有在任务栈栈顶的activity才可以跟用户进行交互。
3.在我们退出应用程序时,必须把所有的任务栈中所有的activity清除出栈时,任务栈才会被销毁。当然任务栈也可以移动到后台, 并且保留了每一个activity的状态. 可以有序地给用户列出它们的任务, 同时也不会丢失Activity的状态信息。
4.对应AMS中的ActivityRecord、TaskRecord、ActivityStack(AMS中的总结)
1.16.有哪些Activity常用的标记位Flags
FLAG_ACTIVITY_NEW_TASK
此标记位作用是为Activity指定“singleTask”启动模式,其效果和在XML中指定相同android:launchMode=“singleTask”
FLAG_ACTIVITY_SINGLE_TOP
此标记位作用是为Activity指定“singleTop”启动模式,其效果和在XML中指定相同android:launchMode=“singleTop”
FLAG_ACTIVITY_CLEAR_TOP
具有此标记位的Activity,当它启动时,在同一个任务栈中位于它上面的Activity都要出栈。此标记位一般会和singleTask启动模式一起出现,此情况下,若被启动的Activity实例存在,则系统会调用它的onNewIntent。
1.17.Activity的数据是怎么保存的,进程被Kill后,保存的数据怎么恢复的
在Activity的onSaveInstanceState方法回调时,put到参数outState(Bundle)里面。outState就是ActivityClientRecord的state。
ActivityClientRecord实例,都存放在ActivityThread的mActivities里面。
Activity变得不可见时(onSaveInstanceState和onStop回调之后),在应用进程这边会通过
ActivityTaskManagerService的activityStopped方法,把刚刚在onSaveInstanceState中满载了数据的Bundle对象,传到系统服务进程那边! 然后(在系统服务进程这边),会进一步将这个Bundle对象,赋值到对应ActivityRecord的icicle上
ActivityRecord是用来记录对应Activity的各种信息的,如theme,启动模式、当前是否可见等等(为了排版更简洁,上图只列出来一个icicle),它里面还有很多管理Activity状态的相关方法;
TaskRecord就是大家耳熟能详的任务栈(从上图可以看出并不真的是栈)了,它的主要职责就是管理ActivityRecord。每当Activity启动时,会先找到合适的TaskRecord(或创建新实例),然后将该Activity所对应的ActivityRecord添加到TaskRecord的mActivities中;
ActivityStack管理着TaskRecord,当新TaskRecord被创建后,会被添加到它mTaskHistory里面。
2.Service
2.1.service 的生命周期,两种启动方式的区别
startService
onCreate() -> onStartCommand() -> onDestroy()
bindService
onCreate() -> onbind() -> onUnbind()-> onDestroy()
区别
启动
如果服务已经开启,多次执行startService不会重复的执行onCreate(), 而是会调用onStart()和onStartCommand()。
如果服务已经开启,多次执行bindService时,onCreate和onBind方法并不会被多次调用
销毁
当执行stopService时,直接调用onDestroy方法
调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind->onDestroy
使用startService()方法启用服务,调用者与服务之间没有关联,即使调用者退出了,服 务仍然运行。
使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。
1、单独使用startService & stopService
(1)第一次调用startService会执行onCreate、onStartCommand。
(2)之后再多次调用startService只执行onStartCommand,不再执行onCreate。
(3)调用stopService会执行onDestroy。
2、单独使用bindService & unbindService
(1)第一次调用bindService会执行onCreate、onBind。
(2)之后再多次调用bindService不会再执行onCreate和onBind。
(3)调用unbindService会执行onUnbind、onDestroy。
2.2.Service启动流程
1.Process A进程采用Binder IPC向system_server进程发起startService请求;
2.system_server进程接收到请求后,向zygote进程发送创建进程的请求;
3.zygote进程fork出新的子进程Remote Service进程;
4.Remote Service进程,通过Binder IPC向sytem_server进程发起attachApplication请求;
5.system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向remote Service进程发送scheduleCreateService请求;
6.Remote Service进程的binder线程在收到请求后,通过handler向主线程发送CREATE_SERVICE消息;
7.主线程在收到Message后,通过发射机制创建目标Service,并回调Service.onCreate()方法。
到此,服务便正式启动完成。当创建的是本地服务或者服务所属进程已创建时,则无需经过上述步骤2、3,直接创建服务即可。
bindService
startService
2.3.Service与Activity怎么实现通信
通过Binder对象
1.Service中添加一个继承Binder的内部类,并添加相应的逻辑方法
2.Service中重写Service的onBind方法,返回我们刚刚定义的那个内部类实例
3.Activity中绑定服务,重写ServiceConnection,onServiceConnected时返回的IBinder(Service中的binder)调用逻辑方法
Service通过BroadCast广播与Activity通信
2.4.IntentService是什么,IntentService原理,应用场景及其与Service的区别
what
IntentService 是 Service 的子类,默认开启了一个工作线程HandlerThread,使用这个工作线程逐一处理所有启动请求,在任务执行完毕后会自动停止服务。只要实现一个方法 onHandleIntent,该方法会接收每个启动请求的 Intent,能够执行后台工作和耗时操作。
可以启动 IntentService 多次,而每一个耗时操作会以队列的方式在 IntentService 的 onHandlerIntent 回调方法中执行,并且,每一次只会执行一个工作线程,执行完第一个再执行第二个。并且等待所有消息都执行完后才终止服务。
how
1.创建一个名叫 ServiceHandler 的内部 Handler
2.把内部Handler与HandlerThread所对应的子线程进行绑定
3.HandlerThread开启线程 创建自己的looper
4.通过 onStartCommand() intent,依次插入到工作队列中,并发送给 onHandleIntent()逐个处理
可以用作后台下载任务 静默上传
why IntentService会创建独立的worker线程来处理所有的Intent请求 Service主线程不能处理耗时操作,IntentService不会阻塞UI线程,而普通Serveice会导致ANR异常。
为Service的onBind()提供默认实现,返回null;onStartCommand提供默认实现,将请求Intent添加到队列中。
所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service。
2.5.Service 的 onStartCommand 方法有几种返回值?各代表什么意思?
START_NOT_STICKY
在执行完 onStartCommand 后,服务被异常 kill 掉,系统不会自动重启该服务。
START_STICKY
重传 Intent。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异 常 kill 掉,系统会自动重启该服务 ,并且onStartCommand方法会执行,onStartCommand方法中的intent值为null。
适用于媒体播放器或类似服务。
START_REDELIVER_INTEN
使用这个返回值时,服务被异 常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。
适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
2.6.bindService和startService混合使用的生命周期以及怎么关闭
如果你只是想要启动一个后台服务长期进行某项任务,那么使用startService便可以了。如果你还想要与正在运行的Service取得联系,那么有两种方法:一种是使用broadcast,另一种是使用bindService。
如果先startService,再bindService
onCreate() -> onbind() -> onStartCommand()
如果先bindService,再startService
onCreate() -> onStartCommand() -> onbind()
如果只stopService
Service的OnDestroy()方法不会立即执行,在Activity退出的时候,会执行OnDestroy。
如果只unbindService
只有onUnbind方法会执行,onDestory不会执行
如果要完全退出Service,那么就得执行unbindService()以及stopService。
3.BroadcastReceiver
3.1.广播的分类和使用场景
Android 广播分为两个角色:广播发送者、广播接受者
广播接收器的注册分为两种:静态注册、动态注册。
静态广播接收者:通过AndroidManifest.xml的标签来申明的BroadcastReceiver。
动态广播接收者:通过AMS.registerReceiver()方式注册的BroadcastReceiver,动态注册更为灵活,可在不需要时通过unregisterReceiver()取消注册。
广播类型:根据广播的发送方式,
1.普通广播:通过Context.sendBroadcast()发送,可并行处理
2.系统广播:当使用系统广播时,只需在注册广播接收者时定义相关的action即可,不需要手动发送广播(网络变化,锁屏,飞行模式)
3.有序广播: 指的是发送出去的广播被 BroadcastReceiver 按照先后顺序进行接收 发送方式变为:sendOrderedBroadcast(intent);
广播接受者接收广播的顺序规则(同时面向静态和动态注册的广播接受者):按照 Priority 属性值从大-小排序,Priority属性相同者,动态注册的广播优先。
4.App应用内广播(Local Broadcast)
背景 Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)
冲突 可能出现的问题:
其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息; 即会出现安全性 & 效率性的问题。
解决方案 使用App应用内广播(Local Broadcast)
App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。 相比于全局广播(普通广播),App应用内广播优势体现在:安全性高 & 效率高
具体使用1 - 将全局广播设置成局部广播
注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收; 在广播发送和接收时,增设相应权限permission,用于权限验证;
发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
具体使用2 - 使用封装好的LocalBroadcastManager类
对于LocalBroadcastManage,方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册
5.粘性广播(Sticky Broadcast) 由于在Android5.0 & API 21中已经失效,所以不建议使用,在这里也不作过多的总结。
应用场景
同一 App 内部的不同组件之间的消息通信(单个进程);
不同 App 之间的组件之间消息通信;
Android系统在特定情况下与App之间的消息通信,如:网络变化、电池电量、屏幕开关等。
3.2.广播的两种注册方式的区别
静态注册:常驻系统,不受组件生命周期影响,即便应用退出,广播还是可以被接收,耗电、占内存。
动态注册:非常驻,跟随组件的生命变化,组件结束,广播结束。在组件结束前,需要先移除广播,否则容易造成内存泄漏。
3.3.广播发送和接收的原理
动态注册
1.创建对象
LoadedApk.ReceiverDispatcher.InnerReceiver的实例,该对象继承于IIntentReceiver.Stub(InnerReceiver实际是一个binder本地对象(BBinder:本地Binder,服务实现方的基类,提供了onTransact接口来接收请求))。
2.将IIntentReceiver对象和注册所传的IntentFilter对象发送给AMS。 AMS记录IIntentReceiver、IntentFilter和注册的进程ProcessRecord,并建立起它们的对应关系。
3.当有广播发出时,AMS根据广播intent所携带的IntentFilter找到IIntentReceiver和ProcessRecord,然后回调App的ApplicationThread对象的
scheduleRegisteredReceiver,将IIntentReceiver和广播的intent一并传给App,App直接调用IIntentReceiver的performReceive。
4.因为广播是通过binder线程回调到接收进程的,接收进程通过ActivityThread里的H这个Handler将调用转到主线程,然后回调BroadcastReceiver的onReceive。
静态注册
静态注册是通过在Manifest文件中声明实现了BroadcastReceiver的自定义类和对应的IntentFilter,来告诉PMS(PackageManagerService)这个App所注册的广播。
当AMS接收到广播后,会查找所有动态注册的和静态注册的广播接收器,静态注册的广播接收器是通过PMS(PackageManagerService)发现的,PMS找到对应的App
对应进程已经创建,直接调用App的ApplicationThread对象的scheduleReceiver
对应进程尚未创建,先启动App进程,App进程启动后回调AMS的attachApplication,attachApplication则继续派发刚才的广播App这边收到调用后会先通过Handler转到主线程,然后根据AMS传过来的参数实例化广播接收器的类,接着调用广播接收器的onReceive。
3.4.本地广播和全局广播的区别
BroadcastReceiver是针对应用间、应用与系统间、应用内部进行通信的一种方式
LocalBroadcastReceiver仅在自己的应用内发送接收广播,也就是只有自己的应用能收到,数据更加安全广播只在这个程序里,而且效率更高。
BroadcastReceiver采用的binder方式实现跨进程间的通信;
LocalBroadcastManager使用Handler通信机制。
LocalBroadcastReceiver 使用
LocalBroadcastReceiver不能静态注册,只能采用动态注册的方式。
在发送和注册的时候采用,LocalBroadcastManager的sendBroadcast方法和registerReceiver方法
注册过程,主要是向mReceivers和mActions添加相应数据:
mReceivers:数据类型为HashMap<BroadcastReceiver, ArrayList>, 记录广播接收者与IntentFilter列表的对应关系;
mActions:数据类型为HashMap<String, ArrayList>, 记录action与广播接收者的对应关系
根据Intent的action来查询相应的广播接收者列表;
发送
MSG_EXEC_PENDING_BROADCASTS消息,回调相应广播接收者的onReceive方法
由于内容实在太多了,在这里就不方便做全部的展示,以上就是《Android高级开发面试题2.0》的部分,对这份资料感兴趣的小伙伴点赞+关注支持一下博主后,扫描文末CSDN官方认证二维码免费获取!!!
写在最后
我个人觉得面试也像是一场全新的征程,失败和胜利都是平常之事。所以,劝各位不要因为面试失败而灰心、丧失斗志。也不要因为面试通过而沾沾自喜,等待你的将是更美好的未来,继续加油!