Android Activity 详述

activity类处于android.app包中,继承关系:

extends ContextThemeWrapper
implements LayoutInflater.Factory2 Window.Callback KeyEvent.CallbackView.OnCreateContextMenuListenerComponentCallbacks2

java.lang.Object
   ↳ android.content.Context
     ↳ android.content.ContextWrapper
       ↳ android.view.ContextThemeWrapper
         ↳ android.app.Activity

Activity 简介

Activity是什么呢?Activity是一个应用程序提供与用户进行交流的界面。每个Activity都可以通过布局来呈现自己的用户界面,一个应用程序通常包括很多Activity,其中有一个被称为主Activity,这是程序第一次启动所展示的,例如很多程序都有的欢迎界面。将Activity设置成主Activity可以通过配置AndroidManifest.xml文件,将如下代码复制到activity的标签之中:

    <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>  

一个Activity可以启动另一个Activity来实现不同的表现,当一个Activity启动后,它被压入一个stack中,获得焦点,当用户按了back按钮后,当前的Activity从stack中弹出(即被destroyed),先前的Activity被释放从新获得焦点。这些涉及到Activity的生命周期,后面将进行讨论。

怎么创建一个Activity呢?通过继承父类Activity,来创建一个属于自己的Activity,这要求你需要实现Activity父类的回调方法,这些方法在Activity的生命周期中的不同状态被调用,如:创建,暂停,释放,销毁。其中有两个最重要的回调方法:

1.onCreate()

  这个方法在Activity被创建时被调用,在这个方法应该初始化各个控件,通过调用setContentView(R.layout.xxx)方法定义布局来展现Activity的用户界面,其中R.layout.xxx为Activity的XML布局文件。

2.onPause()

  这个方法在用户离开当前Activity时被调用,这就需要在这个方法中保存用户与当前Activity的会话,比如在EditText中输入的值。用户在返回当前Activity时,还应该显示离开时所填写的值。

当然每创建一个Activity都需要在AndroidManifest.xml文件中注册一个相应的Activity。如:创建的Activity的类名为:MyActivity则在文件中需添加如下代码:

     <activity
            android:name=".SecondActivity"
            android:label="@string/app_name" 
            >
      </activity>

Activity 的状态及状态间的转换:

在 android 中,Activity 拥有四种基本状态:

  1. Active/Runing一个新 Activity 启动入栈后,它在屏幕最前端,处于栈的最顶端,此时它处于可见并可和用户交互的激活状态。
  2. Paused 当 Activity 被另一个透明或者 Dialog 样式的 Activity 覆盖时的状态。此时它依然与窗口管理器保持连接,系统继续维护其内部状态,所以它仍然可见,但它已经失去了焦点故不可与用户交互。
  3. Stoped 当 Activity 被另外一个 Activity 覆盖、失去焦点并不可见时处于 Stoped状态。
  4. Killed Activity 被系统杀死回收或者没有被启动时处于 Killed状态。

当一个 Activity 实例被创建、销毁或者启动另外一个 Activity 时,它在这四种状态之间进行转换,这种转换的发生依赖于用户程序的动作。下图说明了 Activity 在不同状态间转换的时机和条件:

图 1. Activity 的状态转换

如上所示,Android 程序员可以决定一个 Activity 的“生”,但不能决定它的“死”,也就时说程序员可以启动一个 Activity,但是却不能手动的“结束”一个 Activity。当你调用Activity.finish()方法时,结果和用户按下 BACK 键一样:告诉 Activity Manager 该 Activity 实例完成了相应的工作,可以被“回收”。随后 Activity Manager 激活处于栈第二层的 Activity 并重新入栈,同时原 Activity 被压入到栈的第二层,从 Active 状态转到 Paused 状态。例如:从 Activity1 中启动了 Activity2,则当前处于栈顶端的是 Activity2,第二层是 Activity1,当我们调用Activity2.finish()方法时,Activity Manager 重新激活 Activity1 并入栈,Activity2 从 Active 状态转换 Stoped 状态,Activity1. onActivityResult(int requestCode, int resultCode, Intent data)方法被执行,Activity2 返回的数据通过data参数返回给 Activity1。

Activity 栈

Android 是通过一种 Activity 栈的方式来管理 Activity 的,一个 Activity 的实例的状态决定它在栈中的位置。处于前台的 Activity 总是在栈的顶端,当前台的 Activity 因为异常或其它原因被销毁时,处于栈第二层的 Activity 将被激活,上浮到栈顶。当新的 Activity 启动入栈时,原 Activity 会被压入到栈的第二层。一个 Activity 在栈中的位置变化反映了它在不同状态间的转换。Activity 的状态与它在栈中的位置关系如下图所示:

图 2. Activity 的状态与它在栈中的位置关系

如上所示,除了最顶层即处在 Active 状态的 Activity 外,其它的 Activity 都有可能在系统内存不足时被回收,一个 Activity 的实例越是处在栈的底层,它被系统回收的可能性越大。系统负责管理栈中 Activity 的实例,它根据 Activity 所处的状态来改变其在栈中的位置。

