Activity初步,初学者必看
Activity是什么?
Activity是一个可与用户交互并呈现组件的视图。通俗说就是运行的程序当前的这个显示界面。
如果你还不明白,那么你写过HTML吗,它就好比一个网页。我们程序中的用户视图,都是Activity类的一个实例。
Activity Manager与Activity回退栈
一个Android应用程序,可以有一个或者更多的Activity。Android系统通过一个Activity Manager 来管理所有应用程序的Activity,确切说
Activity Manager维护着一个Activity栈,也就是以栈的形式来管理Activity。这是什么意思呢?
每当程序创建并显示一个新的Activity,这个Activity就会被压到Activity栈的栈顶,原先的Activity就看不见了。置于栈顶的下一层。
从用户的角度看就是老Activity消失,这个Activity视图来到前台,显示。
当用户点击手机上的Back键,当前Activity就会被销毁,这个Activity从栈中弹出,下一层的Activity就会重新回到前台,显示。
这个机制就好比浏览器的回退按钮的作用:点击回退按钮,显示上一个浏览过的网页。
下面是一个抽象出来的Activity栈的模型图,并不是在向你解释手机液晶屏的制作工艺~
Activity的生命周期及常用方法
官方那张图已经被传烂了。为了不占用篇幅,让你看起来很累,我把它放到文章的最后。
在一个应用程序运行期间,它包含的Activity有多种生命周期状态。作为开发者,你不必像C++程序员管理他们的堆内存一样,手动管理
这些Activity,它是由Activity Manager来管理的。
但是你可以在Activity的状态发生变化时,得到通知。
比如,当Activity第一次创建时,onCreate()就会被调用,当Activity被销毁时,onDestroy()函数被调用。
Activity还有一些其他的onXXX()形式的函数。
作为开发者,我们的主要精力就是Override这些函数,以便在Activiy发生相应的生命周期变化时,通过这些函数去处理(回应)一些我们
关注的工作。
• onCreate(Bundle savedInstanceState)
这个函数在Activity第一次创建时调用。你可以在这个函数中做一些 "一次性工作",比如用户界面视图的初始化: setContentView(R.layout.xx)
再比如获取组件的引用 mXXX = (T)findViewById(R.id.xxx)
以及为组件绑定监听器 mXXX.setOnClickListener(new OnClickListener(){......});
savedInstanceState是一个Bundle对象,Bundle,顾名思义,代表数据的捆绑体(数据包)。它的作用后面讲。
•onStart()
当Activity显示并可见时,调用。
•onResume()
当Activity可以与用户交互(可被操作)时调用,在这个方法中播放动画或者music是个好主意
•onPause()
当Activity将被调回后台时调用,一般是因为某个其他的Activity出现在,挡住这个Activity而发生。
在这方法中,你最好保存你需要永久保存的数据,比如数据库数据,或者编辑的文本。
•onStop()
这个Activity完全不可见了,被压到后台。"我暂时不需要这个界面接口了","哪你就待在后台吧"
注意:如何内存使用紧缺,android会直接kill掉程序的进程,这个方法可能来不及被调用,
•onRestart()
"刚刚被丢在后台的那个Activity,我现在又需要你了,你出来吧"
户从后台重新调回后台的Activity,然后onRestart()就会调用,接下来就调用onStart()方法。
onStop() .............onRestart()....................onStart() ......onResume()
丢到后台 调到前台从新开始 可见 可交互
•onDestroy()
当Activity被销毁前,调用。
注意:如何内存使用紧缺,android会直接kill掉程序的进程,这个方法可能来不及被调用.
从上面的概述来看,onStop()和onDestroy()方法会不会得到调用都是不能百分百保障的,因为一旦一个Activity被置于后台,在内存紧缺的
情况下,为了给新的Activity创建提供内存,android会直接干掉后台Activity的进程。
那么,最安全的阵营就是onPause()方法了。这是你必须知道的。
下面我们通过代码证实一下Activity的生命周期的转换机制。
MainActivity.java
import android.app.Activity; import android.os.Bundle; import android.util.Log; public class MainActivity extends Activity { public static String final TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.v(TAG,"onCreate"); } @Override protected void onStart() { super.onStart(); Log.v(TAG,"onStart"); } @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); Log.v(TAG,"onResume"); } @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); Log.v(TAG,"onPause"); } @Override protected void onRestart() { // TODO Auto-generated method stub super.onRestart(); Log.v(TAG,"onRestart"); } @Override protected void onStop() { // TODO Auto-generated method stub super.onStop(); Log.v(TAG,"onStop"); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); Log.v(TAG,"onDestroy"); } }
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Activity life cycle test" /> </LinearLayout>
最后的一个屏幕旋转演示时卡住了,只能说Android虚拟机太吃内存了,我得加内存条了 - - 。大家可以自己测试。
依次如下操作,并在LogCat中查看程序打印的日志
启动应用程序 : onCreate ... onStart ... onResume
在onResume状态下,点击回退按钮
onPause ... onStop ... onDestroy
在onResume状态下,点击Home按钮
onPause ... onStop
调出后台任务,重新回到这个Activity :
onRestart ... onStart ... onResume
在onResume状态下,旋转屏幕:
onPause ... onStop ... onDestroy ... onCreate ... onStart ... onResume
Activity的数据保存与恢复
下面通过一个例子来说名Activity数据的保存和恢复的作用和过程。
例子的思路是这样的:在竖屏状态下,输入70,将初始为0 的 mTotal累加到70,并通过Toast显示,然后,切换手机为
横屏,再输入30,将mTotal累加到100,再次显示。
java文件
import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends Activity { private int mTotal = 0; //累计储存变量 private EditText mInputText = null; private Button mSubmitButton = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mInputText = (EditText)findViewById(R.id.id_input_text); mSubmitButton = (Button)findViewById(R.id.id_submit_button); mSubmitButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try{ int inputNum = Integer.parseInt(mInputText.getText().toString()); mTotal+=inputNum; showToast(String.format("total=%d", mTotal)); }catch(Exception e) { showToast("请输入一个数!!!"); } } }); } private void showToast(String s) { Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show(); } }
布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <EditText android:id="@+id/id_input_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="numberSigned" android:background="#55667A" android:textColor="#000000" android:textSize="20sp" />
<!--android:inputType="numberSigned" 表示只允许输入带符号的数-->
<Button android:id="@+id/id_submit_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:text="提交" /> </LinearLayout>
结果出乎意外,Toast显示为30,并不是100,。那么先前输入的70跑哪里去呢?
通过前面对Activity生命周期的状态切换的例子发现:当手机旋转后,原来的Activity被销毁了,取而代之的是一个新建的Activity实例。
而total变量是Activity实例的一个字段,也就是说,total为70时的Activity随着Activity销毁而销毁了。新的Activity创建时,新的mTotal被初始化为0。
所以mTotal的值并没有随着屏幕旋转得到保留。
怎么解决这个问题呢?下面再介绍2个Activity的onXXX()形式的函数。
•onSaveInstanceState(Bundle outState)
SaveInstanceState,很明显,意为保存实例状态(数据),on,表明它是一个触发函数。
它的参数是一个Bundle类对象,也就是数据包对象,我们可以 将需要保存的数据,put到Bundle对象outState中。从数据结构的角度上说,Bundle其实就是
一个Map,有的也叫Dictionary,是通过key - value的形式存取数据的。
同样,onSaveInstanceState方法也是自动被调用的,也就是说,你不用关心onSaveInstanceState何时被调用, 你只需关心,你需要保存哪些数据 。
确切说,onSaveInstanceState是在onPause() 、onStop、onDestroy 调用之前被调用。
这也符合常理,当Activity有被销毁的趋势时,就开始保存需要保存的数据,为下次创建使用。
当activity被销毁后,android系统会保存它对应的Bundle对象,当这个anctivity再次被激活实例化时,则可以从先前的Bundle中提出保存的数据,无损恢复。
况且onPause()是最安全的阵营。onStop()和onDestroy()方法能不能被调用是不能百分百保证的。
如果不复写这个方法,默认情况下,这个方法会保存所有的控件状态数据。
请注意上面图片中,EditText输入框中的内容70,它并没有在屏幕旋转后清空,说明默认的onSaveInstanceState()方法起到作用了。
不信你可以测试一下,复写onSaveInstanceState方法,注释掉super.onSaveInstanceState(outState),输入数字旋转屏幕,会发现数字消失了。
那么,复写这个方法的意义在于,在MVC的Model层,保存你自定义的数据。这正符合我们的意图:保存mTotal
•onRestoreInstanceState(Bundle savedInstanceState)
上面介绍了如何暂存数据,这个当然就是如何将数据再取出来啦!
当一个Activity重新创建并初始化时,这个方法就会调用,在这里重新获取先前通过onSaveInstanceState(Bundle outState)方法保存的Activity销毁
前保留的数据。
默认情况下,这个方法会重新初始化用户控件的状态。
onRestoreInstanceState()会在onResume()之前调用,因为我们需要在activity可以与用户交互前做数据的恢复工作,这同样也符合常理。
下面开始fix前面mTotal例子的BUG!
修改后的java文件
import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends Activity { //为存储的数据对象创建一个KEY public static final String KEY_MTOTAL = "MainActivity.MTOTAL"; private int mTotal = 0; private EditText mInputText = null; private Button mSubmitButton = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mInputText = (EditText)findViewById(R.id.id_input_text); mSubmitButton = (Button)findViewById(R.id.id_submit_button); mSubmitButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try{ int inputNum = Integer.parseInt(mInputText.getText().toString()); mTotal+=inputNum; showToast(String.format("total=%d", mTotal)); }catch(Exception e) { showToast("请输入一个数!!!"); } } }); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); //存储空间状态 outState.putInt(KEY_MTOTAL, mTotal); //存储自定义数据,key - value形式 } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); //恢复控件状态 if(savedInstanceState!=null) { //通过KEY,提取mTotal的值,最后一个参数0表示如果提取失败,则让mTotal为0 mTotal = savedInstanceState.getInt(KEY_MTOTAL , 0); } } private void showToast(String s) { Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show(); } }
Bundle形象点说就时一个暂存数据的"包裹",它为横屏Activity和竖屏Activity之间数据传递搭建了一个桥梁。
Bundle对象中有很多putXXX和getXXX形式的方法,用来存取数据,这些数据都是通过key - value的形式存取的,所以你需要为存取的数据定义一个
独一无二的KEY值(String类型)。同时你还需要注意的是,Bundle只能存取一些常用的内置类型数据,如果你要存储你自定义的类型,则需要让你的类
实现Parcelable或者Serializable接口,限于篇幅,这里不再扩展,以后我会写出来分享。
细心的同学会发现,onCreate方法的参数也是一个Bundle,你也可以在onCreate方法中来恢复数据。不过我还是建议大家让每个函数各尽其职。
Activity与进程的关系
进程 != 程序
实质上讲,每一个用户操作视图,就是一个Activity的实例,每一个Activity的实例都有自己的生命周期。
应用程序就是一个或多个Activity,再加上容纳这些Activity并为Activity提供服务的进程。
在android中,即便一个应用程序的进程被干掉了,它仍然可以是“活着的”,因为
进程并不是和Activity绑定的。进程就像公交车,而Activity就是乘客,乘客的一天也是有不同的状态的,天亮了,起床乘公交
上班,中午,乘公交回家吃饭,下午,乘公交回家。可以发现,同样的道理,乘客并不依赖某一辆公交,比如上班的时候坐的是25路,
回家也可以坐32路公交,只要这辆车能为乘客完成自己的状态转换就行。没有公交车,乘客永远不能完成自己的状态转换,
没有进程,Activity也不能完成自己生命周期的切换,这也是为什么onStop()和onDestroy()方法不能得到调用的原因。
Activity初步,初学者必看的更多相关文章
- 15个初学者必看的基础SQL查询语句
本文由码农网 – 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 本文将分享15个初学者必看的基础SQL查询语句,都很基础,但是你不一定都会,所以好好看看吧. 1.创建表和数据插 ...
- mysql进阶(二十六)MySQL 索引类型(初学者必看)
mysql进阶(二十六)MySQL 索引类型(初学者必看) 索引是快速搜索的关键.MySQL 索引的建立对于 MySQL 的高效运行是很重要的.下面介绍几种常见的 MySQL 索引类型. 在数 ...
- java初学者必看之构造方法详细解读
java初学者必看之构造方法详细解读 构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法. 格式 public 类名称(参数类型 参数名称){ 方法体 } 注 ...
- web前端开发初学者必看的学习路线(附思维导图)
很多同学想学习WEB前端开发,虽然互联网有很多的教程.网站.书籍,可是却又不知从何开始如何选取.看完网友高等游民白乌鸦无私分享的原标题为<写给同事的前端学习路线>这篇文章,相信你会有所收获 ...
- Python初学者必看(1)
python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言 ...
- (纯干货)最新WEB前端学习路线汇总初学者必看
Web前端好学吗?这是很多web学习者常问的问题,想要学习一门自己从未接触过的领域,事先有些了解并知道要学的内容,对接下来的学习会有事半功倍的效果.在当下来说web前端开发工程师可谓是高福利.高薪水的 ...
- PHP常用180函数总结【初学者必看】
数学函数 1.abs(): 求绝对值 <span style="font-size: 14px;">$abs = abs(-4.2); //4.2<br>& ...
- JavaScript初学者必看“this”
译者按: JavaScript的this和Java等面向对象语言中的this大不一样,bind().call()和apply()函数更是将this的灵活度进一步延伸. 原文: JavaScript: ...
- UI设计初学者必看,这款设计神器教你快速入门
网络时代,网页和手机App已经深入到人们生活的方方面面.这也使得App界面设计越来越受青年求职者们的青睐,并纷纷投入这个行业.但是,作为UI设计初学者,究竟如何才能快速的入门?当今市场上,是否有那么一 ...
随机推荐
- Windows Phone 执行模型概述
Windows Phone 执行模型控制在 Windows Phone 上运行的应用程序的生命周期,该过程从启动应用程序开始,直至应用程序终止. 该执行模型旨在始终为最终用户提供快速响应的体验.为此, ...
- javascript优化--01高质量编码
javascript的浮点数: Javascript的数字都是双精度浮点数: 64位编码数字: 能表达53位精度的整数: 进行位运算时会隐式地转化为32位整数(0,1序列)后计算: 浮点数运算可能会有 ...
- LCIS POJ 2172 Greatest Common Increasing Subsequence
题目传送门 题意:LCIS(Longest Common Increasing Subsequence) 最长公共上升子序列 分析:a[i] != b[j]: dp[i][j] = dp[i-1][j ...
- DelPhi连接数据库方式
一.SQL Server 2000 的连接数据库 1.无密码连接SQLL:='Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;In ...
- BZOJ3619 : [Zjoi2014]璀灿光华
终于把省选时的遗憾补上了… 对于构造立方体: 首先BFS构出底层,然后再逐层构造立方体 对于计算: $O(n^6)$爆搜即可. #include<cstdio> #include<c ...
- 优雅绝妙的Javascript跨域问题解决方案
关于Javascript跨域问题的解决方案已在之前的一片文章中详细说明,详见:http://blog.csdn.net/sfdev/archive/2009/02/13/3887006.aspx: 除 ...
- HDU 4632 Palindrome subsequence(DP)
题目链接 做的我很无奈,当时思路很乱,慌乱之中,起了一个想法,可以做,但是需要优化.尼玛,思路跑偏了,自己挖个坑,封榜之后,才从坑里出来,过的队那么多,开始的时候过的那么快,应该就不是用这种扯淡方法做 ...
- python模块之codecs
http://blog.csdn.net/suofiya2008/article/details/5579413
- libtiff4.04
http://www.linuxfromscratch.org/blfs/view/svn/general/libtiff.html 安装方法 : ./configure --prefix=/usr ...
- Java_BigDecimal类型比较大小
这个类是java里精确计算的类 1 比较对象是否相等 一般的对象用equals,但是BigDecimal比较特殊,举个例子: BigDecimal a=BigDecimal.valueOf(1.0); ...