一、Activity 基础相关
生命周期相关
1、Activity的生命周期7个api
2、onStart 和 onStop ,onResume 和onPause 和从描述上差不多对我们来说有什么实质不同?
从使用过程来讲这两对确实差不多,甚至我们可以只保留一对。但是安卓设计这两对是处于不同的场景考虑的。
-
onStart 和 onStop 定位于于界面是否可见。
-
onResume 和onPause 定位于界面是否前台。
3、异常情况下Activity特点
(1)异常情况指的是系统配置发生改变、或者系统内存不足。在异常情况下Activity默认会销毁重建。
(2)异常情况下Activity会回调两个方法(onSaveInstanceState、onRestoreInstanceState。)来进行界面数据的保存、恢复。
(3)正常生命周期情况下这两个数据恢复相关的api不会回调。
4、onRestoreInstanceState和onCreate中都可以收到onSaveInstanceState存的Bundle值。这两个方法有啥区别?
- onRestoreInstanceState只有异常生命周期下这个回调才会被调用(onSaveInstanceState也是)一旦被调用其参数savedInstanceState一定是有值的,我们不用额外判空。
- onCreate如果是正常启动Activity时,onCreate方法的参数值为空,所以要使用参数值时要额外判空。
5、资源配置发生改变情况下如何禁止activity重新创建
(1)给activity指定configChanges属性:保证异常情况下activity不会被销毁重建。属性值有好多如orientation屏幕方向发生改变,这个最常用,比如旋转了手机屏幕。当你配置了哪些属性值,对应情况下次就不会重建activity。
(2)辅助方法:重写activity的onConfigurationChanged回调,处理配置改变时要做的事情。
启动模式相关
1、四种启动模式
standard
singleTop
singleTask:
注意这里系统会先判断A所需的任务栈是否存在。具体是根据A任务相关性taskAffinity 判断的。任务相关性默认为包名。可在清单文件配置。
任务相关性只和 singleTask或allowTaskReparenting 配对使用。其他情况无意义、无效。
singleInstance:开启新的任务栈。实例单独放任务栈中。
2、TaskAffinity 与allowTaskReparenting
(1)TaskAffinity 主要与singleTask 模式配合使用,来指定activity所需任务栈。
默认情况下TaskAffinity属性值为包名。即sigleTask模式下任务栈默认为启动他的activity所在任务栈,当待启动activity的TaskAffinity与启动他的activity的TaskAffinity不同时,待启动的activity所在任务栈就不再是启动它的activity所在的任务栈了。
(2) TaskAffinity 与allowTaskReparenting配合使用
- 默认情况下:应用A的ActivityA打开应用B的ActivityB。此时ActivityB进入开启他的ActivityA所属任务栈。表现的现象为 ActivityA->open->ActivityB->Home键->点击AppB 图标 ->appB 打开主页Activity(非ActivityB)。
- ActivityB allowTaskReparenting 设置为true情况下:应用A的ActivityA打开应用B的ActivityB。此时ActivityB不会进入开启他的ActivityA所属任务栈。而是为ActivityB创建一个任务栈(默认B应用包名),打开ActivityB。表现的现象为 ActivityA->open->ActivityB->Home键->点击AppB 图标 ->开启ActivityB页面(而不是应用B的主页)。
ps:上述两种栗子TaskAffinity 都默认为自己的包名。所以两者的任务相关性是不同的。
情况1中属于常规情况,A开启B,则B进入开启他A所属任务栈中。
情况2中allowTaskReparenting 属性设置为true 就允许任务栈更改了。不是进入A所属的任务栈了。直接本应用中创建任务栈。
这里想要搞清楚还是需要很多理论知识的:
1、通过栗子可以字节先明白下这些现象。
2、以后有机会可以研究下AMS中的任务栈。
3、启动模式的使用场景(最好结合自己做过的项目分析)
(1)standard 模式就不说了,默认情况下就是这种模式。
这种模式有个弊端,假如有个按钮A点击后打开ActivityA。当用户快速点击按钮多次时可能造成页面A频繁被打开。处理方案,根据用户点击按钮的时间差进行拦截。可以在点击事件中书写逻辑。也可以使用aop拦击点击事件处理逻辑。
(2)singleTop 适合接收通知启动的内容显示页面。例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。
(3)singleTask 应用主页、浏览器主页面。目的从Home页返回时直接退出app。
大多数 App 的主页。对于大部分应用,当我们在主界面点击回退按钮的时候都是退出应用,那么当我们第一次进入主界面之后,主界面位于栈底, 以后不管我们打开了多少个 Activity,只要我们再次回到主界面,都应该使用将主界面Activity 上所有的 Activity 移除的方式来让主界面 Activity 处于栈顶,而不是往栈顶新加一个主界面 Activity 的实例,通过这种方式能够保证退出应用时所有的 Activity 都能被销毁。
(4)singleInstance:如APP经常调用的拨打电话、系统通讯录、地图类APP 等页面,不同APP调用此类Activity 时,首次创建实例,之后其他APP只需复用此实例。
IntentFilter 过滤规则
1、IntentFilter:Activity节点下可以有多组IntentFilter节点。只要目标intent满足一组IntentFilter的规则即可打开这个activity。
2、IntentFilter 子节点
(1)action:区分大小写,可以有多个。intent满足一个即可。
(2)category:可以有多个,intent都要满足。
这里需要注意一点,系统在调用startActivity或者startActivityForResult时,会默认为intent加上了android.intent.category.DEFAULT这个category。所以为了activity能够接受隐式调用必须在intentFilter下设置android.intent.category.DEFAULT。
(3)data:data由两部分组成mimeType和URI。
- mimeType:媒体类型,如image/jpeg 、audio/mpeng4-generic、vodeo/* 可以表示图片文本视屏等不同媒体格式。data 中可以不指定这个属性。
- URI
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
注意设置时scheme,host不能为空。
二、Service 基础相关
1、两种开启方式
2、生命周期
(1)start方式:
onCreate->onStartCommand->Service Running ----->onDestroy(stop by itself or client)
(2) bind方式:
onCreate->onBind->Service Running------>onUnbind(client call unbindService) ->onDestroy
3、几种开启方式的特点
(1)start方式
(2)bind方式
(3)混合方式
4、start开启方式onStartCommand 返回值
(1)START_STICKY_COMPATIBILITY = 0 :START_STICKY返回值的兼容性版本,当服务被杀死后不能确保服务被再次开启。
(2)START_STICKY = 1:服务所在系统进程在 onStartCommand 方法执行后,系统会重新开启服务。但是系统不会使用你上次开启服务的intent,而是使用个空intent对象开启服务。 这种模式对用户确定开启并关闭任意一段时间 这种场景是适用的。例如 后台播放音乐。
(3)START_NOT_STICKY = 2:不会重启
(4)START_REDELIVER_INTENT :会重新开启服务。并且使用最近一次开启服务的intent对象。这适用于主动执行应该立即恢复的作业的服务。例如下载文件。
5、service如何提升其进程优先级
变为前台进程,具体的做法是startService方式,在onStartCommand 中开启通知栏。
6、Service家族
(1)intentService:IntentService是Service的子类,由于Service里面不能做耗时的操作,为了方便使用,Google提供了IntentService。
IntentService内维护了一个工作线程来处理耗时操作,当任务执行完后,IntentService会自动停止。
另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
(2)jobService:为了解决特定条件下触发service、优化资源,安卓提供了jobervice。通过jobSchedule对所有的service任务统一管理。
三、广播相关
1、广播的应用场景有哪些?
(1)同一app内部的同一组件内的消息通信(单个或多个线程之间):针对这种情况直接使用接口回调或者handler都可以完成,不必浪费系统广播组件资源。
(2)同一app内部的不同组件之间的消息通信(单个进程):这种条件下使用接口可能不好处理,可以使用EventBus进行同进程中处理,轻松解耦。
EventBus不可以用于多进程。EventBus是订阅者模式,而在多进程中,对象是不一致的,需要通过binder才能进行多进程通信,在这方面EventBus不支持,自然就不支持多进程了。
(3)同一app有不同的组件,且运行在不同的进程中(多个进程之间):适合使用广播。
(4)不同app之间的组件之间消息通信(多个进程之间):适合使用广播。
(5) Android系统在特定情况下与App之间的消息通信。:适合使用广播。
一般夸进程通信时可以使用广播。其他条件下都有一些替代方案的。系统组件资源也是昂贵的,能替代就尽量避免。
2、广播的两种注册方式及其区别
(1)动态注册:灵活、跟随组件生命周期。组件销毁要注意解绑,否则易造成内存泄漏。
(2)静态注册:常驻广播,app关闭后还可接收。由于时刻监听广播相对来数耗内存、耗电。
注意:
安卓逐渐对广播进行了优化,安卓8.0开始限制了大部分系统广播,静态注册的广播好多都接收不到了。
我们上面说了静态注册的系统广播,隐式启动时完全失效。但是自定义广播不受影响啊,可以显式启动啊,如上你把发送广播的代码替换为注释部分则会看到又可以接收到啦。 这也只是发送自定义广播情况下。
如果我们使用系统的广播时还是动态注册吧。
3、广播分类
(1)普通广播:我们自定义的广播。想要接收我们自定义的广播,只需要搞个receiver监听我们自定义的action即可。通常自定义广播伴随着自定义权限一起食用。
(2)系统广播:系统定义了一系列广播,这些广播由系统在一定条件下发送。想要接收系统广播receiver监听相应的action即可。
(3)有序广播:发送广播时可以设置广播为有序广播。优先级(-1000-10000)高的接受者先接收到。
同优先级时:动态大于静态。(同优先级动态注册时先注册大于后注册)
缺点:广播可被拦截修改、广播可被拦截者终结。
(4)粘性广播:安卓5.0已经弃用
(5)本地广播:系统广播,普通广播具有不安全性。app可能会接收到不感兴趣的广播(export = true ,且intentfilter 匹配成功时)、其他app可能得知我们广播权限,action来监听我们的广播。 如果仅仅为了本app内使用,开发者需要处理这些细节问题。所以为了方便google提供了api搞个本地广播。
四、ContentProvider
1、作用:夸进程通信。其他app需要本app的db等数据时会用到这个组件。
2、项目实战:SonyAccountApp 中tokenId使用到这个。还添加了自定义权限控制。(相同签名、或者具备系统签名的app才能使用contentProvider活的到数据)
五、高级会问(这里为四大组件相关)
1、activity启动流程(流程简略总结:留意Application、context创建)
2、service启动流程 (流程简略总结:留意Application、context创建)
3、广播注册、监听流程(未做,先忽略。)
4、contentProvider启动流程(未做,先忽略)
5、本地广播源码设计(未做,先忽略)
6、Intentservice 设计(未做,这个可以总结下源码简单)
以上见后文单独总结。