-

「Generated by Manus, instructions issued by binbinwang」

本章将深入探讨Android的Activity组件及其生命周期,并与iOS的UIViewController进行对比。作为Android应用的核心组件,Activity的理解对于从iOS转向Android开发的开发者尤为重要。

3.1 Activity基础

Activity概念与作用

Activity是Android应用的基本构建块,代表用户可以执行单一、专注任务的屏幕。每个Activity都提供一个窗口,通常占满整个屏幕,应用可以在其中绘制UI。

Activity的主要作用:

  1. 提供用户界面
  2. 处理用户交互
  3. 启动其他Activity
  4. 作为应用组件的容器

Activity与iOS UIViewController对比:

特性 Android Activity iOS UIViewController
定义 表示单一屏幕的组件 管理视图层次结构的对象
创建方式 继承Activity或AppCompatActivity 继承UIViewController
视图加载 通过setContentView()设置布局 通过loadView()或storyboard加载
生命周期 复杂的多状态生命周期 相对简单的生命周期
导航 通过Intent启动新Activity 通过present或push显示新控制器

创建与配置Activity

创建Activity:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 基本Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// 初始化视图和事件处理
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
// 处理点击事件
}
}
}

在AndroidManifest.xml中声明Activity:

1
2
3
4
5
6
7
8
9
10
11
12
13
<manifest>
<application>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Activity常用配置属性:

  • android:name:Activity类名
  • android:label:Activity显示的标题
  • android:theme:Activity使用的主题
  • android:launchMode:Activity启动模式
  • android:screenOrientation:屏幕方向
  • android:configChanges:配置变化处理

对比iOS UIViewController的创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 基本UIViewController
@interface MainViewController : UIViewController
@end

@implementation MainViewController

- (void)viewDidLoad {
[super viewDidLoad];

// 初始化视图和事件处理
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:@"Press Me" forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}

- (void)buttonPressed:(UIButton *)sender {
// 处理点击事件
}

@end

主要差异:

  1. Android需要在清单文件中声明Activity,而iOS不需要
  2. Android使用XML布局文件,而iOS传统上使用代码或XIB/Storyboard
  3. Android的Activity需要显式设置内容视图,而iOS的UIViewController自动创建根视图
  4. Android的事件处理使用监听器,而iOS使用目标-动作模式

3.2 Activity生命周期

生命周期回调方法

Android Activity的生命周期比iOS UIViewController更复杂,包含更多状态和回调方法:

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
class LifecycleActivity : AppCompatActivity() {

// 创建Activity时调用
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_lifecycle)
Log.d("Lifecycle", "onCreate")

// 恢复保存的状态
savedInstanceState?.let {
val savedText = it.getString("saved_text")
// 使用恢复的数据
}
}

// Activity变为可见时调用
override fun onStart() {
super.onStart()
Log.d("Lifecycle", "onStart")
}

// Activity获取焦点,可与用户交互时调用
override fun onResume() {
super.onResume()
Log.d("Lifecycle", "onResume")
}

// Activity即将失去焦点时调用
override fun onPause() {
super.onPause()
Log.d("Lifecycle", "onPause")
}

// Activity不再可见时调用
override fun onStop() {
super.onStop()
Log.d("Lifecycle", "onStop")
}

// Activity被销毁前调用
override fun onDestroy() {
super.onDestroy()
Log.d("Lifecycle", "onDestroy")
}

// Activity重新变为可见但未获取焦点时调用
override fun onRestart() {
super.onRestart()
Log.d("Lifecycle", "onRestart")
}

// 保存Activity状态
override fun onSaveInstanceState(outState: Bundle) {
// 保存需要恢复的数据
outState.putString("saved_text", "Some text to save")
super.onSaveInstanceState(outState)
Log.d("Lifecycle", "onSaveInstanceState")
}

// 恢复Activity状态
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
// 注意:也可以在onCreate中恢复状态
val savedText = savedInstanceState.getString("saved_text")
Log.d("Lifecycle", "onRestoreInstanceState")
}
}

生命周期状态与转换

Activity生命周期状态:

  1. **创建(Created)**:Activity实例已创建但尚未显示
  2. **开始(Started)**:Activity可见但不在前台
  3. **恢复(Resumed)**:Activity在前台并可与用户交互
  4. **暂停(Paused)**:Activity部分可见但失去焦点
  5. **停止(Stopped)**:Activity完全不可见
  6. **销毁(Destroyed)**:Activity被系统回收

生命周期转换图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
                    ┌─────────────┐
│ Created │◄───┐
└─────┬───────┘ │
│ │
▼ │
┌─────────┐ ┌─────────────┐ │
│ Destroyed│◄─────┤ Started │◄─────┤
└─────────┘ └─────┬───────┘ │
│ │
▼ │
┌─────────────┐ │
│ Resumed │ │
└─────┬───────┘ │
│ │
▼ │
┌─────────────┐ │
│ Paused ├──────┤
└─────┬───────┘ │
│ │
▼ │
┌─────────────┐ │
│ Stopped ├──────┘
└─────────────┘

与iOS生命周期对比

