安卓开发笔记——重识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方法。

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

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

 

时间: 2024-11-12 03:37:06

安卓开发笔记——重识Activity的相关文章

安卓开发笔记——深入Activity

在上一篇文章<安卓开发笔记——重识Activity >中,我们了解了Activity生命周期的执行顺序和一些基本的数据保存操作,但如果只知道这些是对于我们的开发需求来说是远远不够的,今天我们继续探索Activity,来了解下关于Activity任务栈和Activity四种启动模式的区别. 为什么需要了解关于Activity的任务栈,其实最直接的体现就是提高用户交互友好性. 举个例子,当我们去浏览一个新闻客户端的时候,我们进入了新闻详情页,在这个页面有相隔两条的新闻标题,当我们去点击这个标题的时

安卓开发笔记——丰富多彩的TextView

随手笔记,记录一些东西~ 记得之前写过一篇文章<安卓开发笔记——个性化TextView(新浪微博)>:http://www.cnblogs.com/lichenwei/p/4411607.html 文章里实现个性化TextView的主要方法是通过替换的方式,对关键字进行一些个性化处理,晚上再来补充一种实现方式. 老规矩,先看下效果图: 晚上带来的这种实现方式是通过Android官方给我们提供的Html类下面的fromHtml方法,这个方法可以对字符串进行HTML格式化,让TextView等一些

安卓开发笔记——关于Handler的一些总结(上)

接上篇文章<安卓开发笔记——关于AsyncTask的使用>,今天来讲下在安卓开发里"重中之重"的另一个异步操作类Handler. 今天打算先讲下关于Handler的一些基本定义和使用方式 还是以一个下载图片为例,先看下实例效果: 好了,先来看下关于Handler的定义: 以上是官方对于Hanler类的描述,大致意思是说:Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息

安卓开发笔记——打造属于自己的博客园APP(四)

在上篇文章<安卓开发笔记——打造属于自己的博客园APP(三)>中,我们对博客文章的详情页和评论页进行了实现,慢慢的一个APP已经出现雏形了,当然这只是完成了"表面效果",要真正做好一个APP并不是一件很轻松的事情,有很多细节需要我们一点一滴的去完善. 好了,来讲下今天要完成的效果,在优化了之前部分代码的前提下,今天来说下关于博客搜索和博客详情页的实现,依旧国际惯例,来看下效果图:(动态图片比较大,加载需要点时间) 效果比较简单,很多东西我们还是可以复用之前的代码,毕竟这种列

安卓开发笔记——自定义广告轮播Banner(无限循环实现)

关于广告轮播,大家肯定不会陌生,它在现手机市场各大APP出现的频率极高,它的优点在于"不占屏",可以仅用小小的固定空位来展示几个甚至几十个广告条,而且动态效果很好,具有很好的用户"友好性",下面来看几个示例图:     再来看下我仿写的效果: 关于广告轮播Banner这个东西,GitHub上面应该有现成的开源组件,不过我没去找过,觉得实现起来不会太难,就自己去仿写了,下面我说下实现的思路: 1.首先看到这个可以滑动切换图片的界面,我们很自然就会想到ViewPager

安卓开发笔记——多种方式实现底部菜单栏(仿微信界面)

关于底部菜单是什么,我想没必要介绍了,在市场上的APP里太常见了,这里提供两种方式来实现. 记得之前写过几篇关于底部菜单实现的方法,有兴趣的朋友可以看看: 1.<安卓开发复习笔记——TabHost组件(一)(实现底部菜单导航)> 2.<安卓开发复习笔记——TabHost组件(二)(实现底部菜单导航)> 3.<安卓开发笔记——Fragment+FragmentTabHost组件(实现新浪微博底部菜单)> 今天带来种相对更通俗易懂的写法,不再和过去一样去沿用TabHost了

安卓开发笔记——打造万能适配器(Adapter)

为什么要打造万能适配器? 在安卓开发中,用到ListView和GridView的地方实在是太多了,系统默认给我们提供的适配器(ArrayAdapter,SimpleAdapter)经常不能满足我们的需要,因此我们时常要去继承BaseAdapter类去实现一个自定义的适配器来满足我们的场景需要. 如果你是开发一个简单点的APP还好,可能ListView和GridView的数量不会太多,我们只要去写几个BaseAdapter实现类就可以了. 但如果有一天,你需要开发一个APP里面具有几十个ListV

安卓 开发笔记目录

安卓 开发笔记index 安卓基础 Fragment总结 安卓 BroadcastReceiver笔记 安卓 Notification-通知总结 开源框架笔记 安卓 okhttp小结 EventBus框架总结 安卓 图片加载框架ImageLoader 第三方SDK 安卓 短信验证MobSMS集成 开源项目 其他 安卓 常用属性设置代码笔记 安卓 代码混淆与打包 AOSP开发笔记 开发工具 SecureCRT连接Ubuntu配置 Ubuntu开发环境搭建 开发环境 AOSP android7.1.

安卓开发笔记——高仿新浪微博文字处理(实现关键字高亮,自定义表情替换并加入点击事件实现)

先让大家看下效果图,这个是我自己在闲暇时间仿写的新浪微博客户端: 今天来讲讲如何实现上图的效果,这里需要用到SpannableString这个工具类,如果你对这个类并不熟悉,可以先看下我之前写的2篇文章: <安卓开发笔记——个性化TextView(新浪微博)>:http://www.cnblogs.com/lichenwei/p/4411607.html <安卓开发笔记——丰富多彩的TextView>:http://www.cnblogs.com/lichenwei/p/46120