Android四大组件之Activity

Android四大组件之Activity

概念

  一个Activity是一个引用程序组件,在手机显示屏上提供一个单独显示数据和给与用户交互的界面。窗口通常全屏显示,但是也可以小于屏幕而浮于屏幕。

  一个应用程序通常由多个activities组成,他们通常是松耦合关系。通常,一个应用程序中的activity被指定为”main”activity,当第一次启动应用程序的时候呈现给用户的那个activity。每一个activity然后可以启动另一个activity为了完成不同的动作。每一次一个activity启动,前一个activity就停止了,但是系统保留activity在一个栈上(“back stack”)。当一个新activity启动,它被推送到栈顶,取得用户焦点。Back Stack符合简单“后进先出”原则,所以,当用户完成当前activity然后点击back按钮,它被弹出栈(并且被摧毁),然后之前的activity恢复。

  当一个activity因新的activity启动而停止,它被通知这种状态转变通过activity的生命周期回调函数。有许多回调函数一个activity可能会收到,源于它自己的状态变化-无论系统创建它、停止它、恢复它、摧毁它-并且每个回调提供你完成适合这个状态的指定工作的机会。例如,当停止的时候,你的activity应该释放任何大的对象,例如网络数据库连接。当activity恢复,你可以重新获得必要的资源和恢复被中断的动作。这些状态转换都是activity的生命周期的部分。

创建一个Activity

创建一个Activity是很简单的一个操作,在继承android.app.Activity的同时在manifest文件当中注册即可

public class EX01 extends Activity { 
    private static final String LOG_TAG = EX01.class.getSimpleName(); 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);      
        setContentView(R.layout.main); 
        Log.e(LOG_TAG, "onCreate"); 
    } 
    @Override 
    protected void onStart() { 
        Log.e(LOG_TAG, "onStart"); 
        super.onStart(); 
    } 
    @Override 
    protected void onResume() { 
        Log.e(LOG_TAG, "onResume"); 
        super.onResume(); 
    } 
    @Override 
    protected void onPause() { 
        Log.e(LOG_TAG, "onPause"); 
        super.onPause(); 
    } 
    @Override 
    protected void onStop() { 
        Log.e(LOG_TAG, "onStop"); 
        super.onStop(); 
    } 
    @Override 
    protected void onDestroy() { 
        Log.e(LOG_TAG, "onDestroy "); 
        super.onDestroy(); 
    } 
 }

AndroidManifest.xml 中通过 节点说明 Activity,将 apk 文件安装后,系统根据这里的说明来查找读取 Activity

<activity android:name=".EX01" android:label="@string/app_name"> 
     <intent-filter> 
         <action android:name="android.intent.action.MAIN" /> 
         <category android:name="android.intent.category.LAUNCHER" /> 
     </intent-filter> 
 </activity>

IntentFilter

Intent 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,应用程序会得到一个系统抛出的异常。这个匹配的过程如下:

匹配
graph TD
    A[加载安装所有的Intent Filter到一个列表当中] --> B(剔除Action匹配失败的Intent Filter)
    B --> C(剔除URI匹配失败的Intent Filter)
    C --> D(剔除Category匹配失败的Intent Filter)
    D --> E{余下的Intent Filter数量是否为0}
    E --> |否| F(将匹配成功的Intent Filter按照优先级排序)
    E --> |是| G(查找失败,抛出异常)
    F --> H(返回优先级最高的Intent Filter)

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 携带外部数据给目标组件。在 节点中,通过 节点匹配外部数据。
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 类别匹配

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

Activity的生命周期

初始化Activity
graph TD
    A[onCreate] --> B[onWindowAttributesChanged]
    B --> C[onStart]
    C --> D[onResume]
    D --> E[onWindowFocusChanged]

在上一步的基础之上点击Home键
graph TD
    A[onWindowFocusChanged] --> B[onPause]
    B --> C[onStop]

重新进入Activity(在不退出的情况下)
graph TD
    A[onReStart] --> B[onStart]
    B --> C[onResume]
    C --> D[onWindowFocusChanged]

在第一步的基础上点击返回(相当于退出应用程序,再次进入从onCreate开始)
graph TD
    A[onPause] --> B[onWindowFocusChanged]
    B --> C[onStop]
    C --> D[onDestroy]

点击跳转按键
graph TD
    A[onPause] --> B[onWindowFocusChanged]
    B --> C[onStop]

跳转之后返回(LiveActivity2)的生命周期
graph TD
    A[onPause] --> B[onWindowFocusChanged]
    B --> C[onStop]
    C --> D[onDestroy]

跳转之后返回(LiveActivity)的生命周期
graph TD
    A[onReStart] --> B[onStart]
    B --> C[onResume]
    C --> D[onWindowFocusChanged]

生命周期测试到此结束,点击Home按键和返回按键式不一样的,Home键相当于程序在后台运行,没有正真退出程序,但是返回按键式退出了整个程序。
要想改变这一状态可以使用以下方法来实现:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {

     if(keyCode==KeyEvent.KEYCODE_HOME){
    return false;
     }
     return super.onKeyDown(keyCode, event);
}

如果想点击返回按键不退出程序,而是程序在后台执行,可以使用以下方法:

@Override  
public boolean onKeyDown(int keyCode, KeyEvent event) {  
    if (keyCode == KeyEvent.KEYCODE_BACK) {  
        moveTaskToBack(false);  
        return true;  
    }  
    return super.onKeyDown(keyCode, event);  
} 

以上测试程序:

