目录
[三、Activity 跳转与数据传递](#三、Activity 跳转与数据传递)
[五、Activity 启动模式](#五、Activity 启动模式)
你可以把Activity想象成手机屏幕上的一个"页面"。比如,当你打开一个App时,看到的第一个界面就是一个Activity;点击某个按钮跳转到另一个界面,那就是另一个Activity。每个Activity就是一个独立的"屏幕",负责展示内容和与用户交互。
一、什么是Activity
Activity 是 Android 应用的核心交互组件。
1. 单屏交互容器
- 每个 Activity 对应一个独立的用户界面(UI)屏幕;
- 此界面承载用户可见的视图控件,如按钮、文本框等;
- 用户可在此界面进行交互操作,如点击、输入等。
- 应用通常包含多个 Activity,通过跳转实现不同功能界面的切换。
2. 生命周期管理
onCreate():Activity被创建时调用。通常会在这里初始化界面和变量,这时我们看到的是一片空白。
onStart():Activity即将可见时调用。此后页面可见,但用户还不能跟页面进行互动。
onResume():Activity获得焦点,用户可以与之交互时调用。
onPause():Activity失去焦点时调用。比如,用户按了Home键回到桌面,或者跳转到另一个页面,但页面还没有完全不可见。
onStop():Activity不再可见时调用。比如,你点击文章详情页跳转到了文章里面。首页面被完全覆盖。
onRestart():Activity从停止状态重新启动时调用。首页Activity从后台回到前台,准备重新显示。
onDestroy():Activity被销毁时调用。比如,用户关闭了页面
3. 跨组件通信
- 使用 Intent 与其他 Activity 或组件传递数据或启动新界面。
java
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("key", "value");
startActivity(intent);
4. 关键功能
- 用户事件处理:监听触摸、按键等操作,响应交互逻辑。
- 界面动态更新:根据业务需求更新 UI 元素(如列表数据刷新)。
- 资源管理 :在
onDestroy()
中释放数据库连接、网络请求等资源,避免内存泄漏。
5. 门店与后厨模型
- Activity 类似"门店"(直接面向用户),负责展示和接收指令;
- Service 类似"后厨"(后台处理任务),通过 Intent("订单")传递需求。
二、如何创建和配置Activity
1. 手动创建 Activity
- 在 Android Studio 中,右击包名 (如java/com/demo)→ New → Activity → Empty Activity,输入名称(如
MainActivity
),取消勾选自动生成布局文件和主 Activity 选项。 - 自动生成的类需继承
AppCompatActivity
,并重写onCreate()
方法:
java
// java/com/demo/MainActivity.java
package com.demo
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 初始化组件和布局
}
@Override
protected void onStart() {
// Activity 可见但未获取焦点
}
@Override
protected void onResume() {
// 恢复交互,如重启动画
}
@Override
protected void onPause() {
// 暂停耗时操作,保存临时数据
}
@Override
protected void onStop() {
// 释放非必要资源
}
@Override
protected void onDestroy() {
// 清理线程、关闭数据库连接和网络请求、释放资源,避免内存泄漏
}
}
2. 配置布局文件
- 在
res/layout
目录新建 XML 文件(如activity_main.xml
),定义 UI 元素。 - 在 Activity 中通过
setContentView(R.layout.activity_main)
加载布局。
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<!-- 顶部标题栏 -->
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center"
android:text="用户登录"
android:textSize="24sp"
android:background="#3F51B5"
android:textColor="#FFFFFF"/>
<!-- 输入区域 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入用户名"
android:inputType="text"/>
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:hint="请输入密码"
android:inputType="textPassword"/>
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="24dp"
android:text="立即登录"
android:onClick="onLoginClick"
android:backgroundTint="#2196F3"
android:textColor="#FFFFFF"/>
</LinearLayout>
<!-- 底部操作区域 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="注册账号"
android:textColor="#757575"/>
<View
android:layout_width="1dp"
android:layout_height="16dp"
android:layout_marginHorizontal="12dp"
android:background="#BDBDBD"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="忘记密码"
android:textColor="#757575"/>
</LinearLayout>
</LinearLayout>
3. 注册 Activity
- 在
AndroidManifest.xml
中添加声明:
XML
<activity
android:name=".MainActivity"
android:label="主界面">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
三、Activity 跳转与数据传递
1. 显式 Intent 跳转
java
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("key", "value"); // 附加数据
startActivity(intent);
2. 隐式 Intent 跳转
在目标 Activity 的 Manifest 中声明:
XML
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.demo.action.ACTION_VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
调用代码:
java
Intent intent = new Intent("com.demo.action.ACTION_VIEW");
startActivity(intent);
// 或者
Intent intent = new Intent();
intent.setAction("com.demo.action.ACTION_VIEW");
startActivity(intent);
无需在代码中显式导入目标 Activity 的包名或类。
注意事项:
- 必须包含 DEFAULT category :隐式 Intent 的接收 Activity 需在
<intent-filter>
中声明android.intent.category.DEFAULT
,否则会触发ActivityNotFoundException
。 - 自定义 action 命名规范 :如
com.demo.action.ACTION_VIEW
,避免与其他应用冲突。 - 多应用匹配处理 :若多个应用声明相同
action
,系统会弹出选择器让用户选择目标应用。
3. 返回数据
使用 startActivityForResult()
启动 Activity,并在 onActivityResult()
处理返回数据。
- MainActivity发送数据并启动新的SecondActivity。输入参数通过
Intent.putExtra()
传递。
java
public class MainActivity extends AppCompatActivity {
// 自定义请求标识符,用于区分不同Activity的返回结果
private static final int REQUEST_CODE = 1001;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnOpen = findViewById(R.id.btn_open);
btnOpen.setOnClickListener(v -> {
// 1. 创建显式 Intent
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
// 2. 传递输入参数
intent.putExtra("username", "admin");
intent.putExtra("max_tries", 3);
// 3. 启动并等待返回结果
startActivityForResult(intent, REQUEST_CODE);
});
}
// 4. 接收返回结果的回调方法
@Override
protected void onActivityResult(int requestCode,
int resultCode,
@Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) { // 匹配SecondActivity的返回结果
if (resultCode == RESULT_OK && data != null) {
// 5. 解析返回数据
String result = data.getStringExtra("result_key");
int score = data.getIntExtra("score", 0);
// 6. 更新UI(示例:显示结果)
TextView tvResult = findViewById(R.id.tv_result);
tvResult.setText("结果: " + result + " 得分: " + score);
} else {
Toast.makeText(this, "用户取消操作", Toast.LENGTH_SHORT).show();
}
}
}
}
- SecondActivity接收数据并返回结果。返回数据通过
setResult()
返回。
java
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
// 1. 接收输入参数
Bundle extras = getIntent().getExtras();
if (extras != null) {
String username = extras.getString("username");
int maxTries = extras.getInt("max_tries", 1);
Log.d("DEBUG", "用户名: " + username + " 最大尝试次数: " + maxTries);
}
Button btnConfirm = findViewById(R.id.btn_confirm);
btnConfirm.setOnClickListener(v -> {
// 2. 创建返回数据的 Intent
Intent resultIntent = new Intent();
resultIntent.putExtra("result_key", "操作成功");
resultIntent.putExtra("score", 85);
// 3. 设置结果码并结束当前 Activity
setResult(RESULT_OK, resultIntent);
finish();
});
Button btnCancel = findViewById(R.id.btn_cancel);
btnCancel.setOnClickListener(v -> {
// 4. 用户取消操作的处理
setResult(RESULT_CANCELED);
finish();
});
}
}
结果码:
RESULT_OK
:操作成功完成RESULT_CANCELED
:用户取消操作- 也可自定义数值(需使用
Activity.RESULT_FIRST_USER
+ N 格式)
4. 新版 Activity Result API
Google 推荐使用 ActivityResultContracts
替代传统方式startActivityForResult():
java
// 在 Activity/Fragment 中初始化
ActivityResultLauncher<Intent> launcher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK) {
Intent data = result.getData();
// 处理返回数据
}
}
);
// 启动 Activity
launcher.launch(new Intent(this, SecondActivity.class));
四、数据保存与恢复
1. 临时数据保存
屏幕旋转等场景,需通过 onSaveInstanceState()
保存数据,并在重建时通过 onCreate()
或 onRestoreInstanceState()
恢复。
java
public class MainActivity extends AppCompatActivity {
private static final String KEY_COUNTER = "counter";
private static final String KEY_TEXT = "user_input";
private static final String KEY_USER = "user_object";
private int mCounter = 0;
private EditText mEditText;
private User mUser; // 假设 User 类实现了 Parcelable
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = findViewById(R.id.et_input);
// 方式1:通过 onCreate 恢复(推荐)
if (savedInstanceState != null) {
mCounter = savedInstanceState.getInt(KEY_COUNTER, 0);
mEditText.setText(savedInstanceState.getString(KEY_TEXT));
mUser = savedInstanceState.getParcelable(KEY_USER);
Log.d("RESTORE", "通过onCreate恢复数据");
}
}
// 方式2:通过 onRestoreInstanceState 恢复(可选)
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 此处的 Bundle 一定非空,无需判空
String tempText = savedInstanceState.getString(KEY_TEXT);
if (!TextUtils.isEmpty(tempText)) {
mEditText.setText(tempText);
}
Log.d("RESTORE", "通过onRestoreInstanceState恢复数据");
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
// 保存基本类型
outState.putInt(KEY_COUNTER, mCounter);
// 保存用户输入
outState.putString(KEY_TEXT, mEditText.getText().toString());
// 保存自定义对象(需实现 Parcelable)
if (mUser != null) {
outState.putParcelable(KEY_USER, mUser);
}
Log.d("SAVE", "数据已保存");
}
// 示例按钮点击事件
public void incrementCounter(View view) {
mCounter++;
TextView tvCounter = findViewById(R.id.tv_counter);
tvCounter.setText(String.valueOf(mCounter));
}
}
2. 持久化数据
建议在 onPause()
中执行保存操作。
特性 | SharedPreferences | SQLite |
---|---|---|
数据类型 | 简单键值对(基本类型、字符串) | 结构化数据(支持复杂查询) |
存储容量 | 适合小数据(KB级) | 适合大数据(MB级) |
查询能力 | 无 | 支持SQL查询、事务、索引 |
适用场景 | 用户设置、临时状态 | 用户生成内容、历史记录 |
性能表现 | 读写速度快 | 写操作较慢(需事务优化) |
a. 使用 SharedPreferences 保存数据(适合简单配置)
java
public class MainActivity extends AppCompatActivity {
private static final String PREFS_NAME = "MyPrefs";
private static final String KEY_USERNAME = "username";
private static final String KEY_REMEMBER_ME = "remember_me";
private EditText etUsername;
private CheckBox cbRememberMe;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etUsername = findViewById(R.id.et_username);
cbRememberMe = findViewById(R.id.cb_remember);
// 从 SharedPreferences 加载数据
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
etUsername.setText(prefs.getString(KEY_USERNAME, ""));
cbRememberMe.setChecked(prefs.getBoolean(KEY_REMEMBER_ME, false));
}
@Override
protected void onPause() {
super.onPause();
// 保存数据到 SharedPreferences
SharedPreferences.Editor editor
= getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit();
editor.putString(KEY_USERNAME, etUsername.getText().toString());
editor.putBoolean(KEY_REMEMBER_ME, cbRememberMe.isChecked());
editor.apply(); // 使用异步提交避免阻塞
}
}
b. 使用 SQLite 保存数据(适合结构化数据)
数据库帮助类:
java
public class UserDbHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "UserDatabase.db";
private static final int DATABASE_VERSION = 1;
// 用户表结构
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + UserContract.UserEntry.TABLE_NAME + " (" +
UserContract.UserEntry._ID + " INTEGER PRIMARY KEY," +
UserContract.UserEntry.COLUMN_NAME + " TEXT," +
UserContract.UserEntry.COLUMN_EMAIL + " TEXT)";
public UserDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_ENTRIES);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 简单示例直接删除旧表
db.execSQL("DROP TABLE IF EXISTS " + UserContract.UserEntry.TABLE_NAME);
onCreate(db);
}
}
数据操作实现:
java
public class MainActivity extends AppCompatActivity {
private EditText etName, etEmail;
private UserDbHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etName = findViewById(R.id.et_name);
etEmail = findViewById(R.id.et_email);
dbHelper = new UserDbHelper(this);
loadDataFromDatabase();
}
private void loadDataFromDatabase() {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.query(
UserContract.UserEntry.TABLE_NAME,
null, null, null, null, null, null
);
if (cursor.moveToFirst()) {
etName.setText(cursor.getString(
cursor.getColumnIndex(UserContract.UserEntry.COLUMN_NAME)));
etEmail.setText(cursor.getString(
cursor.getColumnIndex(UserContract.UserEntry.COLUMN_EMAIL)));
}
cursor.close();
}
@Override
protected void onPause() {
super.onPause();
saveToDatabase();
}
private void saveToDatabase() {
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 先清空旧数据(根据业务需求决定是否保留历史)
db.delete(UserContract.UserEntry.TABLE_NAME, null, null);
ContentValues values = new ContentValues();
values.put(UserContract.UserEntry.COLUMN_NAME,
etName.getText().toString());
values.put(UserContract.UserEntry.COLUMN_EMAIL,
etEmail.getText().toString());
db.insert(UserContract.UserEntry.TABLE_NAME, null, values);
}
@Override
protected void onDestroy() {
dbHelper.close(); // 必须关闭数据库连接
super.onDestroy();
}
}
五、Activity 启动模式
模式 | 行为描述 | 典型场景 |
---|---|---|
standard |
默认模式,每次启动创建新实例入栈,即使已存在相同Activity。 | 普通页面跳转,如列表→详情 |
singleTop |
若目标Activity在栈顶,直接复用,调用onNewIntent()。 |
避免重复推送,如通知栏点击 |
singleTask |
若栈中存在目标Activity实例,清空其上方所有实例并移至栈顶;否则,新建实例。 | 应用主页(保证唯一性) |
singleInstance |
独占新任务栈,全局唯一实例;其他应用调用时直接复用。 | 独立功能模块,如系统相机、系统拨号界面 |
1. 标准模式(默认)
XML
<activity android:name=".DetailActivity" /> <!-- 默认无需显式声明 -->
2. 栈顶复用模式
XML
<activity
android:name=".NotificationActivity"
android:launchMode="singleTop" /> <!-- 避免多次点击通知重复创建 -->
3. 任务栈内唯一模式
XML
<activity
android:name=".MainActivity"
android:launchMode="singleTask" /> <!-- 应用主入口 -->
4. 全局单例模式(很少用)
XML
<activity
android:name=".CameraActivity"
android:launchMode="singleInstance" <!-- 声明为全局单例模式 -->
android:taskAffinity="com.example.camera.task" /> <!-- 指定独立任务栈(可选) -->
注意:
- 优先级冲突 :若同时通过
Intent
标志(如FLAG_ACTIVITY_NEW_TASK
)设置启动模式,Intent
标志优先级高于AndroidManifest.xml
配置。 - 任务栈管理 :
singleTask
和singleInstance
模式会显著影响任务栈结构,需结合实际业务逻辑设计。
当前主流实践推荐:**核心页面(如主页)使用
singleTask
,高频复用页面(如通知页)使用singleTop
**,以优化内存和用户体验。
六、自定义返回行为
基础自定义返回实现:
java
public class MainActivity extends AppCompatActivity {
private long backPressedTime = 0;
@Override
public void onBackPressed() {
// 场景1:双击返回退出应用
if (backPressedTime + 2000 > System.currentTimeMillis()) {
super.onBackPressed(); // 执行默认返回
finishAffinity(); // 关闭所有关联Activity
} else {
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();
}
backPressedTime = System.currentTimeMillis();
// 场景2:Fragment返回栈处理
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed(); // 必须调用父类方法
}
}
}
Android X推荐:
java
// 在Activity或Fragment中使用
private OnBackPressedCallback callback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
// 自定义返回逻辑
if (shouldInterceptBack()) {
showExitConfirmation();
} else {
setEnabled(false); // 禁用当前回调
requireActivity().onBackPressed(); // 触发系统返回
}
}
};
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 注册返回回调(推荐在Fragment中使用)
requireActivity().getOnBackPressedDispatcher()
.addCallback(this, callback);
}
七、复杂界面布局
1. Activity和Fragment
组件 | Activity | Fragment |
---|---|---|
核心生命周期方法 | onCreate() onStart() onResume() onPause() onStop() onDestroy() |
包含所有Activity方法,额外增加: onAttach() onCreateView() onDestroyView() onDetach() |
特有方法 | 无 | onAttach() (绑定宿主Activity) onCreateView() (创建UI视图) onDetach() (解绑宿主Activity) |
独立性 | 独立组件,可直接运行 | 依附于宿主Activity,不可独立存在 |
组件定位 | 系统级交互单元(处理权限、窗口管理等) | UI模块化组件(实现跨Activity界面复用与动态组合) |
2. 典型架构
架构模式 | 适用场景 | 优势 |
---|---|---|
单 Activity 架构 | 复杂导航流程、深度链接 | 统一管理导航、更好的状态恢复 |
多 Activity 架构 | 独立功能模块、不同任务栈需求 | 明确职责划分、方便权限管理 |
混合架构: 单Activity+多Fragment模式 多模块Activity+多Fragment模式 | 大型项目、模块化开发 | 灵活组合、便于团队协作 |