【Android】四大组件之Activity

发布于:2025-05-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

一、什么是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 或组件传递数据或启动新界面。
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/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 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 中添加声明:
<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 跳转

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("key", "value"); // 附加数据
startActivity(intent);

2. 隐式 Intent 跳转

在目标 Activity 的 Manifest 中声明:

<activity android:name=".SecondActivity">
    <intent-filter>
        <action android:name="com.demo.action.ACTION_VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

调用代码:

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() 传递。

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() 返回。
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():

// 在 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() 恢复。

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 保存数据(适合简单配置)

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 保存数据(适合结构化数据)

数据库帮助类:

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);
    }
}

数据操作实现:

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. 标准模式(默认)

<activity android:name=".DetailActivity" />  <!-- 默认无需显式声明 -->

2. 栈顶复用模式

<activity 
    android:name=".NotificationActivity"
    android:launchMode="singleTop" />  <!-- 避免多次点击通知重复创建 -->

3. 任务栈内唯一模式

<activity 
    android:name=".MainActivity"
    android:launchMode="singleTask" />  <!-- 应用主入口 -->

4. 全局单例模式(很少用)

<activity 
    android:name=".CameraActivity"
    android:launchMode="singleInstance"  <!-- 声明为全局单例模式 -->
    android:taskAffinity="com.example.camera.task" />  <!-- 指定独立任务栈(可选) -->

注意:

  • 优先级冲突‌:若同时通过Intent标志(如FLAG_ACTIVITY_NEW_TASK)设置启动模式,Intent标志优先级高于AndroidManifest.xml配置。
  • 任务栈管理‌:singleTasksingleInstance模式会显著影响任务栈结构,需结合实际业务逻辑设计。

当前主流实践推荐:‌核心页面(如主页)使用singleTask,高频复用页面(如通知页)使用singleTop‌,以优化内存和用户体验。

    六、自定义返回行为

    基础自定义返回实现:

    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推荐:

    // 在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模式‌ 

    大型项目、模块化开发 灵活组合、便于团队协作


    网站公告

    今日签到

    点亮在社区的每一天
    去签到