• 基础总结篇之中的一个:Activity生命周期


    子曰:溫故而知新,能够為師矣。《論語》

    学习技术也一样,对于技术文档或者经典的技术书籍来说,指望看一遍就全然掌握,那基本不大可能,所以我们须要常常回过头再细致研读几遍,以领悟到作者的思想精髓。

    近来回想了一下关于Activity的生命周期,參看了相关书籍和官方文档,也有了不小的收获,对于曾经的认知有了非常大程度上的改善,在这里和大家分享一下。

    熟悉javaEE的朋友们都了解servlet技术,我们想要实现一个自己的servlet,须要继承对应的基类,重写它的方法,这些方法会在合适的时间被servlet容器调用。事实上android中的Activity执行机制跟servlet有些相似之处,Android系统相当于servlet容器,Activity相当于一个servlet,我们的Activity处在这个容器中,一切创建实例、初始化、销毁实例等过程都是容器来调用的,这也就是所谓的“Don't call me, I'll call you.”机制。

    我们来看一下这一张经典的生命周期流程图:


    相信不少朋友也已经看过这个流程图了,也基本了解了Activity生命周期的几个过程,我们就来说一说这几个过程。

    1.启动Activity:系统会先调用onCreate方法,然后调用onStart方法,最后调用onResume,Activity进入执行状态。

    2.当前Activity被其它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.用户退出当前Activity:系统先调用onPause方法,然后调用onStop方法,最后调用onDestory方法,结束当前Activity。

    可是知道这些还不够,我们必须亲自试验一下才干深刻体会,融会贯通。

    以下我们就结合实例,来演示一下生命周期的几个过程的具体情况。我们新建一个名为lifecycle的项目,创建一个名为LifeCycleActivity的Activity,例如以下:

    package com.scott.lifecycle;
    
    import android.app.Activity;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    
    public class LifeCycleActivity extends Activity {
    	
    	private static final String TAG = "LifeCycleActivity";
    	private Context context = this;
    	private int param = 1;
    	
    	//Activity创建时被调用
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.i(TAG, "onCreate called.");
            
            setContentView(R.layout.lifecycle);
            
            Button btn = (Button) findViewById(R.id.btn);
            btn.setOnClickListener(new View.OnClickListener() {
    			@Override
    			public void onClick(View v) {
    				Intent intent = new Intent(context, TargetActivity.class);
    				startActivity(intent);
    			}
    		});
        }
        
        //Activity创建或者从后台又一次回到前台时被调用
        @Override
        protected void onStart() {
        	super.onStart();
        	Log.i(TAG, "onStart called.");
        }
        
        //Activity从后台又一次回到前台时被调用
        @Override
        protected void onRestart() {
        	super.onRestart();
        	Log.i(TAG, "onRestart called.");
        }
        
        //Activity创建或者从被覆盖、后台又一次回到前台时被调用
        @Override
        protected void onResume() {
        	super.onResume();
        	Log.i(TAG, "onResume called.");
        }
        
        //Activity窗体获得或失去焦点时被调用,在onResume之后或onPause之后
        /*@Override
        public void onWindowFocusChanged(boolean hasFocus) {
        	super.onWindowFocusChanged(hasFocus);
        	Log.i(TAG, "onWindowFocusChanged called.");
        }*/
        
        //Activity被覆盖到以下或者锁屏时被调用
        @Override
        protected void onPause() {
        	super.onPause();
        	Log.i(TAG, "onPause called.");
        	//有可能在运行完onPause或onStop后,系统资源紧张将Activity杀死,所以有必要在此保存持久数据
        }
        
        //退出当前Activity或者跳转到新Activity时被调用
        @Override
        protected void onStop() {
        	super.onStop();
        	Log.i(TAG, "onStop called.");	
        }
        
        //退出当前Activity时被调用,调用之后Activity就结束了
        @Override
        protected void onDestroy() {
        	super.onDestroy();
        	Log.i(TAG, "onDestory called.");
        }
        
        /**
         * Activity被系统杀死时被调用.
         * 比如:屏幕方向改变时,Activity被销毁再重建;当前Activity处于后台,系统资源紧张将其杀死.
         * 另外,当跳转到其它Activity或者按Home键回到主屏时该方法也会被调用,系统是为了保存当前View组件的状态.
         * 在onPause之前被调用.
         */
    	@Override
    	protected void onSaveInstanceState(Bundle outState) {
    		outState.putInt("param", param);
    		Log.i(TAG, "onSaveInstanceState called. put param: " + param);
    		super.onSaveInstanceState(outState);
    	}
    	
    	/**
    	 * Activity被系统杀死后再重建时被调用.
    	 * 比如:屏幕方向改变时,Activity被销毁再重建;当前Activity处于后台,系统资源紧张将其杀死,用户又启动该Activity.
    	 * 这两种情况下onRestoreInstanceState都会被调用,在onStart之后.
    	 */
    	@Override
    	protected void onRestoreInstanceState(Bundle savedInstanceState) {
    		param = savedInstanceState.getInt("param");
    		Log.i(TAG, "onRestoreInstanceState called. get param: " + param);
    		super.onRestoreInstanceState(savedInstanceState);
    	}
    }
    大家注意到,除了几个常见的方法外,我们还加入了onWindowFocusChanged、onSaveInstanceState、onRestoreInstanceState方法:

    1.onWindowFocusChanged方法:在Activity窗体获得或失去焦点时被调用,比如创建时首次呈如今用户面前;当前Activity被其它Activity覆盖;当前Activity转到其它Activity或按Home键回到主屏,自身退居后台;用户退出当前Activity。以上几种情况都会调用onWindowFocusChanged,而且当Activity被创建时是在onResume之后被调用,当Activity被覆盖或者退居后台或者当前Activity退出时,它是在onPause之后被调用,如图所看到的:


    这种方法在某种场合下还是非常实用的,比如程序启动时想要获取视特定视图组件的尺寸大小,在onCreate中可能无法取到,由于窗体Window对象还没创建完毕,这个时候我们就须要在onWindowFocusChanged里获取;假设大家已经看过我写的Android动画之Frame Animation这篇文章就会知道,当时试图在onCreate里载入frame动画失败的原因就是由于窗体Window对象没有初始化完毕,所以最后我将载入动画的代码放到了onWindowFocusChanged中,问题迎刃而解。只是大家或许会有疑惑,为什么我在代码里将它凝视掉了,由于对当前Activity每个操作都有它的运行log,我操心这会影响到整个流程的清晰度,所以将它注掉,大家仅仅要了解它应用的场合和运行的顺序就能够了。

    2.onSaveInstanceState:(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,此方法会被调用;(2)在用户改变屏幕方向时,此方法会被调用;(3)在当前Activity跳转到其它Activity或者按Home键回到主屏,自身退居后台时,此方法会被调用。第一种情况我们无法保证什么时候发生,系统依据资源紧张程度去调度;另外一种是屏幕翻转方向时,系统先销毁当前的Activity,然后再重建一个新的,调用此方法时,我们能够保存一些暂时数据;第三种情况系统调用此方法是为了保存当前窗体各个View组件的状态。onSaveInstanceState的调用顺序是在onPause之前。

    3.onRestoreInstanceState:(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,然后用户又回到了此Activity,此方法会被调用;(2)在用户改变屏幕方向时,重建的过程中,此方法会被调用。我们能够重写此方法,以便能够恢复一些暂时数据。onRestoreInstanceState的调用顺序是在onStart之后。

    以上着重介绍了三个相对陌生方法之后,以下我们就来操作一下这个Activity,看看它的生命周期究竟是个什么样的过程:

    1.启动Activity:


    在系统调用了onCreate和onStart之后,调用了onResume,自此,Activity进入了执行状态。

    2.跳转到其它Activity,或按下Home键回到主屏:


    我们看到,此时onSaveInstanceState方法在onPause之前被调用了,而且注意,退居后台时,onPause后onStop相继被调用。

    3.从后台回到前台:


    当从后台会到前台时,系统先调用onRestart方法,然后调用onStart方法,最后调用onResume方法,Activity又进入了执行状态。

    4.改动TargetActivity在AndroidManifest.xml中的配置,将android:theme属性设置为@android:style/Theme.Dialog,然后再点击LifeCycleActivity中的button,跳转行为就变为了TargetActivity覆盖到LifeCycleActivity之上了,此时调用的方法为:


    注意另一种情况就是,我们点击button,仅仅是按下锁屏键,运行的效果也是如上。

    我们注意到,此时LifeCycleActivity的OnPause方法被调用,并没有调用onStop方法,由于此时的LifeCycleActivity没有退居后台,仅仅是被覆盖或被锁屏;onSaveInstanceState会在onPause之前被调用。

    5.按回退键使LifeCycleActivity从被覆盖回到前面,或者按解锁键解锁屏幕:


    此时仅仅有onResume方法被调用,直接再次进入执行状态。

    6.退出:


    最后onDestory方法被调用,标志着LifeCycleActivity的终结。

    大家似乎注意到,在全部的过程中,并没有onRestoreInstanceState的出现,这个并不奇怪,由于之前我们就说过,onRestoreInstanceState仅仅有在杀死不在前台的Activity之后用户回到此Activity,或者用户改变屏幕方向的这两个重建过程中被调用。我们要演示第一种情况比較困难,我们能够结合另外一种情况演示一下详细过程。顺便也向大家解说一下屏幕方向改变的应对策略。

    首先介绍一下关于Activity屏幕方向的相关知识。

    我们能够为一个Activity指定一个特定的方向,指定之后即使转动屏幕方向,显示方向也不会跟着改变:

    1.指定为竖屏:在AndroidManifest.xml中对指定的Activity设置android:screenOrientation="portrait",或者在onCreate方法中指定:

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);	//竖屏
    2.指定为横屏:在AndroidManifest.xml中对指定的Activity设置android:screenOrientation="landscape",或者在onCreate方法中指定:

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);	//横屏
    为应用中的Activity设置特定的方向是经经常使用到的办法,能够为我们省去不少不必要的麻烦。只是,我们今天讲的是屏幕方向改变时的生命周期,所以我们并不採用固定屏幕方向这样的办法。

    以下我们就结合实例解说一下屏幕转换的生命周期,我们新建一个Activity命名为OrientationActivity,例如以下:

    package com.scott.lifecycle;
    
    import android.app.Activity;
    import android.content.res.Configuration;
    import android.os.Bundle;
    import android.util.Log;
    
    public class OrientationActivity extends Activity {
    	
    	private static final String TAG = "OrientationActivity";
    	private int param = 1;
    	
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.orientation_portrait);
    		Log.i(TAG, "onCreate called.");
    	}
    	
    	@Override
    	protected void onStart() {
    		super.onStart();
    		Log.i(TAG, "onStart called.");
    	}
    	
    	@Override
    	protected void onRestart() {
    		super.onRestart();
    		Log.i(TAG, "onRestart called.");
    	}
    	
    	@Override
    	protected void onResume() {
    		super.onResume();
    		Log.i(TAG, "onResume called.");
    	}
    	
    	@Override
    	protected void onPause() {
    		super.onPause();
    		Log.i(TAG, "onPause called.");
    	}
    	
    	@Override
    	protected void onStop() {
    		super.onStop();
    		Log.i(TAG, "onStop called.");
    	}
    	
    	@Override
    	protected void onDestroy() {
    		super.onDestroy();
    		Log.i(TAG, "onDestory called.");
    	}
    
    	@Override
    	protected void onSaveInstanceState(Bundle outState) {
    		outState.putInt("param", param);
    		Log.i(TAG, "onSaveInstanceState called. put param: " + param);
    		super.onSaveInstanceState(outState);
    	}
    	
    	@Override
    	protected void onRestoreInstanceState(Bundle savedInstanceState) {
    		param = savedInstanceState.getInt("param");
    		Log.i(TAG, "onRestoreInstanceState called. get param: " + param);
    		super.onRestoreInstanceState(savedInstanceState);
    	}
    	
    	//当指定了android:configChanges="orientation"后,方向改变时onConfigurationChanged被调用
    	@Override
    	public void onConfigurationChanged(Configuration newConfig) {
    		super.onConfigurationChanged(newConfig);
    		Log.i(TAG, "onConfigurationChanged called.");
    		switch (newConfig.orientation) {
    		case Configuration.ORIENTATION_PORTRAIT:
    			setContentView(R.layout.orientation_portrait);
    			break;
    		case Configuration.ORIENTATION_LANDSCAPE:
    			setContentView(R.layout.orientation_landscape);
    			break;
    		}
    	}
    }
    首先我们须要进入“Settings->Display”中,将“Auto-rotate Screen”一项选中,表明能够自己主动依据方向旋转屏幕,然后我们就能够測试流程了,当我们旋转屏幕时,我们发现系统会先将当前Activity销毁,然后重建一个新的:


    系统先是调用onSaveInstanceState方法,我们保存了一个暂时參数到Bundle对象里面,然后当Activity重建之后我们又成功的取出了这个參数。

    为了避免这样销毁重建的过程,我们须要在AndroidMainfest.xml中对OrientationActivity相应的<activity>配置android:configChanges="orientation",然后我们再測试一下,我试着做了四次的旋转,打印例如以下:


    能够看到,每次旋转方向时,仅仅有onConfigurationChanged方法被调用,没有了销毁重建的过程。

    下面是须要注意的几点:

    1.假设<activity>配置了android:screenOrientation属性,则会使android:configChanges="orientation"失效。

    2.模拟器与真机区别非常大:模拟器中假设不配置android:configChanges属性或配置值为orientation,切到横屏运行一次销毁->重建,切到竖屏运行两次。真机均为一次。模拟器中假设配置android:configChanges="orientation|keyboardHidden"(假设是Android4.0,则是"orientation|keyboardHidden|screenSize"),切竖屏运行一次onConfigurationChanged,切横屏运行两次。真机均为一次。

    Activity的生命周期与程序的健壮性有着密不可分的关系,希望朋友们可以认真体会、熟练应用。

  • 相关阅读:
    dotnet core 获取 MacAddress 地址方法
    dotnet core 获取 MacAddress 地址方法
    dotnet core 发布只带必要的依赖文件
    dotnet core 发布只带必要的依赖文件
    Developing Universal Windows Apps 开发UWA应用 问答
    Developing Universal Windows Apps 开发UWA应用 问答
    cmd 如何跨驱动器移动文件夹
    cmd 如何跨驱动器移动文件夹
    C++ 驱动开发 error LNK2019
    C++ 驱动开发 error LNK2019
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4052844.html
Copyright © 2020-2023  润新知