Android 开发艺术与探究 第一章 Activity 的生命周期和启动模式

来源:互联网 发布:网站数据库下载代码 编辑:程序博客网 时间:2024/06/04 20:02

                     第一章Activity的生命周期和启动模式

                            正常情况下,Activity会经历如下生命周期。

                            (1)onCreate, Activity正在被创建,在这个方法我么可以做一下初始化工作。setContentView去加载布局,初始化Activity所需数据等。

                             (2)OnRestart,Activity正在重新启动,一般情况下,当前Actvity由不可见,变成可见,onRestart就会被调用。这种情形一般是用户导致的。按Home键切换到桌面或者用户打开一个新的Activity,这是当前的Activity就会暂停,执行onPause和onStop被执行了。接着用户又回到这个Activity就会出现这种情况

                           (3)onStart,Activity已结显示还不能和用户交互时调用。

                            (4) onResume 表示已经可见了,并且出现在前台,与onstart相比,onStart的时候Activity还在后台,onResume的时候Activity才显示到前台

                           (5)onPause Activity还在停止,正常情况下,紧接着onStop就会被调用,在特殊情况下,这个时候快速的在回到当前Activity,那么onResume会被调用,那么OnResume会被调用,这种情况属于极端情况,此时可做一些存储数据停止动画等工作,但是注意不能太耗时,因为这会影响到新Activity的显示,onPause必须先执行,新Activity的onResume才会执行          (也就是说只有执行了旧Activity的onpause,新Activity才会执行onCreate     后面还会做详细介绍)

                            

                            (6)onStop:表示Activity即将停止,可以稍微重量级的回收工作,同样不耗时,(特殊情况:在使用透明主题的情况下,onStop不会被执行

                         (7)onDestroy:表示Activity即将被销毁,这是Activity生命周期中的最后一个回调,在这里我们可以做一些回收工作和最终的资源释放。

                   生命周期是配对的,onCreate和onDestroy是配对的,分别标识者Activity的创建和销毁,并且只可能一次调用,从Activity是否可见来说,onStart和onStop是配对的,随着用户的操作或者屏幕的点亮,这两个Activity会被多次调用,从是否在前台来说,onResume和onPause是配对的,随着用户操作或者设备屏幕的点亮和熄灭者两个可能被调用多次,



              面试小提问:‘ 

                      1.   onStart和onResume,onPause和onStop从描述上来看差不多,对我们有什么实质的不同呢?

            

           从使用过程来说,onStart和onResume,onPause和onStop看起来的确差不多,甚至我们可只保留其中一对,比如只保留onStat和onStop既然如此为什么要提供看起来重复的接口,根据上面的分析,这两个配对的回调分别表示不同的意义,onStart和onStop是从Activity是否可见这个角度调用的,二OnResume和OnPause是从Activity是否位于前台这个角度调用的出了这种区别,在实际使用中没有其他明显的区别。


                       2.假设当前Activity为A,如果用户打开一个新的ActivityB,那么B的onResume和A的onPause哪个先执行呢?

          

                第二个问题,从Activity源码的角度来分析,在新的Activity启动之前,栈顶的Activity需要先onpause后,新Activity才能启动。根据实际实际效果打出的log

               A--onpause           B ---onCreate     B--onStart           B--onResume    A--Onstop

            

          从另一个角度,Android官方文档对onPause的解释有这么一句,不能再onpause中做重量级的操作,因为必须onPause执行完成以后新Activity才能Resume。通过分析这个问题,我们知道onStop中操作,儿时的新Activity尽快显示出来切换并且切换到前台,不能再onPause和onStop做耗时操作,尤其是onPause,这意味着我们应当尽量在OnStop中做操作,而使得新Activity尽快显示出来切换到前台。


                上面介绍了正常情况下Activity的生命周期,下面介绍Activity异常生命周期的分析



异常情况下的生命周期的分析


1.资源相关的系统配置发生改变导致Activity被杀死并重新创建。

                            我们首先要对系统资源加载机制有一定的了解,这里不详细分系统的资源加载机制,只是做一个简单说明,那最简单的图片来说,当我们把一张图片仿如drawable目录后,就可以通过Resources去获取这张图片,同事为了兼容不同的设备,我们可能还需要在奇台一些目录放置不同的图片,比如drawable-mdpi等,这样当应用程序启动时,系统就会根据当前设备的情况去加载合适的Resources资源,比如说横屏手机和竖屏手机会拿到两张不同的图片,比如说:当前Activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生改变(不止这一种情况),在默认情况下,Activity就会被从新创建,当然我们也可以组织系统重新创建我们Activity。


      在默认情况下,当我们Activity不做特殊处理,当系统配置发生改变后,Activity就会被从新创建,其嵊州周期如下所示:

       

          其中onPause,onStop ,onDestroy均会被调用,同时由于Activity是在一次情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法在调用时机是在OnStop之前,它和onPause没有既定的时序关系,它可能调用在onPause之前,亦可能在之后,需要强调的一点,当系统认为Activity要被异常终止时候调用这个方法,当Activity重新创建时,我们可以通过OnRestoreInstanceState和OnCreate方法来判断Activity是否被重建。从时序上来说,OnRestoreInstanceState的调用时机在OnStart之后,


         同时,我们要知道,在OnSaveInstanceState和 OnRestoreInstanceState方法中,系统自动。 为我们做一定的回复工作,当Activity在异常情况下重新创建时,系统会默认保存当前Activity的视图结构,并且在Activity重启后为我们回复这些数据,比如文本框中的输入数据,listView的滚动位置等,这些view相关状态系统都能够默认为我们恢复,具体针对某一个特定的View系统能为我们恢复哪一些数据,我们可以查看View的源码,和Activity一样,每个View都有OnsaveInstanceState和OnRestoreinstanceState方法,看一下具体实现,就能够知道系统能够自动为我们恢复哪一些数据。

      关于保存和恢复View层次结构,系统的工作流程,Activity会调用OnStaveInstanceState去保存数据,然后Activity会委托Window保存数据,接着Window在委托他什么的顶级容器去保存数据。顶层容器是一个ViewGroup。最后顶层容器会在一一去通知它的子元素保存数据,这样整个保存数据的过程也就完成了。上层委托下层的模式,类似View的绘制过程和View的时间处理机制。


     当调用onSaveInstanceState中存储一个字符串,然后当Activity销毁并重新创建后,接收的位置可以OnRestoreInstanceState,也可以是OnCreate,两者的区别。onRestoreInstanceState一但被调用,其中参数一定有值,我们不用额外的去判断是否为空,但是OnCreate不行,onCreate如果正常启动,参数Bundle可能为空,这两个方法我们选择任意一个都可以进行数据恢复,单官方文档建议是采用onRestoreInstanceState去恢复数据。

         (onSaveInstanceState方法还有一点需要说明:那就是系统只会在Activity即将销毁并且有机会重新显示的才会调用它。考虑一种情况,当Activity正常销毁的时候系统不会调用OnSaveInstanceState方法,因为被销毁的Activity不可能在被显示。) 屏幕旋转 Activity在即将销毁之后马上就会重新创建,所以会调用onSaveInstanceState方法。

 

  资源不足的情况下导致低优先级的Activity被杀死。这种情况不容易模拟,当系统内存不足时候,系统就会按照优先级杀死Activity,

            1.前台Activity  2.非可见Activity,导致Activity不可见,但是位于后台无法和用户直接交互   3.后台Activity


系统配置发生改变Activity会被重新创建,可以配置Android:configchanges="oreientation "等属性来组织Activity重启。




Activity启动模式

   前言:一个任务栈可以有多个实例,每个实例也可以属于不同的任务栈。

            standard:标准的启动模式,系统默认的模式。每次启动都会创建一个新的实例。在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。

         注意:当我们用Application去启动Standard的时候回报错,因为standard模式的Activity默认进入启动它的Activity的任务栈中,但是由于非Activity类型的Context并没有所谓的任务栈,所以这就有问题了。解决 这个问题的方法是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候会为它创建一个新的任务栈。


       singletop:栈顶复用模式,如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重复创建,同时它的ONNewIntent方法会被调用。



     singleTask:栈内服用模式,者是一种单实例模式,只用Activiy在一个栈中存在,那么多次启动此Activiy都不会重新创建实例,和singTop一样,系统也会调用其onNewIntent。

 具体一点:当一个具有singleTask模式的Activity请求启动后,系统首先寻找是否存在想要的Activity的任务栈,如果不存在就重新创建一个任务栈,然后创建Activity的实例后把Activity放入栈中,如果 存在所需的任务栈,并且A的实例存在,系统就会把Activity调到栈顶,并调用器onNewIntent方法,并且将Activity上面的Activity全部出栈,如果实例不存在将创建Activity并将其押入栈中。


   singleInstane;单列模式,是一个种加强singleTask,它具有所有SingleTask的所有特性外,那就是这种模式的Activity只能单独的位于一个任务栈中。

          

          

下面将singleTask做重点介绍:在singleTask中启动模式中,多次提到某个Activity所需的任务栈,什么是Activity所需的任务栈?这要从一个参数说起,

          TaskAffinty:这个参数标示了一个Activity所需要任务栈的名字。默认情况下,所有Activity所需的任务栈的名字为应用的包名,当然我们可以为每个Activity单独指定TaskAffinity属性。这个属性必须不能和包名相同,否则就相当于没有指定。TaskAffinty属性主要和singleTask启动模式或者AlllowTaskReparenting属性配合使用。在其他情况下没有意义。

        任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity处于暂停状态,用于可以通过切换将后台在任务栈调回前台


        当TaskAffinity和allowTaskReparenting结合的时候这种情况比较复杂,会产生特殊的效果,allowTaskReparenting设置为true,表示可以运行在别的任务栈中。



  Activity指定启动模式,有两个方法一种通过Android:luanchMOde="singleTask"启动,另一种通过Intent中设置标志位来启动。


           两种方法区别优先级代码用Intent的启动高于配置文件中的。配置文件中无法为Activity设置FLAG_ACTIVITY_CLEAR_TOP标识。第二种方式无法为Activity指定singleInstance模式。

         


Activity标记位Flags

  

    Activity标记位很多,

FLAG_ACTIVITY_NEW_TASK  这个标记位作用是为Activity指定singleTask启动模式。

FLAG_ACTIVITY_SINGLE_TOP,这个标记位作用是为Activity指定singleTop启动模式。

FLAG_ACTIVITY_CLEAR_TOP.这个标记为,当它启动时,在同一个任务栈所有位于它上面的Activity出栈,这个一般和LAG_ACTIVITY_NEW_TASK配合使用,在这种情况下,被启动的Activity如果已经存在,那么系统就会调用它的onNewIntent方法。如果启动模式为standard模式。那么连同它之上的Activity都要出栈。系统会创建新的Activity实例并放入栈顶。

               FLAG_ACTIVITY_EXCLUDE_FORM_RECENTS:具有这个标记位的Activity不会出现在历史Activity的列表中。等同于在XML中指定Activity属性android:excludeFormRecents="true"


     Activity的匹配规则:

          

          我们知道Activity启动方式分为两种,显示调用和隐示调用。显示调用不多做介绍。隐示调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标Activity,IntentFilter的过滤信息有Action,category,data。


       一个activity有多个Intent-filter,一个Intent匹配任何一组inten-filter即将可成功启动对应的Activity.


                   Action的匹配规则:

                   一个过滤当中有多个action,那么只要intent中的action能够和过滤规则中的任何一个action相同匹配成功即可。    需要注意的是:如果Intent中没有指定Action那么匹配失败。  总结:action的匹配要求Intent中的action存在且和过滤规则总的其中一个action相同,(也就是说还可以有Action不需要跟过滤规则中的相同)      action区分大小写


                  category的匹配规则:

                          category的匹配规则和action不同,它要求如果Intent中含有category,那么所有的category都必须和过滤规则中的一个category相同。换句话说,只有Intent中存在category不管有几个,对于每个category来说,它必须是过滤规则中定义的category。由于系统在调用startActivity或者startActivityforResult的时候默认会加上Android.intent.category.DEFAULT,所有我们在写Inten-filter的时候尽量带上android.inten.category.DEFAULT.

             


               data的匹配规则:


data语法:

                                 <data android:scheme="sting"  android:host="string“  android:port="String"  anroid:path="string" anroid:pathPattern="stirng" android:pathPrefile=”string"  android:mimeType=“string"/>


                                   data由两部分组成:

                                           mimeType和URI。mimeType指媒体类型。

                                          URI的结构:

                                          <scheme>://<host>:<port>/[<path>|<pahtPrefix|<pathPattern>


                                         scheme:URI的模式,比如http,file,content,如果URI中没有指定scheme,那么整个URI的其他参数无效。

                                         HOST:URI的主机名,比如www.baidu.com.如果Host未指定,那么整个URI的其他参数无效

                                         Port:URI 的端口号,比如80,仅当URI指定了scheme和host参数port参数才能有意义。

                                         path.pathPattern和pathPrefix,这三个参数表示路径信息, path表示完整路径,pahtPattern表示也是完整路径,但是可以包含通配符"*","*"表示0个或者多个字符串。pathPrefix表示路径的前缀信息。

                                         data的匹配 规则:

                                          <data android:mimeType="image/*">

                                         这种情况下。那么Inten中的mimeType属性必须有Image/*才能够匹配。虽然没有指定uri但是却有默认值。uri的默认值为content或者file.也就是说。虽然没有指定URI,但是Intent中URI部分的scheme必须为content或者file才能匹配


                    注意:

                           intent.SetDataAndType(URi.parse("file://abc"),"image/png");

                       另外如果要完整的data必须调用setDataAndType方法。setData在和setTye会清除对方的的值



  最后

  通过隐示启动的Intent的时候,可以做一些判断,看是否有Activity能偶匹配我们Intent,否则就会报Activity找不到错误。判断有方法有两种,采用PackageManager,resolveActivity方法或Intent的resolveActivity方法,如果找不到匹配的Activity就会返回null 。

        也可以通过PagemanagerrueryIntentActvityes方法,这个方法和resolveActivity不同的是,它不是返回最佳的匹配Activity信息,而是所有符合匹配规则的Activity的信息的集合。 如下:

     public abstract List<ResolveInfo> queryIntentActivities(Intent intent,inf flags);

      public abstract ResolveInfo ResolveActivity(Intent intent,int Flags);

   

        第一个参数比较好理解,第二个参数需要注意,我们使用MATCH_DEFAULT_ONLY这个标记位。这个标记位的含义是仅仅匹配那一些在intent-filter中声明了<category andr oid:name="android.intent.category.DEFAULT"/> 这个category的Activity。

    使用这个标记意义在于,只要上述方法不反回null,那么startActivity一定可以匹配成功。(因为startActivity方法自带<category andr oid:name="android.intent.category.DEFAULT"/> ,根据上面的category匹配规则。) 如果不含这个标记可能导致startActivity失败。


          有一类action和category比较重要,他们是

                            <action adroid:name=“android.intent.action.main>

                           <category android:name="android.intent.categor.LAUNCHER>

        这二者公同的作用表面Activity的入口。并且会出现在系统的应用列表中,少了任何一个都没有实际意义。也无法出现在系统的应用列表当中。也就是这二者缺一不可

        

     另外。针对Service和BroadcastReceiver.Packagemanager同样提供了类似的方法去获取成功匹配的组件信息

    


          



 


                        


                               



   


 


  



          






   



                               


0 0
原创粉丝点击