Android 四大组件之 Activity

什么是 Activity?

Activity 是一种能够包含用户界面,提供与用户交互功能的 Android 组件,一个应用可以包含零个或多个 Activity,但是不包含任何 Activity 的应用并不常见,毕竟谁也不想自己的应用无法被用户看到吧?

Activity 继承自 ContextThemeWrapper,Activity 的继承关系如下:

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

Activity 的基本用法

Activity 的创建

Activity 的创建比较简单,只需新建一个类继承 Activity 类并重写 onCreate() 方法即可,下面是一个 Activity 的列子:

1
2
3
4
5
6
7
8
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

创建和加载布局

通过在 Activity 的 onCreate() 方法中调用 setContentView() 方法来加载布局,setContentView() 方法需要传入布局文件的 id,在上面的的例子中,我们使用 setContentView(R.layout.activity_main) 加载了 activity_main.xml 布局文件

在 Android 开发中,通常使用 XML 布局文件绘制视图,然后由 Activity 引入进来,这样能够很好的分离视图和逻辑。一个较好的习惯是每一个 Activity 都能够对应一个布局文件。常用的布局有 LinearLayoutRelativeLayoutFrameLayout

在 AndroidManifest 文件中注册

所有的 Activity 都必须在 AndroidManifest 文件中进行注册,下面是 Android Studio 为我们自动生成的 AndroidManifest 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activitydemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

可以看到,Activity 的注册是通过在 <application> 标签中使用 <activity> 标签完成的。在 <activity> 标签中,使用 android:name 来指定具体是注册哪一个 Activity

每一个 Android 应用程序有且只有一个主 Activity,主 Activity 是 Android 应用程序的入口。可以通过在 <intent-filter> 标签中使用 <action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/> 指定当前的 Activity 为主 Activity

使用 Intent 启动 Activity

正如前面所说,一个应用通常都会包含至少一个 Activity,那对于多个 Activity 而言,如何从一个 Activity 跳转到另一个 Activity 呢?答案是使用 Intent

Intent 是 Android 程序中各组件之间交互的一种重要方式,它不仅能够指明当前组件想要执行的动作,还能再不同组件之间传递数据。Intent 一般被用于启动 Activity、启动 Service 以及发送广播等

  1. 使用显式 Intent

    所谓的 “显式”,就是明确指明了要启动的 Activity。下面是一个使用显式 Intent 的例子:

    1
    2
    3
    4
    5
    6
    7
    button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    Intent intent = new Intent(MainActivity.this, SecondActivity.class);
    startActivity(intent);
    }
    });

    首先构建一个 Intent 对象,为该 Intent 对象传入 MainActivity.this 作为上下文,传入 SecondActivity.class 作为目标Activity,然后调用 startActivity() 方法执行这个 Intent,这就完成了从 MainActivity 到 SecondActivity 的跳转

  2. 使用隐式 Intent

    相对于显式 Intent,隐式 Intent 并未指明具体的 Activity,而是指定了 actioncategory,然后由系统分析决定要启动哪个 Activity。下面是一个使用隐式 Intent 的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    Intent intent = new Intent("com.example.ACTION_START");
    intent.addCategory("com.example.MY_CATEGORY");
    startActivity(intent);
    }
    });

    与显式 Intent 不同的是,这里的 Intent 对象传入的是一个 action 字符串,并且还调用了 Intent 的 addCategory() 方法,该方法传入的是一个 category 字符串

    既然已经为 Intent 指定了 actioncategory,那还需要修改 SecondActivity,使其能够响应这个 Intent,在 AndroidManifest 文件的 SecondActivity 部分添加相应的 <action><category> 内容,这样就能使用隐式 Intent 完成从 MainActivity 到 SecondActivity 的跳转

    1
    2
    3
    4
    5
    6
    7
    <activity android:name=".SecondActivity">
    <intent-filter>
    <action android:name="com.example.ACTION_START" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="com.example.MY_CATEGORY" />
    </intent-filter>
    </activity>

使用 Intent 传递数据

Intent 提供了多个 putExtra() 方法的重载,通过该方法能够传递数据给下一个 Activity,putExtra() 方法需要传入两个参数,第一个参数是键,用于后面从 Intent 中取值,第二个参数才是真实传递的数据

1
2
3
4
5
6
7
8
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("data", "Hello, SecondActivity!");
startActivity(intent);
}
});

从 Intent 中取值也很简单,只需要通过 getIntent() 获得用于启动的 Intent 对象,由于传递的是字符串,所以使用 getStringExtra() 方法从 Intent 中取值, getStringExtra() 方法需要传入的参数就是之前 putExtra() 方法中的键。

1
2
3
4
5
6
7
8
9
10
11
12
public class SecondActivity extends Activity {
private static final String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent = getIntent();
Log.i(TAG, intent.getStringExtra("data"));
}
}

类似的,如果传递的是整型数据,则需使用 getIntExtra() 方法取值;如果传递的是布尔型数据,则需要使用 getBooleanExtra() 方法取值,以此类推

Activity 的销毁

通常使用返回键就可以销毁当前的 Activity,但如果不想通过这种方式,而是要使用代码来销毁一个 Activity 也是被允许的,只需调用 finish() 方法即可

Activity 生命周期

返回栈

Android 使用栈来保存一系列的 Activity,这个栈被称为 Back Stack,一个 Activity 被启动后入栈,并位于栈顶,当其被销毁时从栈顶出栈,这时前一个入栈的 Activity 就会处于栈顶

Back Stack 管理 Activity 示意图

Activity 的状态

