美文网首页
Android开发一《Activity的生命周期和启动模式》

Android开发一《Activity的生命周期和启动模式》

作者: 独自闯天涯的码农 | 来源:发表于2022-03-21 19:55 被阅读0次

    一、Activity的生命周期全面分析

    Activity生命周期

    注意:

    1、情况 1:资源相关的系统配置发生改变导致Activity被杀死并重新创建
    onSaveInstanceState方法在onStop之前;  
    onRestoreInstanceState在onStart之后;
    和onPause没有既定的时序关系;
    

    当系统配置发生改变后,Activity会被销毁,其onPause、onStop、onDestory均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法的调用时机是在onStop之前,它和onPause没有既定的时序关系,它既可能在onPause之前调用,也可能在onPause之后调用。需要强调的一点是,这个方法只会出现在Activity被异常终止的情况下,正常情况下系统不会回调这个方法。如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState的调用时机在onStart之后。

    关于保存和恢复View层次结构,系统的工作流程是这样的:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window再委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说它很可能是DecorView。最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这是一种典型的委托思想,上层委托下层、父容器委托子元素去处理一些事情,这种思想在Android中有很多应用,比如View的绘制过程、事件分发等都是采用类似的思想;
    onRestoreInstanceState或者onCreate,二者的区别是:onRestoreInstanceState一旦被调用,其参数Bundle savedInstanceState一定是有值的,我们不用额外地判断是否为空;但是onCreate不行,onCreate如果是正常启动的话,其参数Bundle savedInstanceState为null,所以必须要额外判断。这两个方法我们选择任意一个都可以进行数据恢复,但是官方文档的建议是采用onRestoreInstanceState去恢复数据。

    当然可以指定系统配置防止Activity被重新创建

    android:configChanges="orientation|keyboardHidden"
    
    configChanges配置内容
    2、情况2:资源内存不足导致低优先级的Activity被杀死

    等级由低到高清除

    (1)前台Activity——正在和用户交互的Activity,优先级最高。
    (2)可见但非前台Activity——比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互。
    (3)后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低。
    
    3、onNewIntent方法执行时期
    1、其他Activity启动存在的SingleTask, singleInstance的Activity
    onNewIntent-》onRestart -》onStart -》onResume
    2、本Activity启动存在的SingleTop,SingleTask, singleInstance的本Activity
    onPause -》onNewIntent -》onResume
    

    注意:在onNewIntent()里面设置setIntent()才能获取最新的数据;

    4、onActivityResult方法执行时期
    onActivityResult -》onStart -》onResume
    

    如果跳转的Activity是android:launchMode="singleInstance"属性,Activity一定在新的任务(进程)中,和原来的Activity不在同一进程中,onActivityResult函数表现异常;onActivityResult在startActivityForResult执行的一瞬间就被调用了。

    二、Activity的启动模式

    Activity 的启动模式

    启动模式 启动模式区别

    standard:这个是android默认的Activity启动模式,每启动一个Activity都会被实例化一个Activity,并且新创建的Activity在堆栈中会在栈顶。

    singleTop:如果当前要启动的Activity就是在栈顶的位置,那么此时就会复用该Activity,并且不会重走onCreate方法,会直接它的onNewIntent方法,如果不在栈顶,就跟standard一样的。如果当前activity已经在前台显示着,突然来了一条推送消息,此时不想让接收推送的消息的activity再次创建,那么此时正好可以用该启动模式,如果之前activity栈中是A-->B-->C如果点击了推动的消息还是A-->B--C,不过此时C是不会再次创建的,而是调用C的onNewIntent。而如果现在activity中栈是A-->C-->B,再次打开推送的消息,此时跟正常的启动C就没啥区别了,当前栈中就是A-->C-->B-->C了。

    singleTask:该种情况下就比singleTop厉害了,不管在不在栈顶,在Activity的堆栈中永远保持一个。这种启动模式相对于singleTop而言是更加直接,比如之前activity栈中有A-->B-->C---D,再次打开了B的时候,在B上面的activity都会从activity栈中被移除。下面的acitivity还是不用管,所以此时栈中是A-->B,一般项目中主页面用到该启动模式。

    singleInstance:该种情况就用得比较少了,主要是指在该activity永远只在一个单独的栈中。一旦该模式的activity的实例已经存在于某个栈中,任何应用在激活该activity时都会重用该栈中的实例,解决了多个task共享一个activity。其余的基本和上面的singleTask保持一致。

    上面的各种启动模式主要是通过配置清单文件,常见还有在代码中设置flag也能实现上面的功能:

    FLAG_ACTIVITY_CLEAR_TOP:这种启动的话,只能单纯地清空栈上面的acivity,而自己会重新被创建一次,如果当前栈中有A-->B-->C这几种情况,重新打开B之后,此时栈会变成了A-->B,但是此时B会被重新创建,不会走B的onNewIntent方法。这就是单独使用FLAG_ACTIVITY_CLEAR_TOP的用处,能清空栈上面的activity,但是自己会重新创建。
    如果在上面的基础上再加上FLAG_ACTIVITY_SINGLE_TOP此时就不重新创建B了,也就直接走B的onNewIntent。它两者结合着使用就相当于上面的singleTask模式。如果只是单独的使用FLAG_ACTIVITY_SINGLE_TOP跟上面的singleTop就没啥区别了。

    FLAG_ACTIVITY_CLEAR_TOP+FLAG_ACTIVITY_SINGLE_TOP=singleTask,此时要打开的activity不会被重建,只是走onNewIntent方法。

    FLAG_ACTIVITY_SINGLE_TOP=singleTop
    FLAG_ACTIVITY_NEW_TASK

    在相同taskAffinity情况下:启动activity是没有任何作用的。
    在不同taskAffinity情况下:如果启动不同栈中的activity已经存在了某一个栈中的activity,那么此时是启动不了该activity的,因为栈中已经存在了该activity;如果栈中不存在该要启动的activity,那么会启动该acvitity,并且将该activity放入该栈中。
    FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP一起使用,并且要启动的activity的taskAffinity和当前activity的taskAffinity不一样才会和singleTask一样的效果,因为要启动的activity和原先的activity不在同一个taskAffinity中,所以能启动该activity,这个地方有点绕,写个简单的公式:

    FLAG_ACTIVITY_NEW_TASK如果启动同一个不同taskAffinity的activity才会有效果。
    FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP如果一起使用要开启的activity和现在的activity处于同一个taskAffinity,那么效果还是跟没加FLAG_ACTIVITY_NEW_TASK是一样的效果。
    FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP启动和现在的activity不是同一个taskAffinity才会和singleTask一样的效果。
    FLAG_ACTIVITY_CLEAR_TASK

    在相同taskAffinity情况下:和FLAG_ACTIVITY_NEW_TASK一起使用,启动activity是没有任何作用的。
    在不同taskAffinity情况下:和FLAG_ACTIVITY_NEW_TASK一起使用,如果要启动的activity不存在栈中,那么启动该acitivity,并且将该activity放入该栈中,如果该activity已经存在于该栈中,那么会把当前栈中的activity先移除掉,然后再将该activity放入新的栈中。
    FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_SINGLE_TOP 用在当app正在运行点击push消息进到某个activity中的时候,如果当前处于该activity,此时会触发activity的onNewIntent。
    FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_CLEAR_TOP用在app没在运行中,启动主页的activity,然后在相应的activity中做相应的activity跳转。

    三、IntentFilter的匹配规则

    Actvitiy分为两种启动模式,分别是显式调用和隐式调用。隐式调用需要指定IntentFilter,其中包含了action、category、data。只有三个同时匹配时(不需要三个必须同时存在,如果没有则默认匹配成功),才能算IntentFilter匹配成功。此外一个activity可以有多个IntentFilter,一个intent只需要匹配成功一个就算匹配成功,能够启动对应的activity。

    匹配规则
    1、action

    action是一个字符串。一个IntentFilter可以有多个action,只需要匹配任意一个action,就算action项匹配成功。action的匹配规则是,intent中的action字符串必须和IntentFilter中的action的字符串值完全相同,并且区分大小写。

    2、category

    category也是一个字符串。系统预定义了一些category,也可以自定义category。一个IntentFilter中可以有多个category,但在匹配时,intent中添加的所有category必须匹配所有的category才能算匹配成功。此外如果intent不设置category,则有默认值default

    3、data

    data由两部分组成,分别是miniType和URI,miniType指的是媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*。而uri的结构如下所示。

    <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
    举几个例子
    content://com.example.project:200/folder/subfolder/etc
    http://www.baidu.comn:80/search/info
    
    scheme:代表url的模式,比如http、file、content等,这是必填项
    host:代表uri的主机名,如www.baidu.com,这也是必填项。
    port:uri中的端口号,这是选填项。
    path、pathPrefix、pathPattern:这三个参数表示路径的信息,其中path表示完整路径,pathPrefix表示带有路径的前缀信息,pathPattern代表有通配符“*”表达式来匹配路径。
    

    匹配符号:

    “*” 用来匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…
    “.” 用来匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…
    因此 “.
    ” 就是用来匹配任意字符0次或更多,如:“.*html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”…

    data的匹配规则

    和action一样,只需要匹配manifest中任意一个即可

    比如如下所示的代码,image/*代表匹配所有类型的图片,此时虽没有制定uri,但是有默认值content和file,也就是说intent中的data的schema必须为content或者file才能匹配。

    <intent-filter>
        ……
      <data android:mimeType="image/*"/>
    </intent-filter>
    

    注意:
    1、注意当需要同时设置uri和type的时候,必须使用setDataAndType。如果分开调用setData和setType,都会先清除对方的值,在进行赋值。

    intent.setDataAndType(Uri.parse("file://abc"),"image/*");
    

    2、uri和url有什么区别?
    uri是为了能唯一标识某个资源,url则是通过地址的方式,标识某个资源,因此url是uri的子集,是一种具体的实现方式。

    参考:Activity的知识

    四、Fragment生命周期

    1、Fragment 作用

    Android 在 Android 3.0 (API 11)中引入了片段,主要是为了给更大的屏幕(如平板电脑)上更加动态和灵活的 UI 设计提供支持。由于平板屏幕比手机的屏幕大得多,也能显示更多的布局和组件。

    2、Fragment生命周期

    Fragment生命周期
    onAttach() 在Fragment 和 Activity 建立关联是调用(Activity 传递到此方法内)
    onCreateView() 当Fragment 创建视图时调用
    onActivityCreated() 在相关联的 Activity 的 onCreate() 方法已返回时调用。
    onDestroyView() 当Fragment中的视图被移除时调用
    onDetach() 当Fragment 和 Activity 取消关联时调用。
    
    Activity生命周期和Fragment生命周期联系

    -> Activity.onAttach -> Fragment.onAttch
    -> Fragment.onCreate -> Fragment.onCreateView
    -> Activity.onCreate -> onActivityCreated ->
    -> Activity.onStart -> Fragment.onStart
    -> Activity.onResume -> Fragment.onResume
    -> Fragment.onPause -> Activity.onPause
    -> Fragment.onStop -> Activity.onStop
    -> Fragment.onStop -> Activity.onStop
    -> Fragment.onDestroyView -> Fragment.onDestroy
    -> Fragment.onDetach -> Activity.onDestroy

    3、Fragment事务添加Fragment

     Fragment newFragment = new MainFragment();
     FragmentTransaction mTransaction =  getFragmentManager().beginTransaction();
    //用新的 fragment 替换原来fragment 所在位置的布局,并且把此事务添加到返回栈中。
     mTransaction.replace(R.id.frame_layout,newFragment);
     mTransaction.addToBackStack(null);
     mTransaction.commit();
    

    相关文章

      网友评论

          本文标题:Android开发一《Activity的生命周期和启动模式》

          本文链接:https://www.haomeiwen.com/subject/nkozlrtx.html