iOS UIViewController生命周期:

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
@implementation LifecycleViewController

// 视图加载到内存
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"viewDidLoad");
}

// 视图即将出现
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"viewWillAppear");
}

// 视图已经出现
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"viewDidAppear");
}

// 视图即将消失
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSLog(@"viewWillDisappear");
}

// 视图已经消失
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
NSLog(@"viewDidDisappear");
}

// 内存警告
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
NSLog(@"didReceiveMemoryWarning");
}

// 视图被卸载(iOS 6及以上很少调用)
- (void)viewDidUnload {
[super viewDidUnload];
NSLog(@"viewDidUnload");
}

@end

生命周期方法对比:

Android iOS 说明
onCreate viewDidLoad 初始化和设置视图
onStart viewWillAppear 视图即将可见
onResume viewDidAppear 视图完全可见并可交互
onPause viewWillDisappear 视图即将隐藏
onStop viewDidDisappear 视图完全隐藏
onDestroy dealloc 对象被销毁
onSaveInstanceState encodeRestorableState 保存状态
onRestoreInstanceState decodeRestorableState 恢复状态

主要差异:

  1. Android的生命周期更复杂,状态更多
  2. iOS没有直接对应Android的onRestart的方法
  3. Android通过Bundle保存状态,iOS通过NSCoding或State Restoration
  4. Android在配置变化(如旋转)时会重建Activity,而iOS默认不会重建UIViewController
  5. Android明确区分前台和后台状态,iOS通过AppDelegate处理应用级生命周期

3.3 Activity间的通信与导航

Intent系统

Intent是Android组件间通信的核心机制,用于启动Activity、Service等组件。

Intent类型:

  1. 显式Intent:明确指定目标组件
  2. 隐式Intent:指定操作类型,由系统匹配合适的组件

使用Intent启动Activity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 显式Intent
val intent = Intent(this, SecondActivity::class.java)
// 添加数据
intent.putExtra("key_name", "value")
intent.putExtra("key_age", 25)
// 启动Activity
startActivity(intent)

// 隐式Intent
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com"))
startActivity(browserIntent)

// 启动Activity并期望结果
val pickContactIntent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST)

在目标Activity中接收数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)

// 获取传递的数据
val extras = intent.extras
if (extras != null) {
val name = extras.getString("key_name")
val age = extras.getInt("key_age")
// 使用数据
}
}
}

处理Activity结果:

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
// 旧方式
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == Activity.RESULT_OK) {
// 处理返回的数据
data?.data?.let { contactUri ->
// 使用contactUri
}
}
}
}

// 新方式(使用ActivityResultLauncher)
private val pickContactLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let { contactUri ->
// 使用contactUri
}
}
}

// 使用新方式启动
pickContactLauncher.launch(Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI))

与iOS导航模式对比

iOS的导航方式:

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
// 模态展示
UIViewController *secondVC = [[SecondViewController alloc] init];
secondVC.name = @"value";
secondVC.age = 25;
[self presentViewController:secondVC animated:YES completion:nil];

// 使用导航控制器
SecondViewController *secondVC = [[SecondViewController alloc] init];
secondVC.name = @"value";
secondVC.age = 25;
[self.navigationController pushViewController:secondVC animated:YES];

// 使用Segue(Storyboard)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"ShowSecondVC"]) {
SecondViewController *secondVC = segue.destinationViewController;
secondVC.name = @"value";
secondVC.age = 25;
}
}

// 获取结果(使用委托模式)
@protocol ContactPickerDelegate <NSObject>
- (void)contactPicker:(ContactPickerViewController *)picker didPickContact:(Contact *)contact;
@end

// 在目标控制器中
- (void)finishPickingContact:(Contact *)contact {
if ([self.delegate respondsToSelector:@selector(contactPicker:didPickContact:)]) {
[self.delegate contactPicker:self didPickContact:contact];
}
[self dismissViewControllerAnimated:YES completion:nil];
}

主要差异:

  1. Android使用Intent系统进行导航,而iOS使用直接引用或Segue
  2. Android可以通过隐式Intent调用其他应用的组件,iOS通过URL Scheme或Universal Links
  3. Android使用Bundle传递数据,iOS直接设置属性或使用初始化方法
  4. Android使用startActivityForResult获取结果,iOS通常使用委托模式
  5. Android的Intent系统更灵活,支持更多导航场景,而iOS的导航更直接

3.4 Activity启动模式与任务栈

启动模式

Android提供四种Activity启动模式,控制Activity实例在任务栈中的行为:

  1. standard(标准模式):默认模式,每次启动都创建新实例
  2. singleTop(栈顶复用):如果目标Activity已在栈顶,则复用该实例
  3. singleTask(栈内复用):如果目标Activity已在任务栈中,则复用该实例并清除其上的所有Activity
  4. singleInstance(单实例):目标Activity独占一个任务栈

在AndroidManifest.xml中设置启动模式:

1
2
3
<activity
android:name=".SingleTopActivity"
android:launchMode="singleTop" />

通过Intent标志设置启动行为:

