——个人平时笔记,看到的同学欢迎指正错误,文中多处摘录于各大博主与书籍精华
生命周期时间:Activity 5秒,Service 取决于系统回收杀死的时间,BoradcastReceiver 10秒,ContentProvider 注册创建即存在直到应用被卸载
1、Activity是一种展示型组件。Activity的启动由Intent触发,其中Intent可以分为显式Intent和隐式Intent,显式Intent可以明确地指向一个Activity组件,隐式Intent则指向一个或多个目标Activity组件。
在需要使用Activity的Context的情况下,尽量使用ApplicationContext替代,这样可以避免Activity被回收时还被其他外部引用而导致无法回收,造成内存溢出。
Activity四种启动模式:
1.standard:标准模式,也是默认模式。每次启动一次activity都会创建一个新的activity实例,加入任务栈中。
2.singleTop:栈顶复用模式。如果要启动的新activity已经位于任务栈栈顶,则会复用该activity不会生成新的activity实例,并且会回调onNewIntent()方法
3.singleTask:栈内复用模式。如果要启动的新activity已经存在于在当前任务栈栈中,则会踢出位于它同一栈顶的其他activity,直至要启动的activity位于当前栈顶,并且会回调onNewIntent()方法
4.singleInstance:单一实例模式,单例模式。它具有singleTask所有的特性。当前模式的activity只能单独的位于一个任务栈中,如果已经存在于一个任务栈中,则会复用,并且会回调onNewIntent()方法
静态设置启动模式和 动态启动FLAG 的区别主要在于:前者是固定的,后者是可组合的。例如 singleTask 是固定的、而 FLAG_ACTIVITY_CLEAR_TOP(近似于 singleTask 模式)是可组合的:当你多数场景下不合适使用 SingleTask,但某个别场景又需要它的这种清空栈中上方 Activity 的特性,那么此时你就选用 singleTop + FLAG_ACTIVITY_CLEAR_TOP的FLAG 的组合,如此,在清单中你配置的是 singleTop,但是仅在你需要清空效果的场景下,在代码中动态附加 FLAG 来组合使用。
FLAG_ACTIVITY_NEW_TASK这个Flag跟我们的singleInstance很相似,在给目标Activity设立此Flag后,会根据目标Activity的affinity进行匹配:
如果已经存在与其affinity相同的task,则将目标Activity压入此Task。反之没有的话,则新建一个task,新建的task的affinity值与目标Activity相同。然后将目标Activity压入此栈。其实简单来说,就是先看看需不需要创建一个新的Task,依据就是有没有相同的affinity。然后把Activity放进去。
但是此情况和静态singleInstance有不同,有两点注意的地方:
1.新的Task没有说只能存放一个目标Activity。只是说决定是否新建一个Task。而静态singleInstance模式下新的Task只能放置一个目标Activity。
2.在同一应用下,如果Activity都是默认的affinity,那么此Flag无效。而singleInstance默认情况也会创建新的Task。
异常状态时数据保存:
Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态。
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
2、Service是一种计算型组件,用于在后台执行一系列计算任务。Service组件有两种状态:启动状态和绑定状态。Service的这两种启动状态是可以共存的。
启动状态时:Service组件可以在后台执行计算,但是它本身是运行在主线程中的,因此耗时的后台计算仍然需要在单独的线程中去完成。结束该Service也只需要调用一次stopService。
绑定状态时:同样也可在后台执行计算,但是处于这种状态时外界可以很方便地和Service组件进行通信。多次使用bindService绑定同一个Service时,Service的onBind方法只会执行一次,手动调用unbindService()后解绑。
生命周期说明:
- onCreate():首次创建服务时,系统将调用此方法。如果服务已在运行,则不会调用此方法,该方法只调用一次。
- onStartCommand():当另一个组件通过调用startService()请求启动服务时,系统将调用此方法。
- onDestroy():当服务不再使用且将被销毁时,系统将调用此方法。
- onBind():当另一个组件通过调用bindService()与服务绑定时,系统将调用此方法。
- onUnbind():当另一个组件通过调用unbindService()与服务解绑时,系统将调用此方法。
- onRebind():当旧的组件与服务解绑后,另一个新的组件与服务绑定,onUnbind()返回true时,系统将调用此方法。
生命周期调用
1)startService启动Service服务
单次:startService() —> onCreate() —> onStartCommand()
多次:startService() —> onCreate() —> onStartCommand() —> onStartCommand()
2)停止Service服务
stopService() —> onDestroy()
1)bindService绑定Service服务
bindService() —> onCreate() —> onBind()
2)解绑Service服务
unbindService() —> onUnbind() —> onDestroy()
1)启动绑定Service服务
startService() —> onCreate() —> onStartCommand() —> bindService() —> onBind()
2)解绑停止Service服务
unbindService() —> onUnbind() —> stopService() —> onDestroy()
3)解绑绑定Service服务
unbindService() —> onUnbind(ture) —> bindService() —> onRebind()
3、BroadcastReceiver是一种消息型组件,用于在不同的组件乃至不同的应用之间传递消息。通过Context的一系列send方法来发送广播,发送和接收过程的匹配是通过广播接收者的<intent-filter>来描述的。BroadcastReceive广播有两种方式注册:静态注册和动态注册。
静态注册
是指在AndroidManifest中注册的广播,这种广播在应用安装时会被系统解析,此种形式的广播不需要应用启动就可以收到相应的广播。动态注册广播
需要通过Context.registerReceiver()来实现,并且在不需要的时候要通过Context.unRegisterReceiver()来解除广播,否则容易造成内存泄漏。此种形态的广播必须要应用启动才能注册并接收广播,因为应用不启动就无法注册广播,无法注册广播就无法收到相应的广播。同等级,动态比静态广播优先接收信息。普通广播的发送
Context类提供两个方法可以用于发送普通广播,差别是第二个设置权限:
sendBroadcast(Intent intent);
sendBroadcast(Intent intent, String receiverPermission);
发给特定的用户:
sendBroadcastAsUser(Intent intent, UserHandle user);
sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission);有序广播的发送
有序广播会按照处理器的不同优先级来区分的,高优先级的处理器会优先截获这个消息,并且可以将这个消息删除。
有序广播因为要处理消息的处理结果,所以要复杂一些。
sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);
如果只是想让广播可以按优先级来收取,并不在意处理的结果,可以用下面的版本:
sendOrderedBroadcast(Intent intent, String receiverPermission);
同样,在多用户环境下,也可以选择给指定用户发广播:
sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);粘性广播(安卓5.0即API 21开始废除了)
粘性消息在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理,如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态,粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据。
不管是普通的还是有序的广播都对应有粘性的版本:
sendStickyBroadcast(Intent intent);
sendStickyBroadcastAsUser(Intent intent, UserHandle user);
sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);
sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);
4、ContentProvider是一种数据共享型组件,用于向其他组件乃至其他应用共享数据。它的内部实现了增删改查这四种操作,在它的内部维持着一份数据集合,这个数据集合既可以通过数据库来实现,也可以采用其他任何类型来实现,比如List和Map;ContentProvider对数据集合的具体实现并没有任何要求。需要注意的是,ContentProvider内部的insert、delete、update和query方法需要处理好线程同步,因为这几个方法是在Binder线程池中被调用的,另外ContentProvider组件也不需要手动停止。ContentProvider的onCreate要先于Application的onCreate而执行,这在四大组件中是个例外。
从数据共享的角度出发,ContentProvider应该是Android在系统启动时就创建了,否则就谈不上数据共享了。
android contentResolver的使用
ContentProvider讲解与实例应用
5、Fragment碎片的生命周期
6、Context使用:
Context类本身是一个纯abstract类,它有两个具体的实现子类:ContextImpl和ContextWrapper。其中ContextWrapper类,如其名所言,这只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。ContextThemeWrapper类,如其名所言,其内部包含了与主题(Theme)相关的接口,这里所说的主题就是指在AndroidManifest.xml中通过android:theme为Application元素或者Activity元素指定的主题。当然,只有Activity才需要主题,Service是不需要主题的,因为Service是没有界面的后台场景,所以Service直接继承于ContextWrapper,Application同理。而ContextImpl类则真正实现了Context中的所以函数,应用程序中所调用的各种Context类的方法,其实现均来自于该类。一句话总结:Context的两个子类分工明确,其中ContextImpl是Context的具体实现类,ContextWrapper是Context的包装类。Activity,Application,Service虽都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们初始化的过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法。
正确使用Context
一般Context造成的内存泄漏,几乎都是当Context销毁的时候,却因为被引用导致销毁失败,而Application的Context对象可以理解为随着进程存在的,所以我们总结出使用Context的正确姿势:
1:当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
4:一句话总结:凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏。