Android学习笔记-2-Activity生命周期&数据存储

41 阅读10分钟

Android学习笔记:Activity生命周期&数据存储

Activity生命周期&数据存储.png

1. Activity生命周期核心概念

为什么生命周期如此重要?
它决定了应用在不同状态下的行为,是处理内存管理、数据保存、UI更新的关键。理解它,你就能写出不崩溃、不丢失数据的Android应用。

stateDiagram-v2
    [*] --> Created
    Created --> Started
    Started --> Resumed
    Resumed --> Paused
    Paused --> Stopped
    Stopped --> Destroyed
    Paused --> Resumed
    Stopped --> Started
    Resumed --> Stopped
    Stopped --> Destroyed
    Paused --> Destroyed

关键状态说明

状态说明用户可见为什么重要
CreatedActivity创建完成初始化UI、数据
StartedActivity可见但未获得焦点准备显示内容
ResumedActivity获得焦点,用户可交互核心交互状态
PausedActivity失去焦点但可见(如弹出对话框)保存关键数据
StoppedActivity完全不可见(如进入后台)释放非关键资源
DestroyedActivity被销毁释放所有资源

💡 关键洞察
用户不会等待应用完成生命周期,必须在onPause()保存关键数据,否则会丢失!


2. 生命周期方法详解

每个方法都是一个"关键时刻",必须正确处理

方法调用时机做什么为什么
onCreate()Activity首次创建初始化UI、数据必须
onStart()Activity可见但未获得焦点无需操作(通常)通常无需处理
onResume()Activity获得焦点,用户可交互重启动画、恢复数据关键交互点
onPause()失去焦点(如弹出对话框)保存关键数据、暂停动画防止数据丢失
onStop()完全不可见(进入后台)释放非关键资源优化内存
onDestroy()Activity被销毁释放所有资源必须

⚠️ 新手常见错误

// 错误:在onPause()中保存数据,但未在onResume()恢复
@Override
protected void onPause() {
 // 保存数据
 SharedPreferences.Editor editor = prefs.edit();
 editor.putString("text", textView.getText().toString());
 editor.apply();
}

// 未在onResume()恢复数据!

3. 实战:生命周期日志监控

步骤1:修改MainActivity
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "ActivityLifecycle";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate()");
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart()");
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume()");
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause()");
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop()");
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy()");
    }
}
步骤2:运行并观察Logcat
  1. 运行应用
  2. 点击Home键(进入后台)
  3. 从最近任务列表重新打开应用
  4. 观察Logcat输出

典型输出

D/ActivityLifecycle: onCreate()
D/ActivityLifecycle: onStart()
D/ActivityLifecycle: onResume()
// 点击Home键
D/ActivityLifecycle: onPause()
D/ActivityLifecycle: onStop()
// 重新打开应用
D/ActivityLifecycle: onStart()
D/ActivityLifecycle: onResume()

💡 关键发现
每次从后台回来,都会触发onStart()onResume(),但不会重新调用onCreate()。这就是为什么数据保存在onPause()而非onCreate()


4. 生命周期实战场景

场景1:保存用户输入
// 在onPause()中保存数据
@Override
protected void onPause() {
    super.onPause();
    
    // 保存输入框内容
    SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString("user_input", editText.getText().toString());
    editor.apply();
}
场景2:恢复用户输入
// 在onCreate()中恢复数据
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    // 恢复输入
    SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
    String savedInput = prefs.getString("user_input", "");
    editText.setText(savedInput);
}
场景3:暂停/恢复媒体播放
private MediaPlayer mediaPlayer;

@Override
protected void onResume() {
    super.onResume();
    if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
        mediaPlayer.start(); // 恢复播放
    }
}

@Override
protected void onPause() {
    super.onPause();
    if (mediaPlayer != null && mediaPlayer.isPlaying()) {
        mediaPlayer.pause(); // 暂停播放
    }
}

避坑指南

🚫 不要onCreate()中保存关键数据(会被重复调用)
应该onPause()中保存关键数据
🚫 不要onResume()中初始化UI(会重复)
应该onCreate()中初始化UI

不是所有变量都需要声明为成员变量,但 UI 控件(如 EditText、Button)必须是成员变量。


📌 关键代码(数据保存恢复):

// 保存数据
@Override
protected void onPause() {
    super.onPause();
    SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString("input", editText.getText().toString());
    editor.apply();
}

// 恢复数据
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
    String savedInput = prefs.getString("input", "");
    editText.setText(savedInput);
}

SharedPreferences 深度解析:Android 持久化存储的基石

SharedPreferences 是 Android 中最常用、最轻量级的本地存储方案,用于保存简单的键值对数据(如用户偏好、登录状态、应用设置等)。


🔑 一、SharedPreferences 的本质