package vgod.myapplication;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;

/**
 * User: niuwei([email protected])
 * Date: 2015-04-23
 * Time: 01:33
 * FIXME
 */
public class LiveActivity extends Activity{
    private String TAG = getClass().getSimpleName() + "Test";
    private Button button;

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

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

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume()");
    }

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

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

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy()");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate()");
        setContentView(R.layout.activity_live_test);
        button = (Button)findViewById(R.id.live_btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(LiveActivity.this, LiveActivity2.class));
            }
        });
    }

    @Override
    public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
        super.onWindowAttributesChanged(params);
        Log.d(TAG, "onWindowAttributesChanged()");
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        Log.d(TAG, "onWindowFocusChanged()");
    }
}
package vgod.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;

/**
 * User: niuwei([email protected])
 * Date: 2015-04-23
 * Time: 01:35
 * FIXME
 */
public class LiveActivity2 extends Activity{
    private String TAG = getClass().getSimpleName();

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

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

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume()");
    }

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

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

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy()");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate()");
    }

    @Override
    public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
        super.onWindowAttributesChanged(params);
        Log.d(TAG, "onWindowAttributesChanged()");
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        Log.d(TAG, "onWindowFocusChanged()");
    }
}

Activity栈和模式

Activity的启动方式有四个:
- standard
- singleTop
- singleTask
- singleInstance
在实际的项目中,应该选择合适的启动方式,避免在栈当中大量的使用重复的Activity。下面介绍一下各自的作用:

注册模式

在使用之前需要在activity标签当中声明模式:

<activity  
    android:name="com.test.MainActivity"  
    android:launchMode="standard" /> 

standard模式

这是Android的Activity默认的启动模式,使用这个模式的时候,可以存在相同的模式,允许Activity的叠加。

比如有两个Activity A、B,AB可以互相跳转,当使用情景为:A-A-A-B-B-A的时候,Activity栈中存在五个Activity,而且每一个都是一个独立的屏幕显示内容,在返回的时候会依次执行每一个Activity的生命周期函数。

singleTop模式

可以使用多个相同的实例,但是不允许相同的Activity叠加,如果A位于栈顶,当继续跳转到A的时候不会在声明一个Activity,而是直接使onNewIntent方法

singleTask模式

只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。

singleInstance模式

只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。

Activity跳转

启动一个Activity

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

当然,OtherActivity同样需要在 AndroidManifest.xml 中定义。在跳转的同时可以使用Bundle进行数据传递

Activity数据传递

使用Bundle

在 Android 中,不同的 Activity 实例可能运行在一个进程中,也可能运行在不同的进程中。因此我们需要一种特别的机制帮助我们在 Activity 之间传递消息。Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容,这好比一封 Email,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个 Intent 对象,消息“目的地”是必须的,而内容则是可选项。
在上面的实例中通过 Activity. startActivity(intent)启动另外一个 Activity 的时候,我们在 Intent 类的构造器中指定了“收件人地址”。
如果我们想要给“收件人”Activity 说点什么的话,那么可以通过下面这封“e-mail”来将我们消息传递出去:

Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);
// 创建一个带“收件人地址”的 email 
Bundle bundle =new Bundle();// 创建 email 内容
bundle.putBoolean("boolean_key", true);// 编写内容
bundle.putString("string_key", "string_value"); 
intent.putExtra("key", bundle);// 封装 email 
startActivity(intent);// 启动新的 Activity

上面我们通过 bundle对象来传递信息,bundle维护了一个 HashMap

Intent intent =new Intent(EX06.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");

使用 SharedPreferences

SharedPreferences使用xml格式为Android应用提供一种永久的数据存贮方式。对于一个Android应用,它存贮在文件系统的/data/data/your_app_package_name/shared_prefs/目录下,可以被处在同一个应用中的所有 Activity 访问。Android提供了相关的API来处理这些数据而不需要程序员直接操作这些文件或者考虑数据同步问题。

// 写入 SharedPreferences 
SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE); 
Editor editor = preferences.edit(); 
editor.putBoolean("boolean_key", true); 
editor.putString("string_key", "string_value"); 
editor.commit(); 

// 读取 SharedPreferences 
SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE); 
preferences.getBoolean("boolean_key", false); 
preferences.getString("string_key", "default_value");

其它方式

Android 提供了包括SharedPreferences在内的很多种数据存贮方式,比如 SQLite,文件等,程序员可以通过这些API实现Activity之间的数据交换。如果必要,我们还可以使用 IPC 方式。

关闭Activity

在Activity当中,如果图片资源大,导致OOM异常使得程序崩溃,那么可以在Activity的onStop或者onDestroy当中调用一下方法来彻底关闭Activity和回收资源:

//先回收一些View相关的变量,比如
imageView = null;
//然后使得程序整个contentView为空,提前建立一个空得xml
setContentView(R.layout.null_view);
finish();
//如果不执行gc函数,java虚拟机不会迅速回收资源而是等到内存不足的
//时候再做回收,有时候程序就这么崩溃了
System.gc();

一些关于 Activity 的技巧

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

为了更友好的用户体验,在处理一些需要花费较长时间的任务时可以使用一个进度条来提示用户“不要着急,我们正在努力的完成你交给的任务”。如下图:
在 Activity 的标题栏中显示进度条不失为一个好办法,下面是实现代码:
“`java
// 不明确进度条
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.main);
setProgressBarIndeterminateVisibility(true);

// 明确进度条
requestWindowFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.main);
setProgress(5000);
“`

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。