需求:Activity A 寫入文本,Activity C 未啟動時也能讀取該文本。核心誤區:嘗試跨 Activity 直接操作 TextView (視圖引用),正確模式是數據持久化 + 各自綁定。
// ❌ Activity A 嘗試操作 Activity C 的 View
TextView tv = findViewById(R.id.showmsg); // 這是 Activity A 自己的 View!
tv.setText("data");
public class ActivityA extends AppCompatActivity {
private EditText inputEdit;
private static final String PREFS_NAME = "shared_text_prefs";
private static final String KEY_TEXT = "shared_text_key";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
inputEdit = findViewById(R.id.inputEdit);
// 方案 1: 文本變化即時保存 (適用於輸入框)
inputEdit.addTextChangedListener(new TextWatcher() {
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
saveText(s.toString());
}
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override public void afterTextChanged(Editable s) {}
});
// 方案 2: 按鈕點擊保存 (適用於表單提交)
findViewById(R.id.btnSave).setOnClickListener(v ->
saveText(inputEdit.getText().toString())
);
}
private void saveText(String text) {
getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
.edit()
.putString(KEY_TEXT, text)
.apply(); // 異步寫入,不阻塞主線程
}
}
public class ActivityC extends AppCompatActivity {
private TextView displayText;
private static final String PREFS_NAME = "shared_text_prefs";
private static final String KEY_TEXT = "shared_text_key";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_c);
displayText = findViewById(R.id.showmsg);
// 讀取保存的文本,默認值 "默認文本"
String savedText = getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
.getString(KEY_TEXT, "默認文本");
displayText.setText(savedText);
}
}
// 依賴
implementation "androidx.datastore:datastore-preferences:1.0.0"
// 定義鍵
val SHARED_TEXT_KEY = stringPreferencesKey("shared_text_key")
val Context.dataStore: DataStore by preferencesDataStore("shared_text_prefs")
// Activity A 寫入 (協程)
lifecycleScope.launch {
dataStore.edit { it[SHARED_TEXT_KEY] = inputEdit.text.toString() }
}
// Activity C 讀取 (Flow 自動觀察)
val savedTextFlow = dataStore.data.map { it[SHARED_TEXT_KEY] ?: "默認文本" }
savedTextFlow.distinctUntilChanged().collectIn(lifecycleScope) { text ->
displayText.text = text
}
| 方案 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| SharedPreferences | 簡單鍵值、小數據、舊專案 | API 簡單、同步讀取快 | 主線程磁盤 I/O 風險、無類型安全、無變更通知 |
| DataStore (Preferences) | 新專案首選、Flow 響應式 | 異步、協程、Flow、類型安全、事務性 | 需學習協程/Flow、稍多樣板代碼 |
| DataStore (Proto) | 複雜結構化數據 | Schema 定義、序列化高效 | 需編寫 .proto 文件 |
| ViewModel + Repository | 業務邏輯複雜、多源數據 | 架構清晰、可測試、單一職責 | 過度設計簡單場景 |
這是 Android 「數據驅動 UI」思想的最基礎實踐:UI 只是數據的投影,數據才是核心。