特性说明
本质一个 XML 文件(存储在 /data/data/<package_name>/shared_prefs/
数据格式键值对(String 键 + String/int/boolean/float/long 值)
存储位置应用私有目录(其他应用无法访问)
数据持久性永久存储(应用卸载后才删除)
适用场景小型数据(<100KB)、非敏感数据(如用户设置)

💡 类比:SharedPreferences 就像一个加密的用户手册,你把关键设置写在上面,应用每次启动都能读取。


📦 二、SharedPreferences 的核心使用场景

场景 1:保存用户输入

// 保存输入内容(在 onPause 中)
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
prefs.edit().putString("user_input", editText.getText().toString()).apply();

场景 2:保存用户偏好设置

// 保存主题设置
SharedPreferences prefs = getSharedPreferences("settings", MODE_PRIVATE);
prefs.edit().putBoolean("dark_mode", true).apply();

场景 3:记录用户登录状态

// 保存登录状态
SharedPreferences prefs = getSharedPreferences("auth", MODE_PRIVATE);
prefs.edit().putBoolean("is_logged_in", true).apply();

⚙️ 三、SharedPreferences 的核心 API

✅ 1. 获取 SharedPreferences 对象

// 方式 1:默认名称(推荐)
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);

// 方式 2:自定义名称
SharedPreferences prefs = getSharedPreferences("user_settings", Context.MODE_PRIVATE);

💡 关键点

  • MODE_PRIVATE唯一安全模式(其他应用无法访问)
  • 建议用常量定义名称(避免拼写错误):
    public static final String PREFS_NAME = "app_prefs";
    

✅ 2. 保存数据(安全写入)

// 获取编辑器(不推荐直接用 edit())
SharedPreferences.Editor editor = prefs.edit();

// 写入数据(推荐使用 apply() 而不是 commit())
editor.putString("username", "张三");
editor.putInt("age", 25);
editor.putBoolean("is_active", true);
editor.apply(); // ✅ 异步写入,不阻塞主线程

💡 为什么用 apply() 而不是 commit()

方法特点适用场景
apply()异步(后台线程)99% 场景(推荐)
commit()同步(主线程阻塞)仅当需要立即确认写入时

✅ 3. 读取数据

// 读取字符串(提供默认值)
String username = prefs.getString("username", "游客");

// 读取整数(提供默认值)
int age = prefs.getInt("age", 0);

// 读取布尔值(提供默认值)
boolean isActive = prefs.getBoolean("is_active", false);

⚠️ 四、常见错误及解决方案

❌ 错误 1:忘记检查默认值

// 错误:未提供默认值
String username = prefs.getString("username", null); // 可能返回 null
editText.setText(username); // NullPointerException!

// 正确:提供默认值
String username = prefs.getString("username", "游客");
editText.setText(username);

❌ 错误 2:使用 commit() 阻塞主线程

// 错误:同步写入,可能卡顿
prefs.edit().putString("user_input", text).commit();

// 正确:使用 apply() 异步写入
prefs.edit().putString("user_input", text).apply();

❌ 错误 3:键名拼写错误

// 错误:键名不一致
prefs.edit().putString("user_input", text).apply();

// 获取时拼错
String saved = prefs.getString("user_input2", ""); // 会返回默认值

解决方案:使用常量定义键名

public static final String KEY_USER_INPUT = "user_input";

// 保存
prefs.edit().putString(KEY_USER_INPUT, text).apply();

// 读取
String saved = prefs.getString(KEY_USER_INPUT, "");

📊 五、SharedPreferences vs Bundle:关键区别

特性SharedPreferencesBundle
生命周期永久存储(应用关闭后仍保留)临时存储(仅在 Activity 生命周期内有效)
数据范围应用级全局(所有 Activity 可访问)组件级(仅在当前 Activity/Fragment 内有效)
使用场景保存用户偏好、登录状态等屏幕旋转时临时保存数据
数据大小无严格限制(但建议 <100KB)限制 ~1MB
恢复时机应用启动时自动恢复Activity 重建时(如 onCreate/onSaveInstanceState

🌟 六、最佳实践

✅ 1. 使用常量定义键名(避免拼写错误)

public class MainActivity extends AppCompatActivity {
    public static final String PREFS_NAME = "app_prefs";
    public static final String KEY_USER_INPUT = "user_input";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 从 SharedPreferences 读取
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
        String savedInput = prefs.getString(KEY_USER_INPUT, "");
        editText.setText(savedInput);
    }
}

✅ 2. 优先使用 apply()(避免主线程阻塞)

// ✅ 正确:异步写入
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
prefs.edit().putString(KEY_USER_INPUT, editText.getText().toString()).apply();

✅ 3. 保存前检查数据

// ✅ 安全写入
if (editText != null) {
    String text = editText.getText().toString();
    prefs.edit().putString(KEY_USER_INPUT, text).apply();
}

✅ 4. 读取时提供默认值

// ✅ 安全读取
String savedInput = prefs.getString(KEY_USER_INPUT, "");

🔥 八、高级技巧:原子操作与线程安全

1. 为什么需要原子操作?

  • 多个线程同时修改 SharedPreferences 会导致数据覆盖
  • Android 4.0+apply()线程安全的(但不是原子操作)

2. 保证原子性的方案

