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初步,初学者必看的更多相关文章

  1. 15个初学者必看的基础SQL查询语句

    本文由码农网 – 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 本文将分享15个初学者必看的基础SQL查询语句,都很基础,但是你不一定都会,所以好好看看吧. 1.创建表和数据插 ...

  2. mysql进阶(二十六)MySQL 索引类型(初学者必看)

    mysql进阶(二十六)MySQL 索引类型(初学者必看)   索引是快速搜索的关键.MySQL 索引的建立对于 MySQL 的高效运行是很重要的.下面介绍几种常见的 MySQL 索引类型.   在数 ...

  3. java初学者必看之构造方法详细解读

    java初学者必看之构造方法详细解读 构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法. 格式 public 类名称(参数类型 参数名称){ 方法体 } 注 ...

  4. web前端开发初学者必看的学习路线(附思维导图)

    很多同学想学习WEB前端开发,虽然互联网有很多的教程.网站.书籍,可是却又不知从何开始如何选取.看完网友高等游民白乌鸦无私分享的原标题为<写给同事的前端学习路线>这篇文章,相信你会有所收获 ...

  5. Python初学者必看(1)

    python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言 ...

  6. (纯干货)最新WEB前端学习路线汇总初学者必看

    Web前端好学吗?这是很多web学习者常问的问题,想要学习一门自己从未接触过的领域,事先有些了解并知道要学的内容,对接下来的学习会有事半功倍的效果.在当下来说web前端开发工程师可谓是高福利.高薪水的 ...

  7. PHP常用180函数总结【初学者必看】

    数学函数 1.abs(): 求绝对值 <span style="font-size: 14px;">$abs = abs(-4.2); //4.2<br>& ...

  8. JavaScript初学者必看“this”

    译者按: JavaScript的this和Java等面向对象语言中的this大不一样,bind().call()和apply()函数更是将this的灵活度进一步延伸. 原文: JavaScript: ...

  9. UI设计初学者必看,这款设计神器教你快速入门

    网络时代,网页和手机App已经深入到人们生活的方方面面.这也使得App界面设计越来越受青年求职者们的青睐,并纷纷投入这个行业.但是,作为UI设计初学者,究竟如何才能快速的入门?当今市场上,是否有那么一 ...

随机推荐

  1. Windows Phone 执行模型概述

    Windows Phone 执行模型控制在 Windows Phone 上运行的应用程序的生命周期,该过程从启动应用程序开始,直至应用程序终止. 该执行模型旨在始终为最终用户提供快速响应的体验.为此, ...

  2. javascript优化--01高质量编码

    javascript的浮点数: Javascript的数字都是双精度浮点数: 64位编码数字: 能表达53位精度的整数: 进行位运算时会隐式地转化为32位整数(0,1序列)后计算: 浮点数运算可能会有 ...

  3. LCIS POJ 2172 Greatest Common Increasing Subsequence

    题目传送门 题意:LCIS(Longest Common Increasing Subsequence) 最长公共上升子序列 分析:a[i] != b[j]: dp[i][j] = dp[i-1][j ...

  4. DelPhi连接数据库方式

    一.SQL Server 2000 的连接数据库 1.无密码连接SQLL:='Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;In ...

  5. BZOJ3619 : [Zjoi2014]璀灿光华

    终于把省选时的遗憾补上了… 对于构造立方体: 首先BFS构出底层,然后再逐层构造立方体 对于计算: $O(n^6)$爆搜即可. #include<cstdio> #include<c ...

  6. 优雅绝妙的Javascript跨域问题解决方案

    关于Javascript跨域问题的解决方案已在之前的一片文章中详细说明,详见:http://blog.csdn.net/sfdev/archive/2009/02/13/3887006.aspx: 除 ...

  7. HDU 4632 Palindrome subsequence(DP)

    题目链接 做的我很无奈,当时思路很乱,慌乱之中,起了一个想法,可以做,但是需要优化.尼玛,思路跑偏了,自己挖个坑,封榜之后,才从坑里出来,过的队那么多,开始的时候过的那么快,应该就不是用这种扯淡方法做 ...

  8. python模块之codecs

    http://blog.csdn.net/suofiya2008/article/details/5579413  

  9. libtiff4.04

    http://www.linuxfromscratch.org/blfs/view/svn/general/libtiff.html 安装方法 : ./configure --prefix=/usr ...

  10. Java_BigDecimal类型比较大小

    这个类是java里精确计算的类 1 比较对象是否相等 一般的对象用equals,但是BigDecimal比较特殊,举个例子: BigDecimal a=BigDecimal.valueOf(1.0); ...