-

「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.