1
2
3
val intent = Intent(this, SecondActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
startActivity(intent)

任务栈管理

Android使用任务栈(Task)管理Activity,遵循后进先出(LIFO)原则。

任务栈相关Intent标志:

  • FLAG_ACTIVITY_NEW_TASK:在新任务中启动Activity
  • FLAG_ACTIVITY_CLEAR_TOP:清除目标Activity上面的所有Activity
  • FLAG_ACTIVITY_CLEAR_TASK:清除目标任务中的所有Activity
  • FLAG_ACTIVITY_SINGLE_TOP:等同于singleTop启动模式
  • FLAG_ACTIVITY_REORDER_TO_FRONT:将已存在的Activity实例移到栈顶

任务亲和性(taskAffinity):
指定Activity倾向于归属的任务栈,默认为应用包名。

1
2
3
<activity
android:name=".DifferentTaskActivity"
android:taskAffinity="com.example.different" />

与iOS导航栈对比

iOS的导航栈管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 导航控制器管理视图控制器栈
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:rootVC];

// 压入新控制器
[navController pushViewController:newVC animated:YES];

// 弹出控制器
[navController popViewControllerAnimated:YES];

// 弹出到根控制器
[navController popToRootViewControllerAnimated:YES];

// 弹出到特定控制器
[navController popToViewController:specificVC animated:YES];

// 替换整个栈
[navController setViewControllers:@[rootVC, secondVC, thirdVC] animated:YES];

主要差异:

  1. Android使用系统级的任务栈管理Activity,而iOS使用UINavigationController管理视图控制器栈
  2. Android的启动模式提供更灵活的实例管理,而iOS主要依赖导航控制器的API
  3. Android可以通过taskAffinity将Activity分配到不同任务栈,iOS没有直接等价物
  4. Android的任务栈可以跨应用,而iOS的导航栈通常限于单个应用
  5. iOS的导航栈更简单直观,而Android的任务栈概念更复杂但更灵活

3.5 Activity状态保存与恢复

状态保存机制

Android在配置变化(如屏幕旋转)或系统内存不足时可能会销毁并重建Activity。为保持用户体验,需要保存和恢复Activity状态。

使用onSaveInstanceState保存状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
override fun onSaveInstanceState(outState: Bundle) {
// 保存UI状态
val userInput = editText.text.toString()
outState.putString("user_input", userInput)

val scrollPosition = scrollView.scrollY
outState.putInt("scroll_position", scrollPosition)

// 保存复杂对象(必须实现Parcelable)
val user = User("John", 30)
outState.putParcelable("user_object", user)

super.onSaveInstanceState(outState)
}

恢复保存的状态:

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
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// 恢复状态
savedInstanceState?.let {
val userInput = it.getString("user_input")
editText.setText(userInput)

val scrollPosition = it.getInt("scroll_position")
scrollView.post { scrollView.scrollTo(0, scrollPosition) }

val user = it.getParcelable<User>("user_object")
// 使用恢复的user对象
}
}

// 或者在单独的回调中恢复
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)

// 恢复状态(与onCreate中的代码类似)
val userInput = savedInstanceState.getString("user_input")
editText.setText(userInput)
// ...
}

ViewModel与状态保存

Android Jetpack提供了ViewModel组件,帮助在配置变化时保留数据:

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
// 定义ViewModel
class MainViewModel : ViewModel() {
// 数据在配置变化时保留
val userData = MutableLiveData<User>()

// 加载数据
fun loadUser(userId: String) {
// 异步加载用户数据
viewModelScope.launch {
val user = repository.getUser(userId)
userData.value = user
}
}

// ViewModel被清除时调用
override fun onCleared() {
super.onCleared()
// 清理资源
}
}

// 在Activity中使用ViewModel
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MainViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// 获取ViewModel实例
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

// 观察数据变化
viewModel.userData.observe(this) { user ->
// 更新UI
userNameTextView.text = user.name
}

// 加载数据(只在首次创建时调用,配置变化时不会重复调用)
if (savedInstanceState == null) {
viewModel.loadUser("user_123")
}
}
}

与iOS状态保存对比

iOS的状态保存机制:

```
// 使用NSCoding保存状态

  • (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
    [super encodeRestorableStateWithCoder:coder]; // 保存UI状态
    NSString *userInput = self.textField.text;
    [coder encodeObject:userInput forKey:@”user_input”]; CGPoint scrollPosition = self.scrollView.contentOffset;
    [coder encodeCGPoint:scrollPosition forKey:@”scroll_position”]; // 保存复杂对象(必须符合NSCoding协议)
    User *user = [[User alloc] initWithName:@”John” age:30];
    [coder encodeObject:user forKey:@”user_object”];
    }

// 恢复保存的状态

  • (void)decodeRestorableStateWithCoder:(NSCoder *)coder {
    [super decodeRestorableStateWithCoder:coder]; // 恢复UI状态
    NSString *userInput = [coder decodeObjectForKey:@”user_input”];
    self.textField.text = userInput; CGPoint scrollPosition = [coTo save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with grep -n in order to find the line numbers of what you are looking for.