Activity 生命周期

android.app.Activity类中,Android 定义了一系列与生命周期相关的方法,在我们自己的 Activity 中,只是根据需要复写需要的方法.

Activity生命周期图:

下面我们通过一个实例来说明Activity生命周期。新建工程,编写如下代码:

public class MainActivity extends AppCompatActivity {

    private String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG, "onCreate");
    }

    @Override
    protected void onResume() {
        super.onResume();

        Log.i(TAG, "onResume");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "onStart");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "onStop");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i(TAG, "onRestart");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy");
    }
}
我们通过记录操作和打印日志的方式来看看Activity的生命周期过程;

1、  运行
看到如下打印日志:
11-26 09:09:33.665 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onCreate
11-26 09:09:33.665 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onStart
11-26 09:09:33.665 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onResume
2、按下返回按键:
11-26 09:30:20.097 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onPause
11-26 09:30:20.637 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onStop
11-26 09:30:20.637 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onDestroy
3、长按Home键,弹出最近打开过的应用程序,点击ActivityDemo
11-26 09:31:07.341 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onCreate
11-26 09:31:07.341 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onStart
11-26 09:31:07.341 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onResume
4、按Home键
11-26 09:31:44.649 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onPause
11-26 09:31:45.173 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onStop
5、在AllList中点击打开
11-26 09:32:11.793 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onRestart
11-26 09:32:11.793 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onStart
11-26 09:32:11.793 24933-24933/com.jcdh.jcli.activitydemo I/MainActivity: onResume


通过日志信息,我们可以看到。

Activity的启动过程:onCreate—onStart—onResume;

下返回键时:onPause—onStop—onDestroy 正如上面说是,当按下返回键时,此Activity弹出栈,程序销毁。确实如此;

我们再次 打开时的启动过程又回到onCreate—onStart—onResume。OK;

启动之后按下Home键,回到Launcher,查看打印信息:onPause—onStop;

再次打开的运行过程:onRestart—onStart—onResume;


我们通过对Activity的各种操作,构成了Activity的生命周期,我们看到无论对Activity做如何的操作,都会接收到相关的回调方法,那么我们在开发的过程中通过这些回调方法就可以写工作,比如说释放一些重量级的对象,网络连接,数据库连接,文件读等等。


以下是各个方法的详细说明:

onCreate():当 activity 第一次创建时会被调用。在这个方法中你需要完成所有的正常静态设置 ,比如创建一个视图( view )、绑定列表的数据等等。如果能捕获到 activity 状态的话,这个方法传递进来的 Bundle 对象将存放了 activity 当前的状态。调用该方法后一般会调用 onStart() 方法。

onRestart():在 activity 被停止后重新启动时会调用该方法。其后续会调用 onStart 方法。

onStart() 当 activity 对于用户可见前即调用这个方法。如果 activity回到前台则接着调用 onResume() ,如果 activity 隐藏则调用onStop()

onResume():在 activity 开始与用户交互前调用该方法。在这时该activity 处于 activity 栈的顶部,并且接受用户的输入。其后续会调用 onPause() 方法。

onPause():在系统准备开始恢复其它 activity 时会调用该方法。这个方法中通常用来提交一些还没保存的更改到持久数据 中,停止一些动画或其它一些耗 CPU 的操作等等。无论在该方法里面进行任何操作,都需要较快速完成,因为如果它不返回的话,下一个 activity 将无法恢复出来。如果 activity 返回到前台将会调用 onResume() ,如果 activity 变得对用户不可见了将会调用onStop() 。

onStop():在 activity 对用户不可见时将调用该方法。可能会因为当前 activity 正在被销毁,或另一个 activity (已经存在的activity 或新的 activity )已经恢复了正准备覆盖它,而调用该方法。如果 activity 正准备返回与用户交互时后续会调用onRestart ,如果 activity 正在被释放则会调用 onDestroy 。

onDestroy():在 activity 被销毁前会调用该方法。这是 activity 能接收到的最后一个调用。可能会因为有人调用了 finish 方法使得当前activity 正在关闭,或系统为了保护内存临时释放这个 activity的实例,而调用该方法。你可以用 isFinishing 方法来区分这两种不同的情况。

启动另外一个 Activity

要启动一个新的Activity,我们可以通过调用 startActivity来启动例:

 Intent intent =new Intent(CurrentActivity.this,OtherActivity.class); 
 startActivity(intent);

注意:OtherActivity同样在 AndroidManifest.xml 中定义。

Activity 之间通信使用 Intent 通信

在 Android 中,不同的 Activity 实例可能运行在一个进程中,也可能运行在不同的进程中。因此我们需要一种特别的机制帮助我们在 Activity 之间传递消息。Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容,这好比一封 Email,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个 Intent 对象,消息“目的地”是必须的,而内容则是可选项。

