天天看点

基础篇-Activity解析一,Activity生命周期二.Fragment 碎片三.Activity的加载模式解析 四.Activity与Window,View的关系解析

一,Activity生命周期

基础篇-Activity解析一,Activity生命周期二.Fragment 碎片三.Activity的加载模式解析 四.Activity与Window,View的关系解析

相信不少朋友也已经看过这个流程图了,也基本了解了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生命周期二.Fragment 碎片三.Activity的加载模式解析 四.Activity与Window,View的关系解析

对比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();  //窗口类容发生变化时更新

        }

    }

继续阅读