zoukankan      html  css  js  c++  java
  • Activity的生命周期和启动模式

    1、Activity的生命周期

      生命周期我我在基础里面写过一部分,也写过一段demo,不过那个并不全面。

      http://www.cnblogs.com/xxbbtt/p/7515024.html

      Activity的生命周期分为两种,一种是有用户参与的情况下Activity所经过的生命周期的变化,另一种是指Activity被系统回收或者Configuration发生改变从而导致Activity被销毁重建。

      

      还是一样的使用这张图,Activity是作为与用户交互的界面使用的,它就相当于一个网页,网页里面有文字、图片、视频。Activity同样也有,而一个应用程序可以类比为浏览器,在android中第一次启动一个“浏览器”会默认打开一个“网页”,“网页”打开会加载其中的内容,而想要实现这个加载功能就需要实现onCreate()方法,所以:

    1. onCreate()方法:表示Activity正在被创建,这是生命周期的第一个方法,在这个方法中做一些初始化的工作。
    2. onStart()方法:表示Activity正在被启动,即将开始,这时Activity已经可见了。但是还没有出现在前台,还无法和用户交互。
    3. onResume()方法:表示Activity已经可见,并且出现在前台并开始活动。
    4. onPause()方法:表示Activity正在停止,一般紧接着onStop()方法将会被调用,除非用户很快的又切回这个Activity,不过一般不存在,需要记住的就是不要在这个方法做太耗时的工作,不然会影响到新Activity的启动,因为让新活动重新启动的方法会在onPause()方法之后执行。
    5. onStop()方法:表示Activity即将停止,可以做一些稍微重量级的回收工作,但也不能太耗时。
    6. onRestart()方法:表示Activity正在被重新启动,一般是这个Activity被从不可见状态变为可见状态,一般是用户返回了桌面或者启动了新的活动,然后再返回这个Activity的时候会启动这个方法。
    7. onDestroy()方法:表示Activity即将被销毁,这是生命周期最终后一个方法。

      实际上有下面几种情况:

    1. 一个Activity第一次被启动:onCreate->onStart->onResume
    2. 当一个新的Activity被启动或者切换到桌面的时候:onPause->onStop,这里要注意的时如果还能够看到这个Activity(比如新的Activity采用了透明主题),那么onStop并不会被调用。
    3. 当在此回到旧的Activity时:onRestart->onStart->onResume
    4. 当用户使用back键回退的时候:onPause->onStop->onDestroy
    5. 当Activity被系统回收后再次打开时:onCreate->onStart->onResume
    6. Activity的生命周期中只有一次调用onCreate和onDestroy。从Activity可不可见来看onStart和onStop是配对的,从Activity在不在前台onPause和onResume是配对的。

      这里要针对onPause需要重点说一说:暂停当前Activity大概有四种情况:锁屏、切回桌面、切换到其他Activity、弹出一个对话框。这些情况都会导致当前的Activity从前台切换至后台,onPause会被调用。当切换到其他Activity的时候,这个其他Activity如果是一个新的Activity那么就会调用它的onCreate方法,如果是重新启动的就会调用它的onRestart方法,然后这个onPause方法会先执行,再执行Activity的onCreate或nRestart方法,这就是为什么不能在onPause做太耗时的操作的原因,太耗时会导致新的Activity启动卡顿。

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

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

      这里由一个简单的demo,有一个TextView、一个Button、一个EditText,当按下按钮时TextView会显示从EditText获取的数据。  

    public class MainActivity extends AppCompatActivity {
    
        private TextView textView;
        private EditText editText;
        public Button button1;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = (TextView) findViewById(R.id.main_text);
            editText = (EditText) findViewById(R.id.main_edit);
            button1 = (Button) findViewById(R.id.main_button1);
            button1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    textView.setText(editText.getText());
                }
            });
        }
    }

      比如当前Activity处于竖屏状态,TextView正在显示输入框中的内容,

      

      当我们突然旋转屏幕,TextView的内容将会消失。

      

      这是因为在默认情况下,如果我们的Activity不做特殊处理,那么当系统配置发生改变时Activity将会被销毁并重新创建。

        //由不可见转换为可见时调用
        @Override
        protected void onStart() {
            super.onStart();
            Log.d("MainActivity","onStart()");
        }
    
        //由可见转换为不可见时调用
        @Override
        protected void onStop() {
            super.onStop();
            Log.d("MainActivity","onStop()");
        }
    
        //离开前台,但不是完全不可见
        @Override
        protected void onPause() {
            super.onPause();
            Log.d("MainActivity","onPause()");
        }
    
        //返回前台,准备好和用户交易
        @Override
        protected void onResume() {
            super.onResume();
            Log.d("MainActivity","onResume()");
        }
    
        //重启处于停止状态的活动时对用
        @Override
        protected void onRestart() {
            super.onRestart();
            Log.d("MainActivity","onRestart()");
        }
    
        // 在完整生存期结束时调用
        @Override
        protected void onDestroy() {
            // 清空所有的资源,包括结束线程、关闭数据库连接等。
            super.onDestroy();
            Log.d("MainActivity","onDestroy");
        }
    View Code

      在这个demo中加入以上代码,这样我们就知道那些生命周期的方法被调用了

      当屏幕旋转时的输出:

    12-01 10:19:07.098 30088-30088/com.example.administrator.test D/MainActivity: onPause()
    12-01 10:19:07.101 30088-30088/com.example.administrator.test D/MainActivity: onStop()
    12-01 10:19:07.102 30088-30088/com.example.administrator.test D/MainActivity: onDestroy
    12-01 10:19:07.154 30088-30088/com.example.administrator.test D/MainActivity: onCreate
    12-01 10:19:07.155 30088-30088/com.example.administrator.test D/MainActivity: onStart()
    12-01 10:19:07.163 30088-30088/com.example.administrator.test D/MainActivity: onResume()

      可以看到Activity被销毁,其中onPause、onStop、onDestroy依次被调用,这里是销毁竖屏的Activity

      还可以看到Activity被创建,其中onCreate、onStart、onResume依次被调用,这里是创建横屏的Activity。

      这里由于Activity是在异常情况下终止的,所以系统会调用onSaveInstanceState方法来保存当前的Activity的状态。

        @Override
        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
        }

      这是我Ctrl+o直接插入的这个onSaveInstanceState方法,这个方法使用的是Bundle对象进行之前的状态保存的。

        protected void onCreate(Bundle savedInstanceState) {

      这个onCreate也是编译器一开始就编写好的,可以看到这里接收了一个Bundle对象,所以其实再创建Activity的时候是可以在onCreate进行重建的。

        @Override
        protected void onRestoreInstanceState(Bundle savedInstanceState) {
            super.onRestoreInstanceState(savedInstanceState);
        }

      不过当Activity重建的时候,系统也会调用onRestoreInstanceState这个方法,onSaveInstanceState所保存的Bundle对象会同时传递给onCreate和onRestoreInstanceState方法。

    12-01 10:32:54.836 31306-31306/com.example.administrator.test D/MainActivity: onPause()
    12-01 10:32:54.838 31306-31306/com.example.administrator.test D/MainActivity: onSaveInstanceState()
    12-01 10:32:54.840 31306-31306/com.example.administrator.test D/MainActivity: onStop()
    12-01 10:32:54.840 31306-31306/com.example.administrator.test D/MainActivity: onDestroy
    12-01 10:32:54.881 31306-31306/com.example.administrator.test D/MainActivity: onCreate
    12-01 10:32:54.883 31306-31306/com.example.administrator.test D/MainActivity: onStart()
    12-01 10:32:54.884 31306-31306/com.example.administrator.test D/MainActivity: onRestoreInstanceState()
    12-01 10:32:54.889 31306-31306/com.example.administrator.test D/MainActivity: onResume()

      这是销毁、重建过程中onSaveInstanceState、onRestoreInstanceState被调用的时间。onSaveInstanceState实际上肯定会在onStop之前,当时和onPause的先后就不一定。onRestoreInstanceState的调用时机实在onStart之后。这里要注意的是,onSaveInstanceState和onRestoreInstanceState都是在Activity被异常终止是才会调用的,正常情况下是不会调用的。

      在onSaveInstanceState、onRestoreInstanceState中系统会默认的为我们做一些默认的保存工作,这些默认的保存操作是针对某一个特定的View的,每一个View都有onSaveInstanceState、onRestoreInstanceState方法。

        @Override
        protected void onRestoreInstanceState(Bundle savedInstanceState) {
            super.onRestoreInstanceState(savedInstanceState);
            Log.d("MainActivity","onRestoreInstanceState()");
            textView.setText(savedInstanceState.getString("textView.getText()"));
        }
    
        @Override
        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            Log.d("MainActivity","onSaveInstanceState()");
            outState.putString("textView.getText()", textView.getText().toString());
        }

      这样就能保存之前TextView的状态并恢复。  

    • 情况2:资源内存不足导致低优先级的Activity被杀死

      Activity按照优先级从高到低,可以分为如下三种:

      (1) 前台Activity——正在和用户交互的Activity,优先级最高

      (2)可见但非前台Activity——比如Activity中弹出一个对话框,导致Activity可见但是位于后台无法和用户直接交互

      (3) 后台Activity——已经被暂停的Activity,优先级最低。

      当系统内存不足时,系统会按照上面的优先级去杀死目标Activity所在的进程,并使用onSaveInstanceState、onRestoreInstanceState和方法。

      为了防止当系统配置改变而发生的Activity重新创建,我们可以修改AndroidManifest中的onfigChanges属性。比如添加这样一行:

    android:configChanges="orientation"

    3、Activity的启动模式

      standard:标准模式,每一次启动一个Activity都会创建一个新的实例,假设现在在Activity A,启动一个Activity A那么就会重新调用onCreate,这时栈中就有两个Activity A 。

      singleTop:栈顶复用模式,如果新Activity已经位于任务栈的栈顶那么,该Activity不会被重复创建,同时onNewInrent方法会被调用,,通过此方法的参数我们可以取出当前请求的信息。

      singleTaks:栈类复用模式,只要栈中有这个Activity,系统就会吧这个Activity调到栈顶并调用它的onNewIntent方法,如果不存在,就重新创建一个任务栈,并创建实例并放入其中。这里举几个例子:

    • 比如目前任务栈S1中的情况为ABC,这个时候ActivityD以singleTask模式请求启动,其所需要的任务栈为S2,由于S2和D的实例均不存在,所以系统就会创建任务栈S2,再创建D的实例并将其入栈到S2
    • 假设D所需的任务栈为S1,那么会直接创建D并入栈到S1
    • 如果D所需的任务栈为S1,且当前任务栈S1的情况为ADBC,根据栈内复用的原则,系统会把D切换到栈顶并调用它的onNewIntent方法,但是同时由于singleTaks默认具有clearTop的效果,会导致所有在D上面的Activity全部出栈,S1就变成AD。

      singleInstance:单实例模式,这是一种加强的singleTaks模式,加强的就是,具有这种模式的Activity只能单独地位于一个任务栈中,比如Activity A是singleInstance模式的,,当A 启动的时候,系统会为其建立一个新的任务栈,任何无论在启动多少次A都不会创建新的Activity,除非这个独特的任务栈被系统销毁 了

       那什么是任务栈呢,首先要说一个参数:TaskAffinity,意为任务相关性,这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity属性的任务栈的名字为应用的包名,我们也可以为每一个Activity单独指定TaskAffinity属性,这个属性名不能和包名相同,否则就相当于没有指定。TaskAffinity这个属性通常与singleTask启动模式或者allowTaskReparenting属性配对使用。

       当singleTask和allowTaskReparenting配对使用的时候,会产生特殊的效果。当一个应用A启动了应用B的Activity C的时候,如果allowTaskReparenting为true的话,那么当B被启动的的时候,此Activity C会直接从应用A的任务栈转移到应用B的任务栈中,因为刚开始A启动的B的这个Activity C是运行在A的任务栈中的,这时启动B会创建B的任务栈,但是C是被A启动的也运行在A的任务栈中,所以就会吧C转移到B的任务栈中。

      给Activity指定启动模式有两种方法:第一种是在AndroidManifest中修改

      <activity android:name=".MainActivity"
    android:launchMode="standard" />

      第二种是通过在Intent中设置标志位来为Activity指定启动模式

    Intent intent = new Intent();
    intent.setClass(MainActivity.this, SecondActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);  

      两者的区别:优先级上第二种高于第一种,两种同时存在时,以第二种为准

            第一种无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,第二种无法为Activity指定singleInstance模式

      Activity的Flags  

    • FLAG_ACTIVITY_NEW_TASK

        这个标记位的作用是为Activity指定singleTask启动模式,其效果和在清单文件中指定相同。

    • FLAG_ACTIVITY_SINGLE_TOP

        这个标记位的作用是为Activity指定singleTop启动模式,其效果和在清单文件中指定相同。

    • FLAG_ACTIVITY_CLEAR_TOP

        具有此标记位的Activity,当它启动时,在同一个任务栈中,所有位于它上面的Activity都要出栈。

    • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

        具有这个标记的Activity不会出现在历史Activity列表中(这里点击手机除back和home键的另外一个键就会出现recent列表了),当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。它等同于在清单文件xml中指定Activity的属性android:excludeFromRecents = "true";

    4、IntentFilter的匹配规则

      IntentFilter中过滤的信息有action、category、data.为同时匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。一个过滤列表中的action、category、data可以有多个,所有的action、category、data分别构成不同类别

    • action的匹配规则

      action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action,一个过滤规则可以有多个action,只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功,需要注意的是,Intent中如果没有指定action那么匹配失败,另外action区分大小写。

    • category的匹配规则

      category同样是一个字符串,系统预定义了一些category,同时我们也可以在应用中定义自己的category,不同的是,它要求Intent中如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同。

    • data的匹配规则

      data的匹配规则和和action类似。

      data的语法如下

    <data android:scheme="string"
          android:host="string"
          android:port="string/"
          android:path="string"
          android:pathPattern="string"
          android:pathPrefix="string"
          android:mimeType="string"/>

      data由两部分组成,mimeType和URI。mimeType指媒体类型,比如image/jpg 、audio/mpeg4-generic和video/*等,而URI包含的就多了,下面是URI的结构

    scheme://host:port/path or pathPrefix or pathPattern

      scheme:URI的模式,比如http、file、content,这个是必须指定的。

      host:URI的主机名,比如www.baidu.com,这个也是必须指定的。

      por:URI中的端口号。

      path、 pathPrefix、 pathPattern:这三个参数表述路径信息,其中path表示完整的路径, pathPattern也表示完整的路径信息,但是它里面可以包含通配符“*”,pathPrefix表示路径的前缀信息。

       

    参考书籍:Android 开发艺术探索

  • 相关阅读:
    P3413 SAC#1
    [BJOI2017]树的难题
    [HNOI/AHOI2018]转盘
    P2664 树上游戏
    [POI2013]BAJ-Bytecomputer
    [ZJOI2010]网络扩容
    数列游戏
    士兵占领
    [ZJOI2016]大森林
    P4755 Beautiful Pair
  • 原文地址:https://www.cnblogs.com/xxbbtt/p/7942559.html
Copyright © 2011-2022 走看看