Activity 在其生命周期中存在四种状态:

  • 运行状态

    当一个 Activity 位于屏幕最前端时(即处于栈顶),该 Activity 处于运行状态

  • 暂停状态

    当一个 Activity 失去焦点但依然可见时(例如从该 Activity 中弹出的 Dialog,它只会占用屏幕的一部分空间,此时该 Activity 失去焦点但依然可见),该 Activity 处于暂停状态

  • 停止状态

    当一个 Activity 完全不可见时(即不再处于栈顶),该 Activity 处于停止状态

  • 销毁状态

    当一个 Activity 出栈后(例如按下返回键或者调用 finish() 方法)处于销毁状态

Activity 的生存期

Activity 中定义了 7 个回调方法:

  • onCreate():Activity 首次被创建时调用,通常在此方法中进行初始化操作,例如加载布局、绑定数据等

  • onStart():Activity 由不可见变为可见时调用

  • onResume():Activity 准备好与用户进行交互时调用

  • onPause():系统准备启动或恢复另一个 Activity 时调用

  • onStop():Activity 完全不可见时调用

  • onDestroy():Activity 被销毁之前调用

  • onRestart():Activity 由停止状态变为运行状态之前调用

可以看到,除了 onRestart() 方法之外,其他都是两两相对的,所以还可以将 Activity 的生命周期划分为 3 种生存期

  • 完整生存期

    即从 onCreate() 到 onDestroy(),通常 Activity 在 onCreate() 方法中进行初始化操作,在 onDestroy() 方法中进行释放资源操作

  • 可见生存期

    即从 onStart() 到 onStop(),在此期间 Activity 都是可见的,即便有时候不能与用户进行交互。我们可以在这两个方法中管理对用户可见的资源,例如在 onStart() 方法中加载资源,在 onStop() 方法中释放资源

  • 前台生存期

    即从 onResume() 到 onPause(),在此期间 Activity 都是位于前台并可与用户交互的

为了更好的理解 Activity 的生命周期,Android 为我们提供了 Activity 的生命周期示意图:

Activity 的生命周期示意图

体验 Activity 的生命周期

通过下面的 Demo 可以更加直观的体验 Activity 的生命周期:

创建 MainActivity 用于启动一个普通的 Activity 和 一个对话框形式的 Activity。从 MainActivity 中启动 NormalActivityDialogActivity,NormalActivity 被启动后会覆盖 MainActivity,使其完全不可见,DialogActivity 被启动后只会占据屏幕部分空间,MainActivity 依旧可见

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button start_normal_activity_button = (Button) findViewById(R.id.start_normal_activity_button);
start_normal_activity_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, NormalActivity.class);
startActivity(intent);
}
});
Button start_dialog_activity_button = (Button) findViewById(R.id.start_dialog_activity_button);
start_dialog_activity_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, DialogActivity.class);
startActivity(intent);
}
});
Log.i(TAG, "onCreate()");
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart()");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume()");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause()");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop()");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy()");
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, "onRestart()");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class NormalActivity extends AppCompatActivity {
private static final String TAG = "NormalActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal);
Log.i(TAG, "NormalActivity Started");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class DialogActivity extends AppCompatActivity {
private static final String TAG = "DialogActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dialog);
Log.i(TAG, "DialogActivity Started");
}
}

从 MainActivity 中启动 NormalActivity,然后按返回键返回 MainActivity,最后再按返回键销毁 MainActivity,logcat 打印的信息如下:

2017-06-26_011037.png

从 MainActivity 中启动 DialogActivity,然后按返回键返回 MainActivity,最后再按返回键销毁 MainActivity,logcat 打印的信息如下:

2017-06-26_010532.png

Activity 的启动模式

Activity 共有 4 种启动模式:standardsingleTopsingleTasksingleInstance,可以通过为 AndroidManifest 文件中的 <activity> 标签指定 android:launchMode 属性来选择启动模式

standard

默认情况下 Activity 的启动模式为 standard,这种模式允许有多个相同的 Activity 实例,也就是说,系统并不会在乎返回栈中是否已经存在该 Activity,只要该 Activity 被启动就会入栈位于栈顶

例如:由 Activity1 跳转到 Activity1 再跳转到 Activity1,这时就需要连续按 3 次返回键才能够退出程序

singleTop

如果在返回栈栈顶已经存在 Activity 实例,则再次启动该 Activity 时,系统会直接使用该实例,而不是创建新的 Activity 实例

例如:

由 Activity1 跳转到 Activity2 再跳转到 Activity2,此时实际顺序为 Activity1 —> Activity2

由 Activity1 跳转到 Activity2 再跳转到 Activity1,此时实际顺序为 Activity1 —> Activity2 —> Activity1

singleTask

与 singleTop 相比,singleTask 则更加严格,它不允许返回栈中存在相同的 Activity 实例,使用 singleTask 模式启动的 Activity 在其启动前,系统首先会检查返回栈中是否已经存在该 Activity 实例,如果存在,则直接使用该实例,并将该 Activity 之上的所有 Activity 出栈;如果不存在则创建一个新的实例

singleInstance

与其他启动模式相比,使用 singleInstance 模式启动的 Activity 会启用一个新的返回栈来管理这个 Activity。

Activity 的使用技巧

随时随地退出程序

创建 ActivityCollector 类作为 Activity 管理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<>();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
public static void finishAll() {
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}

创建 BaseActivity 类作为所有 Activity 的父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}

TestActivity 类继承 BaseActivity,在需要退出时调用 ActivityCollector 的 finishAll() 方法即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TestActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
Button button = (Button) findViewById(R.id.buton);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCollector.finishAll();
}
});
}
}