一,Activity生命周期
相信不少朋友也已经看过这个流程图了,也基本了解了Activity生命周期的几个过程,我们就来说一说一些常见操作的生命周期执行情况吧
(1) 启动Activity:onCreate()->onStart()->onResume->Activity进入运行状态
(2) 被其他Activity,窗口覆盖或锁屏:onPause()->暂停当前Activity状态
(3)
当前Activity由被覆盖状态回到前台或解锁屏:
onResume()->再次进入运行状态
(4)
当前Activity转到新的Activity界面或按Home键回到主屏,自身退居后台:onPause()->onStop->不可见状态
(5) 回退到不可见状态的Activity:
onRestart()->onStart()->onResume->再次进入运行状态
(6)
当前Activity处于被覆盖状态或者后台不可见状态,即第2步和第4步,系统内存不足,杀死当前Activity,而后用户退回当前Activity:onCreate()->onStart()->onResume()->进入运行状态
(7)
用户finish退出当前Activity:onPause()->onStop()->onDestory()->结束当前Activity
扩展一些:
1 . onWindowFocusChanged:在
Activity窗口获得或失去焦点时被调用,在onResume与onPause后调用
onResume()->
onWindowFocusChanged()->运行状态->onPause->
onWindowFocusChanged()
比如onCreate中Window对象没有初始化完成,一些动态计算控件大小,动画加载可能报错,所以可以将加载相关的代码放在这个方法中执行
2 .
onSaveInstanceState与
onRestoreInstanceState:
(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,
onSaveInstanceState
会被调用,回退到此Activity时,调用
onRestoreInstanceState
(2)在用户改变屏幕方向时,
系统先销毁当前的Activity,然后再重建一个新的,调用此方法保存一些临时数据
;
(3)在当前Activity跳转到其他Activity或者按Home键回到主屏,自身退居后台时,此方法会被调用。系统调用此方法是为了保存当前窗口各个View组件的状态.
保存状态时调用顺序:运行状态->
onSaveInstanceState()->onPause()
恢复状态时调用顺序:onStart()->onRestoreInstanceState()->onResume
3.onConfigurationChange():当指定了android:configChanges="orientation"后,方向改变时onConfigurationChanged被调用,可以setContentView(R.layout.orientation_landscape),设置Activity不同的xml布局文件适配
二.Fragment 碎片
1.产生与介绍
为了让界面可以在平板上更好地展示,Android在3.0版本引入了Fragment(碎片)功能,它非常类似于Activity,可以像Activity一样包含布局。Fragment通常是嵌套在Activity中使用的,
可以把Fragment当成Activity的一个界面的一个组成部分,
Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添加、替换和移除某个Fragment.碎片化管理屏幕显示的思路
比如,一个应用,其中2个页面,listview列表显示所有内容项FragmentA,对应详情页FragmentB,在竖屏时只嵌入FragmentA,横屏时,嵌入FragmentA与FragmentB,并列左边列表,右边详情.
以及ViewPager+Fragment导航栏组合的应用主页等
2.生命周期
对比Activity的生命周期,可见Activity的回掉方法碎片中几乎都有,碎片还提供了一些附加方法
- onAttach() 当碎片和活动建立关联的时候调用,add后, 在这个方法中可以获得所在的activity,
- onCreateView()为碎片创建视图(加载布局)时调用
- onActivityCreated()确保与碎片相关联的活动一定已经创建完毕的时候调用
- onDestroyView()当与碎片关联的视图被移除的时候调用
- onDetach()当碎片和活动解除关联的时候调用
(1).第一次加载屏幕:
onAttach() -- onCreate()--- onCreateView()--onActivityCreated()--onStart()--onResume()
(2)点击替换:
onPause()--onStop()---onDestoryView() (如果替换的时候没有调用addToBackStack()方法,此时onDestory()--onDetach()方法也会得到执行)
(3)
当点击Back重新回到这个Frament界面:
o
nCreateView
()--onStart()--onResume() (onCreate()和
onAttach
()并没有执行,所以我们使用的addToBackStacck()方法使得Fragment没有销毁,与Activity解绑)
(4)按下back时响应:
onPause()--onStop()---onDestoryView()--onDestory()--onDetach()
动态添加碎片 主要有以下五个步骤:
- 创建待添加碎片实例
- 获取到FragmentManager,在活动中可以直接调用getFragmentManager()方法得到
- 开启一个事务,FragmentTransaction transaction = fm.benginTransatcion();通过调用beginTransaction()方法开启,保证Fragment操作的原子性
- 向容器内加入碎片,一般使用transaction.replace()方法实现,就是remove和add合体,需要传入容器的ID和待添加的碎片实例
- transaction. add(),remove(),hide(),show(),detach(),attach()
- 提交事物,调用commit()方法来完成
实用情景
a、比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。
b、再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。
c、remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach
Fragment与Activity通信
因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:
a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。
c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。
使用DialogFragment来管理对话框,当旋转屏幕和按下后退键时可以更好的管理其声明周期,它和Fragment有着基本一致的声明周期。且DialogFragment也允许开发者把Dialog作为内嵌的组件进行重用,类似Fragment(可以在大屏幕和小屏幕显示出不同的效果)
FragmentTransaction解析:
Fragment事务使得你可以执行一系列fragment操作,不幸的是,提交事务是异步的,而且是附加在主线程handler队列尾部的。当你的app接收到多个点击事件或者配置发生变化时,将处于不可知的状态。楼主就因为曾在复用DialogFragmeng窗口时,由于交互操作导致并发触发,虽然用DialogFragmeng.isAdded(),进行了过滤判断,但是,还是会出现Fragmeng Already add异常,还无法捕捉,导致未知的程序闪退.就是因为在判断DialogFragmeng.isAdded()的时候,并发的DialogFragmeng事物提交的还在handler队列里面,未真正执行,而到了handler中,要执行的时候,Activity状态未知对于,Fragment来说就很危险.所以在使用是,进行生命周期管理,要谨慎处理
提示:对于每个Fragment事务,你能够在提交之前通过调用setTransition()方法,申请一个过渡动画。
调用commit()方法并不立即执行这个事务,而是在Activity的UI线程之上(”main”线程)调度运行,以便这个线程能够尽快执行这个事务。但是,如果需要,可以调用来自UI线程的executePendingTransactions()方法,直接执行被commit()方法提交的事务。通常直到事务依赖其他线程的工作时才需要这样做。
警告:
你能够使用commit()方法提交一个只保存之前Activity状态的事务(在用户离开Activity时)。如果试图在用户离开Activity之后提交,将会发生一个异常。这是因为如果Activity需要被恢复,而提交之后的状态却丢失了。这种情况下,使用commitAllowingStateLoss()方法,你丢失的提交就没问题了。
三.Activity的加载模式解析
1.Activity stack
每个Activity的状态是由它在Activity栈中的位置决定的。
Activity栈是一个后进先出LIFO,包含所有正在运行Activity的队列
当一个新的Activity启动时,当前的活动的Activity将会移到Activity栈的顶部。
如果用户使用后退按钮返回的话,或者前台的Activity结束,在栈上的Activity将会移上来并变为活动状态
一个应用程序的优先级是受最高优先级的Activity影响的。当决定某个应用程序是否要终结去释放资源,Android内存管理使用栈来决定基于Activity的应用程序的优先级。
2.Activity状态
一般认为Activity有以下四种状态:
活动的:当一个Activity在栈顶,它是可视的、有焦点、可接受用户输入的。Android试图尽最大可能保持它活动状态,杀死其它Activity来确保当前活动Activity有足够的资源可使用。当另外一个Activity被激活,这个将会被暂停。
暂停:在很多情况下,你的Activity可视但是它没有焦点,换句话说它被暂停了。有可能原因是一个透明或者非全屏的Activity被激活。
当被暂停,一个Activity仍会当成活动状态,只不过是不可以接受用户输入。在极特殊的情况下,Android将会杀死一个暂停的Activity来为活动的Activity提供充足的资源。当一个Activity变为完全隐藏,它将会变成停止。
停止:当一个Activity不是可视的,它“停止”了。这个Activity将仍然在内存中保存它所有的状态和会员信息。尽管如此,当其它地方需要内存时,它将是最有可能被释放资源的。当一个Activity停止后,一个很重要的步骤是要保存数据和当前UI状态。一旦一个Activity退出或关闭了,它将变为待用状态。
待用: 在一个Activity被杀死后和被装在前,它是待用状态的。待用Acitivity被移除Activity栈,并且需要在显示和可用之前重新启动它。
3.四种加载模式
Activity中特性launchMode的
四种加载模式解析:
标准,单顶(栈顶复用),单栈(单栈共享),单例(多栈共享)。
- standard: 标准模式,一调用startActivity()方法就会产生一个新的实例。
- singleTop: 如果已经有一个实例位于Activity栈的顶部时,就不产生新的实例,而只是调用Activity中的newInstance()方法。如果不位于栈顶,会产生一个新的实例,。
- singleTask: 会在一个新task的栈中产生这个实例,以后每次调用都会使用这个,不会去产生新的实例了,并且处于栈低,加入其它Activity,返回自动弹出销毁。
- singleInstance: 这个跟singleTask基本上是一样,会在一个新task的栈中产生这个实例.只有一个区别:在这个模式下的Activity实例所处的task中,只能有这个activity实例,不能有其他的实例, 是其所在栈的唯一activity,整个系统单例,任何task中调用,它会每次都被重用,多栈共用,2个应用调用显示同一个Activity,第一个中退出,只是把这个栈移开了,第二个退出时,这个activity栈才会退出。
在Android平台上可以将task简单的理解为由多个Activity共同协作完成某项应用任务,而不管Activity具体属于哪个Application,每一个Task有自己对应的 Activity Stack.
Activity的加载模式受启动Activity的Intent对象中设置的Flag和manifest文件中Activity的<activity>元素的特性值交互控制。
下面是影响加载模式的一些特性
核心的Intent Flag有:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
FLAG_ACTIVITY_SINGLE_TOP
核心的<activity>特性有:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunc
加载模式便是决定以哪种方式启动一个跳转到某个Activity实例,各种加载模式区别在于下面几点:
(1)所属task的区别
一般情况下,“standard”和”singleTop”的activity的目标task,和收到的Intent的发送者在同一个task内,就相当于谁调用它,它就跟谁在同一个Task中。 除非Intent包括参数FLAG_ACTIVITY_NEW_TASK。设置FLAG_ACTIVITY_NEW_TASK参数,会启动到别的task里。
“singleTask”和”singleInstance” 总是把要启动的activity作为一个task的根元素,他们会被启动到一个其他task里。
(2)是否允许多个实例
“standard”和”singleTop”可以被实例化多次,并且是可以存在于不同的task中;这种实例化时一个task可以包括一个activity的多个实例;
“singleTask”和”singleInstance”则限制只生成一个实例,并且是task的根元素。
singleTop 要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不创建新的实例。
(3)是否允许其它activity存在于本task内
“singleInstance”独占一个task,其它activity不能存在那个task里;
即便此Activity又启动了一个新的activity,不管新的activity的launch mode 如何,新的activity都将会到别的task里运行(如同加了FLAG_ACTIVITY_NEW_TASK参数)。 而另外三种模式,则可以和其它activity共存。
(4)是否每次都生成新实例
“standard”对于每一个启动Intent都会生成一个activity的新实例;
“singleTop”的activity如果在task的栈顶的话,则不生成新的该activity的实例,直接使用栈顶的实例,否则,生成该activity的实例。
“singleInstance”是其所在栈的唯一activity,所有task共享,它会每次都被重用。
“singleTask” 如果不存在,则在新task中生成实例,存在即复用,弹出该task栈中其他activity
。
四.Activity与Window,View的关系解析
Android系统中的所有UI类都是建立在View和ViewGroup这两个类的基础上的。所有View的子类成为”Widget”,所有ViewGroup的子类成为”Layout”。View和ViewGroup之间采用了组合设计模式,可以使得“部分-整体”同等对待。ViewGroup作为布局容器类的最上层,布局容器里面又可以有View和ViewGroup。LayoutInfalter就是用来生成View的一个工具,XML布局文件就是用来生成View的原料
LayoutInflater是一个用来实例化XML布局文件为View对象的类
LayoutInflater.infalte(R.layout.test,null)用来从指定的XML资源中填充一个新的View
Activity构造的时候只能初始化一个Window(PhoneWindow),另外这个PhoneWindow有一个”DecorView”,是主窗口中顶级view,这个”DecorView”是一个ViewGroup,是最初始的根视图,之后不断重叠ad view,removeview管理,放到window显示窗,
还可以onCreater中设置窗口,显示属性,比如设置无标题,全屏等。
getWindow().setContentView(LayoutInflater.from(this).inflate(R.layout.main, null)),
public void setContentView(View view,ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged(); //窗口类容发生变化时更新
}
}