参考资料:

  • 《疯狂 Android 讲义》
  • 《Android 编程权威指南》

ActivityManager

一个 activity 启动另一个 activity 最简单的方式是使用以下 Activity 方法:

1
public void startActivity(Intent intent)

activity 调用 startActivity(...) 方法时,该方法调用请求发送给操作系统的 ActivityManager。ActivityManager 负责创建 Activity 实例并调用其 onCreate(...) 方法。activity 的启动示意图如下图所示。

ActivityManager 如何知道该启动哪一个 Activity 呢?答案就在于传入 startActivity(...) 方法的 Intent 参数。

Intent

intent 对象是 component 用来与操作系统通信的一种媒介工具。

显式 Intent

显式 Intent 明确指定需要启动或者触发的组件的类名。对于显式 Intent 而言,Android 系统无需对该 Intent 做任何解析,系统直接找到指定的目标组件,启动或触发它即可。

一个构造方法示例:

1
public Intent(Context packageContext, Class<?> cls)

传入该方法的 Class 对象指定 ActivityManager 应该启动的 Activity;Context 对象告知 ActivityManager 在哪一个包里可以找到 Class 对象。示例:

1
2
Intent i = new Intent(QuizActivity.this, CheatActivity.class);
startActivity(i);

在启动 activity 之前,ActivityManager 会检查确认指定的 Class 是否已在配置文件中声明。如已完成声明,则启动 activity ,应用正常运行。反之,则抛出 ActivityNotFoundException 异常。这就是为什么要在 manifest 配置文件中声明应用全部 activity 的原因所在。

Activity 间通信

父 Activity 传给子 Activity

Activity 之间的通讯可以通过 extra 信息附加在传入 startActivity(Intent) 方法的 Intent 上发送出去。extra 信息可以是任意数据,它包含在 Intent 中,由启动方 activity 发送出去。接收方收到操作系统转发的 intent 后,访问并获取包含在其中的 extra 数据信息。extra 也同样是一种 key-value 结构。

1. 将 extra 信息添加到 intent

将 extra 数据信息添加到 intent ,我们需要调用 Intent.putExtra(...) 方法。确切的说,是调用如下方法:

1
public Intent putExtra(String name, boolean value)

Intent.putExtra(...) 方法有多种形式。不变的是,它总有两个参数。一个参数是固定为 String 类型的 key ,另一个参数值可以是多种数据类型。示例:

1
2
3
4
5
6
public class CheatActivity extends Activity {
public static final String EXTRA_ANSWER_IS_TRUE =
"com.bignerdranch.android.geoquiz.answer_is_true";
...

将 extra 附加到 intent 上:

1
2
3
4
5
6
7
8
9
10
...
mCheatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this, CheatActivity.class);
boolean answerIsTrue = mQuestionBank[mCurrentIndex].isTrueQuestion();
i.putExtra(CheatActivity.EXTRA_ANSWER_IS_TRUE, answerIsTrue);
startActivity(i);
}
}

2. 从 extra 获取数据

要从 extra 获取数据,会用到如下方法:

1
public boolean getBooleanExtra(String name, boolean defaultValue)

getBooleanExtra(...) 方法的第一个参数是 extra 的名字,第二个参数是指定默认值(默认答案),它在无法获得有效 key 值时使用。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
public class CheatActivity extends Activity {
...
private boolean mAnswerIsTrue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat);
mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);
}

子 Activity 回传数据给父 Activity

若需要从子 activity 获取返回信息时,可调用以下 Activity 方法取代 startActivity 方法:

1
public void startActivityForResult(Intent intent, int requestCode)

该方法的第一个参数同前述的 intent 。第二个参数是 请求代码 。请求代码是先发送给子 activity ,然后再返回给父 activity 的用户定义整数值。当一个 activity 启动多个不同类型的子 activity ,且需要判断区分消息回馈方时,我们通常会用到该请求代码。

示例:

1
2
3
4
5
6
7
8
9
10
mCheatButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
// Start CheatActivity
Intent i = new Intent(QuizActivity.this, CheatActivity.class);
boolean answerIsTrue = mQuestionBank[mCurrentIndex].isTrueQuestion();
i.putExtra(CheatActivity.EXTRA_ANSWER_IS_TRUE, answerIsTrue);
startActivityForResult(i, 0);
}
});

上例只启动一个类型的子 activity ,因此只传入 0 作为请求代码参数。

1. 设置返回结果
1. 子 Activity 返还 intent

在子 Activity 代码中,为 extra 增加常量 key ,再创建一个私有方法,用来创建 intent ,附加 extra 并设置结果值。然后在要返回消息的代码(如按钮的监听器代码)中调用 Activity.setResult(int, Intent) 方法。

有以下两种 setResult 方法:

1
2
public final void setResult(int resultCode)
public final void setResult(int resultCode, Intent data)

通常来说,参数 resultCode 是 结果代码 ,可以是以下两个预定义常量中的任何一个:

  • Activity.RESULT_OK;
  • Activity.RESULT_CANCELED

(如需自己定义结果代码,还可使用另一个常量:RESULT_FIRST_USER)

子 activity 可以不调用 setResult(...) 方法。如不需要区分附加在 intent 上的结果或其他信息,可让操作系统发送默认的结果代码。如果子 activity 是以调用 startActivityForResult(...) 方法启动的,结果代码则总是会返回给父activity。在没有调用 setResult(...) 方法的情况下,如果用户单击了后退按钮,父 activity 则会收到 Activity.RESULT_CANCELED 的结果代码。

示例代码:

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
public class CheatActivity extends Activity {
public static final String EXTRA_ANSWER_IS_TRUE =
"com.example.hahack.geoquiz.answer_is_true";
public static final String EXTRA_ANSWER_SHOWN =
"com.example.hahack.geoquiz.answer_shown";
...
private void setAnswerShownResult(boolean isAnswerShown) {
Intent data = new Intent();
data.putExtra(EXTRA_ANSWER_SHOWN, isAnswerShown);
setResult(RESULT_OK, data);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Answer will not be shown until the user
// presses the button
setAnswerShownResult(false);
mShowAnswerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAnswerIsTrue) {
mAnswerTextView.setText(R.string.true_button);
} else {
mAnswerTextView.setText(R.string.false_button);
}
setAnswerShownResult(true);
}
});
}
}

用户单击 Show Answer 按钮时,CheatActivity 调用 setResult(int, Intent) 方法将结果代码以及 intent 打包。

然后,在用户单击后退键回到 QuizActivity 时,ActivityManager 调用父 activity 的以下方法

1
protected void onActivityResult(int requestCode, int resultCode, Intent data)

该方法的参数来自于 QuizActivity 的原始请求代码以及传入 SetResult(...) 方法的结果代码和 intent 。

2. 父 Activity 处理返回结果

在父 activity 中,可以覆盖 onActivityResult(...) 方法获取从子 activity 回传的值。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class QuizActivity extends ActionBarActivity {
...
private boolean mIsCheater;
...
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data == null) {
return;
}
mIsCheater = data.getBooleanExtra(CheatActivity.EXTRA_ANSWER_SHOWN, false);
}
...

隐式 Intent

隐式 Intent 只是指定需要启动或者触发的组件应满足怎样的条件。对于隐式 Intent 而言,Android 系统需要对该 Intent 进行解析,解析出它的条件(通过 IntentFilter),然后再去系统中查找与之匹配的目标组件。如果找到符合条件的组件,就启动或触发它们。