// 方案 1:使用 SharedPreferences.Editor 的 apply()(Android 4.0+ 推荐)
prefs.edit().putString("key", "value").apply();

// 方案 2:使用锁(仅当需要严格原子性时)
synchronized (prefs) {
    prefs.edit().putString("key", "value").apply();
}

💡 重要提示:对于大多数应用,apply() 已足够安全,无需额外加锁。


📌 九、常见问题解答

❓ 问题 1:SharedPreferences 适合存储敏感数据吗?

❌ 不适合!它不加密,敏感数据应使用:

  • EncryptedSharedPreferences(Android Jetpack)
  • 数据库加密(Room)

❓ 问题 2:SharedPreferences 可以存储对象吗?

❌ 不能直接存储对象,但可以通过以下方式:

// 方式 1:序列化(不推荐,效率低)
String json = new Gson().toJson(user);
prefs.edit().putString("user", json).apply();

// 方式 2:使用 Parcelable(推荐,但需自己实现)

❓ 问题 3:如何删除 SharedPreferences?

// 删除整个 SharedPreferences
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().clear().apply();

// 删除单个键
getSharedPreferences(PREFS_NAME, MODE_PRIVATE).edit().remove("key").apply();

✅ 十、总结:SharedPreferences 的关键点

问题答案
为什么用 SharedPreferences?保存小量、持久化的键值对数据(如用户偏好)
如何避免崩溃?1. 检查 editText 是否为 null2. 提供默认值3. 使用常量键名
apply() vs commit()始终用 apply()(异步,不阻塞主线程)
与 Bundle 的关系Bundle 临时保存,SharedPreferences 永久保存
安全使用1. 用常量定义键名2. 提供默认值3. 使用 apply()

📚 官方文档支持

Android Developers - SharedPreferences

"SharedPreferences is a simple way to store key-value pairs of primitive data types. It's ideal for storing small amounts of data, such as user preferences."


✅ 结论

SharedPreferences 是 Android 中最基础、最安全的持久化存储方案
无需再担心 NullPointerException 或数据丢失问题

💡 记住
apply() + 常量键名 + 默认值 = 安全可靠的 SharedPreferences 使用

最终代码

package com.example.singlenews;

import androidx.appcompat.app.AppCompatActivity;

import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "ActivityLifecycle";
    public static final String PREFS_NAME = "app_prefs";
    public static final String USER_INPUT = "user_input";

    private EditText editText;
    private MediaPlayer mediaPlayer;

    // Activity类的onCreate方法重写。这是Activity生命周期的第一个被调用的核心方法
    @Override // 当前方法覆盖父类(Activity)中定义的同名方法。重写生命周期方法,以实现自定义逻辑
    protected void onCreate(Bundle savedInstanceState) { // onCreate当Activity首次创建,系统自动调用此方法。
        /**
         * Bundle savedInstanceState:
         * 参数,用于恢复之前保存的状态(如屏幕旋转、内存不足时Activity被销毁重建)
         * 如果Activity是首次启动,savedInstanceState 为 null
         * 如果Activity是重建,系统会传入之前通过onSaveInstanceState()保存的数据
         *
         * Bundle 是 Android 开发中最基础、最常用的数据传递的键值对容器,用于在组件之间(Activity、Fragment、Service 等)通过 Intent安全地传递数据。
         */
        super.onCreate(savedInstanceState); // 调用父类(Activity)的onCreate()方法。通过父类完成Activity的核心初始化。必须放在自定义之前。
        setContentView(R.layout.activity_main); // 加载并设置Activity的UI布局
        /**
         * R:Android自动生成的资源引用类
         * layout:资源类型(布局文件)
         * activity_main:布局文件名(存在于res/layout目录下activity_main.xml文件)
         */
        // Activity显示界面的唯一入口。系统将activity_main中的UI元素渲染到屏幕上。
        // 后续操作(如初始化控件、设置事件监听)通常在此方法中继续编写。

        // 所有Activity都必须重写onCreate!

        Log.d(TAG, "onCreate()");

        editText = findViewById(R.id.edit_text);
        // 恢复数据
//        SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
//        String savedInput = prefs.getString("user_input", "");
        String savedInput = prefs.getString(USER_INPUT, "");
        editText.setText(savedInput);
        /**
         * SharedPreferences
         * SharedPreferences 是 Android 中最常用、最轻量级的本地存储方案,用于保存简单的键值对数据(如用户偏好、登录状态、应用设置等)。
         */
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart()");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume()");

        // 恢复播放
        if(mediaPlayer != null && !mediaPlayer.isPlaying()){
            mediaPlayer.start();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause()");

        // 保存输入框内容
//        SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
//        editor.putString("user_input", editText.getText().toString());
        editor.putString(USER_INPUT, editText.getText().toString());
        editor.apply(); // apply() 是异步安全的,不会阻塞主线程

        // 暂停播放
        if(mediaPlayer != null && mediaPlayer.isPlaying()){
            mediaPlayer.pause();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop()");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy()");
    }
}