• 安卓开发笔记——重识Activity


      Activity并不是什么新鲜的东西,老生常谈,这里只是随笔记录一些笔记。

      每当说起Activity,感觉最关注的还是它的生命周期,因为要使我们的应用程序更加健壮,客户体验更加良好,如果对生命周期不熟悉,那是不可能完成的任务。好了,言归正传,开始笔记,尽可能用最精简的言语来阐述最实用的东西。

      准备写几篇文章,这是第一篇只谈生命周期的普通用法,不涉及到复杂点的知识,比如任何栈回退栈等操作。

    1、一张来自谷歌官方文档的Activity的生命周期图:

      直接来个MainActivity,覆写以上所有方法并在方法里打印出Log日志,给定一个按钮,点击可以跳转到第二个Activity:

     1 package com.lcw.rabbit.activitydemo;
     2 
     3 import android.app.Activity;
     4 import android.content.Intent;
     5 import android.os.Bundle;
     6 import android.util.Log;
     7 import android.view.View;
     8 import android.widget.Button;
     9 
    10 public class MainActivity extends Activity {
    11 
    12     private static final String TAG = "Rabbit";
    13 
    14     private Button mbButton;
    15 
    16     @Override
    17     protected void onCreate(Bundle savedInstanceState) {
    18         super.onCreate(savedInstanceState);
    19         setContentView(R.layout.activity_main);
    20         mbButton = (Button) findViewById(R.id.bt_button);
    21         mbButton.setOnClickListener(new View.OnClickListener() {
    22             @Override
    23             public void onClick(View v) {
    24                 startActivity(new Intent(MainActivity.this, SecondActivity.class));
    25             }
    26         });
    27 
    28         Log.i(TAG, "1----------onCreate");
    29     }
    30 
    31     @Override
    32     protected void onStart() {
    33         super.onStart();
    34         Log.i(TAG, "1----------onStart");
    35     }
    36 
    37     @Override
    38     protected void onResume() {
    39         super.onResume();
    40         Log.i(TAG, "1----------onResume");
    41     }
    42 
    43     @Override
    44     protected void onPause() {
    45         super.onPause();
    46         Log.i(TAG, "1----------onPause");
    47     }
    48 
    49     @Override
    50     protected void onStop() {
    51         super.onStop();
    52         Log.i(TAG, "1----------onStop");
    53     }
    54 
    55     @Override
    56     protected void onRestart() {
    57         super.onRestart();
    58         Log.i(TAG, "1----------onRestart");
    59     }
    60 
    61     @Override
    62     protected void onDestroy() {
    63         super.onDestroy();
    64         Log.i(TAG, "1----------onDestroy");
    65     }
    66 }
    MainActivity.java

      再来个SecondActivity,一样覆写以上所有方法:

     1 package com.lcw.rabbit.activitydemo;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 import android.util.Log;
     6 
     7 public class SecondActivity extends Activity {
     8 
     9     private static final String TAG = "Rabbit";
    10 
    11     @Override
    12     protected void onCreate(Bundle savedInstanceState) {
    13         super.onCreate(savedInstanceState);
    14         setContentView(R.layout.activity_second);
    15         Log.i(TAG, "2----------onCreate");
    16     }
    17 
    18     @Override
    19     protected void onStart() {
    20         super.onStart();
    21         Log.i(TAG, "2----------onStart");
    22     }
    23 
    24     @Override
    25     protected void onResume() {
    26         super.onResume();
    27         Log.i(TAG, "2----------onResume");
    28     }
    29 
    30     @Override
    31     protected void onPause() {
    32         super.onPause();
    33         Log.i(TAG, "2----------onPause");
    34     }
    35 
    36     @Override
    37     protected void onStop() {
    38         super.onStop();
    39         Log.i(TAG, "2----------onStop");
    40     }
    41 
    42     @Override
    43     protected void onRestart() {
    44         super.onRestart();
    45         Log.i(TAG, "2----------onRestart");
    46     }
    47 
    48     @Override
    49     protected void onDestroy() {
    50         super.onDestroy();
    51         Log.i(TAG, "2----------onDestroy");
    52     }
    53 
    54 
    55 }
    SecondActivity.java

       

    2、Activity的流程之旅:(参照上面的流程图来理解)

    实验一:

      当我们进入第一个Activity的时候,我们的Log日志依次打印出了:onCreate,onStart,onResume方法。

    结论:

      当我们第一次启动Activity的时候会调用onCreate方法,然后在界面展示出来的时候调用了onStart方法,当用户获取焦点的时候调用onResume方法。

    实验二: 

       当我们按下Back键的时候,我们的Log日志依次打印出了:onPause,onStop,onDestroy方法。

    结论:

      当Activity处于暂停(未被完全遮挡,比如弹出对话框状态)的时候会调用onPause,当完全不可见(被隐藏)的时候会调用onStop方法,当被销毁的时候会调用onDestroy方法。

    实验三:

      当我们重新进入Activity,它依旧是调用onCreate,onStart,onResume方法,当我们点击按钮跳转第二个Activity的时候,我们的Log日志依次打印出了:onPause,然后第二个Activity打印出依次onCreate,onStart,onResume,然后第一个Activity再调用onStop方法。

    结论:

      当Activity处于暂停(未被完全遮挡)的时候会调用onPause,然后启动第二个Activity,执行第二个Activity被创建的生命周期onCreate,onStart,onPause方法,当第一个Activity完全不可见(被隐藏)的时候会调用onStop方法。

    实验四:

      当我们按下Back键返回到第一个Activity的时候,Log日志依次打印出:第二个Activity的onPause,然后进入第一个Activity的onReStart,onStrat,onResume,然后第二个Activity调用了onStop,onDestroy。

    结论:

      当我们按下Back键返回到第一个Activity的时候,第二个Activity会先调用onPause暂停,由于第一个Activity刚才没被调用onDestroy销毁,所以这里没有重新调用onCreate方法创建而是调用了onReStart方法,然后展示界面onStart,获取屏幕焦点onResume,然后第二个Activity被完全覆盖执行onStop,然后被销毁onDestroy。

    3、Activity设计模式之美

    疑问1:  

      为什么不能直接去启动第二个Activiity?

    答:

      其实大家可以这样来理解,当我启动一个正在执行音乐播放的Activity的时候,突然有一个电话打进来了,电话也是一个Activity,那么在没有对第一个播放音乐的Activity进行暂停操作,就接通了电话,那么是不是我们在边通话的时候还会边放着音乐啊?很显然这是不符合常理的。

    疑问2:

      当我们启动别的Activity的时候,为什么不先把一个Activity完全销毁了,然后再去启动另一个?

    答:

      还是刚才打电话的例子,如果我们直接完全销毁了前面一个Activity,那么我们在接电话的时候当然就是舒服,因为没有音乐吵你了,但是当我们接完电话呢?我们是不是又要去重新打开那个音乐的Activity,然后再重投开始听?然后刚好又来一个电话呢?我想这时的你应该要摔电话了。

      再来,如果我们直接去销毁了这个Activity,那刚好这个电话Activity由于不知名原因发生问题呢?那么此时电话的Activity没启动起来,音乐的Activity又销毁了,那么用户的手机屏幕就会出现黑屏(闪屏),这点在用户体验上是非常不好的。

      可能敏捷点的朋友已经想到这个暂停方法onPause的好处了,当电话来的时候,我们去暂停音乐Activity的同时调用了onPause方法,我们就可以在这个方法里面去记录一些东西了,比如当前音乐的播放进度,当我们接完电话,回到音乐Activity的时候,我们会调用(实验四)的方法,我们可以在onRestart或者onResume中根据刚记录下来的播放进度去继续播放音乐。

      所以Activity的生命周期这样去设计是非常合理的。

    4、Activity交互实例

      光说不练假把式,接下来上个示例,就以播放音乐为例子,注释很全,这里就不再多说了,要注意的是,我们进行了多媒体操作,我们需要在onDestroy的时候释放资源对象,否则会占着内存,程序会越来越卡。

     1 package com.lcw.rabbit.activitydemo;
     2 
     3 import android.app.Activity;
     4 import android.content.Intent;
     5 import android.media.MediaPlayer;
     6 import android.os.Bundle;
     7 import android.util.Log;
     8 import android.view.View;
     9 import android.widget.Button;
    10 
    11 public class MainActivity extends Activity {
    12 
    13     private static final String TAG = "Rabbit";
    14 
    15     private Button mbButton;
    16     private MediaPlayer mMediaPlayer;
    17     private int mCurrentPosition;
    18 
    19     @Override
    20     protected void onCreate(Bundle savedInstanceState) {
    21         super.onCreate(savedInstanceState);
    22         setContentView(R.layout.activity_main);
    23         //点击按钮跳转第二个Activity
    24         mbButton = (Button) findViewById(R.id.bt_button);
    25         mbButton.setOnClickListener(new View.OnClickListener() {
    26             @Override
    27             public void onClick(View v) {
    28                 startActivity(new Intent(MainActivity.this, SecondActivity.class));
    29             }
    30         });
    31 
    32         //播放音乐
    33         mMediaPlayer=MediaPlayer.create(this,R.raw.music);
    34         mMediaPlayer.start();
    35 
    36         Log.i(TAG, "1----------onCreate");
    37     }
    38 
    39     @Override
    40     protected void onStart() {
    41         super.onStart();
    42         Log.i(TAG, "1----------onStart");
    43     }
    44 
    45     @Override
    46     protected void onResume() {
    47         super.onResume();
    48         if(mCurrentPosition!=0){
    49             //如果当前有记录进度,继续播放
    50             mMediaPlayer.seekTo(mCurrentPosition);
    51             mMediaPlayer.start();
    52         }
    53         Log.i(TAG, "1----------onResume");
    54     }
    55 
    56     @Override
    57     protected void onPause() {
    58         super.onPause();
    59         //记录当前进度
    60         mCurrentPosition=mMediaPlayer.getCurrentPosition();
    61         mMediaPlayer.pause();
    62         Log.i(TAG, "1----------onPause");
    63     }
    64 
    65     @Override
    66     protected void onStop() {
    67         super.onStop();
    68         Log.i(TAG, "1----------onStop");
    69     }
    70 
    71     @Override
    72     protected void onRestart() {
    73         super.onRestart();
    74         Log.i(TAG, "1----------onRestart");
    75     }
    76 
    77     @Override
    78     protected void onDestroy() {
    79         super.onDestroy();
    80         //释放资源
    81         mMediaPlayer.release();
    82         mMediaPlayer=null;
    83         Log.i(TAG, "1----------onDestroy");
    84     }
    85 }
    MainActivity.java

    5、关于Activity数据状态的保存

      由于手机是便捷式移动设备,掌握在用户的手中,它的展示方向我们是无法预知的,具有不确定性。平时我们拿着手机多数为竖屏,但有时候我们感觉累了也会躺着去使用手机,那么这时手机屏幕的展示方向可能已经被用户切换成横屏,由于竖屏和横屏的界面宽高比例不同,那么我们的布局界面就会发生改变,所以是件很麻烦的事情,我们需要去准备两套UI布局,当然很多时候我们为了节省设计成本,只准备一套UI布局(竖屏或者横屏),使程序固定在一个方向,让其不跟随着屏幕的旋转而旋转。在这里我们先不去管这些东西,我们来看看当屏幕旋转的时候,Activity的生命周期是怎么走的:

    实验五:

      启动一个Activity,对屏幕进行翻转,观看生命周期的变化

    结论:

      在我们翻转屏幕的时候,会销毁当前的Activity,然后重建Activity。

      对Activity进行重建的时候,我们的数据就会丢失,很多时候,当我们切换到别的Activity的时候,需要保存当前Activity的状态或者是临时数据,那么我们该怎么办呢?

      我们在Activity里再覆写这两个方法:

     1     @Override
     2     protected void onSaveInstanceState(Bundle outState) {
     3         super.onSaveInstanceState(outState);
     4         Log.i(TAG, "1----------onSaveInstanceState");
     5     }
     6 
     7     @Override
     8     protected void onRestoreInstanceState(Bundle savedInstanceState) {
     9         super.onRestoreInstanceState(savedInstanceState);
    10         Log.i(TAG, "1----------onRestoreInstanceState");
    11     }

      然后我们再来看下这两个方法是什么时候被调用的:

    1、当正常进入退出的时候,生命周期依旧正常,这两个方法没有被调用:

     2、当我们正常进入一个Activity点击按钮跳转到别的Activity的时候,我们会发现onSaveInstanceState在第二个Activity获取屏幕焦点(onResume)之后,在第一个Activity执行onPause之后,onStop之前调用了此方法,当从第二个Activity切换回来的时候就重复执行着实验四。

       细心的朋友可能已经发现,onSaveInstanceState方法里有个Bundle类型的回调参数,在onCreate里面也有个Bundle类型的参数,没错,答案就在这里,如果我们要对Activity的数据或者状态进行临时性的保存时,我们可以在onSaveInstaceState存入参数,类似这样的:

    1     @Override
    2     protected void onSaveInstanceState(Bundle outState) {
    3         super.onSaveInstanceState(outState);
    4         outState.putString("name","Rabbit");
    5         Log.i(TAG, "1----------onSaveInstanceState");
    6     }

      在onCreate里获取:

     1     @Override
     2     protected void onCreate(Bundle savedInstanceState) {
     3         super.onCreate(savedInstanceState);
     4         setContentView(R.layout.activity_main);
     5         //获取保存数据
     6         if (savedInstanceState!=null){
     7             Log.i(TAG, "I am "+savedInstanceState.get("name") );
     8         }
     9 
    10         //点击按钮跳转第二个Activity
    11         mbButton = (Button) findViewById(R.id.bt_button);
    12         mbButton.setOnClickListener(new View.OnClickListener() {
    13             @Override
    14             public void onClick(View v) {
    15                 startActivity(new Intent(MainActivity.this, SecondActivity.class));
    16             }
    17         });
    18 
    19 
    20         Log.i(TAG, "1----------onCreate");
    21     }

      在onRestoreInstanceState里取:

    1     @Override
    2     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    3         super.onRestoreInstanceState(savedInstanceState);
    4         //获取保存数据
    5         if (savedInstanceState!=null){
    6             Log.i(TAG, "I am "+savedInstanceState.get("name") );
    7         }
    8         Log.i(TAG, "1----------onRestoreInstanceState");
    9     }

      无图无真相,来看下实验结果图,我进入了Activity对屏幕翻转,触发Activity重建,可以看到数据已经被保存了。

      但这里有个疑问,当我们不翻转屏幕,也就是不触发Activity重建的时候,我们是没有执行onCreate,onRestoreInstanceState方法的,所以这个Bundle对象我们不一定是可以拿到的,那数据保存不就变得很不可靠了吗?

      没错,由于Activity重建的不确定,所以saveInstanceState保存的数据一般都是临时性的,真正持久化操作我们应该在onPause方法里操作。

    这里额外的要提到一点,onRestoreInstanceState方法在两种状态下会被调用:

    1、在Activity被覆盖或退居后台之后,系统资源不足将其杀死,然后用户又回到了此Activity,此方法会被调用;

    2、在用户改变屏幕方向时,重建的过程中,此方法会被调用。

    6、关于屏幕方向改变Activity会重建的应对策略:

    1、

      指定为竖屏:在AndroidManifest.xml中对指定的Activity设置:

    android:screenOrientation="portrait"

      或者在onCreate方法中指定:

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);  //竖屏  

       指定为横屏:在AndroidManifest.xml中对指定的Activity设置:

    android:screenOrientation="landscape"

      或者在onCreate方法中指定:

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //横屏  

    2、

      锁定屏幕虽然可以实现我们想要的效果,但并不是很好的一种做法,为了避免这样销毁重建的过程,我们可以在AndroidMainfest.xml中对对应的<activity>配置:

    android:configChanges="orientation"

       如果是Android4.0,则是:

    android:configChanges="orientation|keyboardHidden|screenSize"

      然后我们在Activity里重写onConfigurationChanged方法:

    1     @Override
    2     public void onConfigurationChanged(Configuration newConfig) {
    3         super.onConfigurationChanged(newConfig);
    4         Log.i(TAG, "1----------onConfigurationChanged");
    5     }

      这样Activity在翻转屏幕的时候就不会被销毁重建了,只是调用了onConfigurationChanged方法。

    总结:

    做工作中,你可能感兴趣的三个关键环① 完整生命周期② 可见生命周期③ 可交互生命周期 
    如图所示,图中的周期都是大的包括小的:

    在实际工作中的使用
    ①onResume可见, 可交互.。把动态刷新的操作启动。
    ②onPause部分可见, 不可交互. 把动态刷新的一些操作, 给暂停了。
    ③onCreate 初始化一些大量的数据
    ④onDestroy 把数据给释放掉, 节省内存。

      好了,今天先写到这里,关于Activity单单了解这些是远远不够的,下篇文章讲关于Activity的任务栈已经回退栈等操作。

    作者:李晨玮
    出处:http://www.cnblogs.com/lichenwei/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
    正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!

     

  • 相关阅读:
    Nginx Http模块开发
    nginx模块开发获取post参数
    nginx上传模块nginx_upload_module和nginx_uploadprogress_module模块进度显示,如何传递GET参数等。
    Spring data jpa 复杂动态查询方式总结
    springData Jpa 快速入门
    从一个简单的 JPA 示例开始
    java中使用Protobuf的实例(Demo)
    Protocol Buffers官方文档(开发指南)
    protobuf(Protocol Buffers)java初体验
    [thrift] thrift基本原理及使用
  • 原文地址:https://www.cnblogs.com/lichenwei/p/4766615.html
Copyright © 2020-2023  润新知