在上面的实例中通过 Activity. startActivity(intent)启动另外一个 Activity 的时候,我们在 Intent 类的构造器中指定了“收件人地址”。

    我们通过 bundle对象来传递信息,bundle维护了一个 HashMap<String, Object>对象,将我们的数据存贮在这个 HashMap 中来进行传递。但是像上面这样的代码稍显复杂,因为 Intent 内部为我们准备好了一个bundle,所以我们也可以使用这种更为简便的方法:
Intent intent =new Intent(MainActivity.this,OtherActivity.class); 
 intent.putExtra("boolean_key", true); 
 intent.putExtra("string_key", "string_value"); 
 startActivity(intent);

接收:

 Intent intent=getIntent(); 
 intent.getBooleanExtra("boolean_key",false); 
 intent.getStringExtra("string_key");

Activity 的 Intent Filter

ntent Filter 描述了一个组件愿意接收什么样的 Intent 对象,Android 将其抽象为 android.content.IntentFilter 类。在 Android 的 AndroidManifest.xml 配置文件中可以通过<intent-filter >节点为一个 Activity 指定其 Intent Filter,以便告诉系统该 Activity 可以响应什么类型的 Intent。

当程序员使用 startActivity(intent) 来启动另外一个 Activity 时,如果直接指定 intent 了对象的 Component 属性,那么 Activity Manager 将试图启动其 Component 属性指定的 Activity。否则 Android 将通过 Intent 的其它属性从安装在系统中的所有 Activity 中查找与之最匹配的一个启动,如果没有找到合适的 Activity,应用程序会得到一个系统抛出的异常。这个匹配的过程如下:

图 4. Activity 种 Intent Filter 的匹配过程

Action 匹配

Action 匹配Action 是一个用户定义的字符串,用于描述一个 Android 应用程序组件,一个 Intent Filter 可以包含多个 Action。在 AndroidManifest.xml 的 Activity 定义时可以在其<intent-filter >节点指定一个 Action 列表用于标示 Activity 所能接受的“动作”,例如:

 <intent-filter > 
 <action android:name="android.intent.action.MAIN" /> 
 <action android:name="com.zy.myaction" /> 
……
 </intent-filter> 

 

如果我们在启动一个 Activity 时使用这样的 Intent 对象:

 Intent intent =new Intent(); 
 intent.setAction("com.zy.myaction"); 

 

那么所有的 Action 列表中包含了“com.zy.myaction”的 Activity 都将会匹配成功。

Android 预定义了一系列的 Action 分别表示特定的系统动作。这些 Action 通过常量的方式定义在 android.content. Intent中,以“ACTION_”开头。我们可以在 Android 提供的文档中找到它们的详细说明。

URI 数据匹配

一个 Intent 可以通过 URI 携带外部数据给目标组件。在 <intent-filter >节点中,通过 <data/>节点匹配外部数据。

mimeType 属性指定携带外部数据的数据类型,scheme 指定协议,host、port、path 指定数据的位置、端口、和路径。如下:

 <data android:mimeType="mimeType" android:scheme="scheme" 
 android:host="host" android:port="port" android:path="path"/> 

 

如果在 Intent Filter 中指定了这些属性,那么只有所有的属性都匹配成功时 URI 数据匹配才会成功。

Category 类别匹配

<intent-filter >节点中可以为组件定义一个 Category 类别列表,当 Intent 中包含这个列表的所有项目时 Category 类别匹配才会成功。

一些关于 Activity 的技巧

锁定 Activity 运行时的屏幕方向

Android 内置了方向感应器的支持。在 G1 中,Android 会根据 G1 所处的方向自动在竖屏和横屏间切换。但是有时我们的应用程序仅能在横屏 / 竖屏时运行,比如某些游戏,此时我们需要锁定该 Activity 运行时的屏幕方向,<activity >节点的android:screenOrientation属性可以完成该项任务,示例代码如下:

 <activity android:name=".EX01"
 android:label="@string/app_name" 
 android:screenOrientation="portrait">// 竖屏 , 值为 landscape 时为横屏
…………
 </activity>

全屏的 Activity

要使一个 Activity 全屏运行,可以在其 onCreate()方法中添加如下代码实现:

 // 设置全屏模式
 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
    WindowManager.LayoutParams.FLAG_FULLSCREEN); 
 // 去除标题栏
 requestWindowFeature(Window.FEATURE_NO_TITLE);

设置Activity 的 Title 

 
setTitle("我的");

实现双击返回键退出功能:

private long exitTime = 0;

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){   
        if((System.currentTimeMillis()-exitTime) > 2000){  
            Toast.makeText(getApplicationContext(), "再按一次退出程序", Toast.LENGTH_SHORT).show();                                
            exitTime = System.currentTimeMillis();   
        } else {
            finish();
            System.exit(0);
        }
        return true;   
    }
    return super.onKeyDown(keyCode, event);
}


部分来源:

http://www.ibm.com/developerworks/cn/opensource/os-cn-android-actvt/

Demo 下载:

posted @ 2015-11-26 18:21  恋恋西风  阅读(281)  评论(0编辑  收藏  举报