当设备切换 wi-fi 网络时,recyclerview 显示的数据意外清空,重启应用后数据恢复——根本原因在于未妥善处理网络状态变更导致的数据加载逻辑中断,需结合生命周期管理、缓存机制与网络监听实现自动恢复。
当设备切换 wi-fi 网络时,recyclerview 显示的数据意外清空,重启应用后数据恢复——根本原因在于未妥善处理网络状态变更导致的数据加载逻辑中断,需结合生命周期管理、缓存机制与网络监听实现自动恢复。
在 Android 开发中,RecyclerView 本身并不负责数据获取或网络状态感知,它仅负责展示已加载的数据。你遇到的问题——网络切换后列表变空、重启才恢复——表明数据源(如从 SQL Server 获取的 List
? 根本原因分析
- MesaAdapter 当前仅接收并持有 List
mesas 的弱引用副本,但未与数据源(如 Repository 或 ViewModel)建立响应式绑定; - 网络切换可能触发 Activity 重建(如配置变更)、后台任务中断或 API 请求失败,若未做错误处理或缓存回退,mesas 列表会被置空;
- WeakReference
和 WeakReference 虽防内存泄漏,但无法保证数据持久性或状态同步。
✅ 正确实践:网络状态监听 + 数据层解耦 + 缓存兜底
1. 在 Activity/Fragment 中监听网络变化(推荐使用 ConnectivityManager + BroadcastReceiver)
// 在 Activity 中注册(例如 onCreate)
private NetworkChangeReceiver networkChangeReceiver;
private boolean isNetworkAvailable = true;
private void registerNetworkChangeReceiver() {
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver,
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
private void unregisterNetworkChangeReceiver() {
if (networkChangeReceiver != null) {
try {
unregisterReceiver(networkChangeReceiver);
} catch (IllegalArgumentException ignored) {}
}
}
private class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) return;
ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
boolean connected = cm.getActiveNetworkInfo() != null &&
cm.getActiveNetworkInfo().isConnected();
if (connected && !isNetworkAvailable) {
isNetworkAvailable = true;
// ✅ 网络恢复:触发刷新(建议通过 ViewModel 或 Repository)
loadDataFromServer();
} else if (!connected && isNetworkAvailable) {
isNetworkAvailable = false;
// ⚠️ 网络断开:显示本地缓存或 Toast 提示
showOfflineHint();
// 若有本地数据库(如 Room),可自动 fallback 加载缓存数据
loadCachedData();
}
}
}
? 注意:从 Android 7.0(API 24)起,CONNECTIVITY_ACTION 广播在后台受限;生产环境强烈推荐改用 ConnectivityManager.NetworkCallback(API 21+)或 Jetpack 的 NetworkState + Flow(搭配 androidx.lifecycle:lifecycle-runtime-ktx),实现更精准、生命周期安全的监听。
2. Adapter 不应直接持有业务数据,而应由 ViewModel 统一管理
重构关键点:将 mesas 数据源上移至 ViewModel,并通过 LiveData 或 StateFlow 观察:
// MesaViewModel.kt
class MesaViewModel : ViewModel() {
private val mesaDao = MesaDatabase.getInstance(getApplication()).mesaDao()
private val _mesas = MutableLiveData<list>>()
val mesas: LiveData<list>> = _mesas
fun loadMesas() {
viewModelScope.launch {
try {
val remoteList = repository.fetchFromSqlServer() // 网络请求
_mesas.value = remoteList
mesaDao.insertAll(remoteList) // 同步更新本地缓存
} catch (e: Exception) {
// 网络失败 → 自动读取本地缓存
_mesas.value = mesaDao.getAll().await()
}
}
}
}</list></list>
Adapter 改为观察式初始化:
// 在 Activity 中
viewModel.mesas.observe(this, mesas -> {
adapter.submitList(mesas); // 推荐使用 ListAdapter + DiffUtil
});
3. 使用 ListAdapter 替代原始 Adapter(强烈推荐)
避免手动 notifyDataSetChanged() 引发闪烁或状态丢失,提升稳定性与性能:
public class MesaAdapter extends ListAdapter<mesa mesaviewholder> {
private final OnSelectedMesaListener listener;
public MesaAdapter(OnSelectedMesaListener listener) {
super(DIFF_CALLBACK);
this.listener = listener;
}
private static final DiffUtil.Callback DIFF_CALLBACK = new DiffUtil.Callback() {
@Override
public int getOldListSize() { return getCurrentList().size(); }
@Override
public int getNewListSize() { return getCurrentList().size(); }
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return getCurrentList().get(oldItemPosition).getId() ==
getCurrentList().get(newItemPosition).getId();
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return getCurrentList().get(oldItemPosition).equals(
getCurrentList().get(newItemPosition));
}
};
@Override
public void onBindViewHolder(@NonNull MesaViewHolder holder, int position) {
holder.configurarMesa(getItem(position));
holder.itemView.setOnClickListener(v -> {
int pos = holder.getBindingAdapterPosition();
if (pos != RecyclerView.NO_POSITION && listener != null) {
listener.onSelected(getItem(pos));
}
});
}
}</mesa>
? 关键注意事项
- ❌ 不要在 Adapter 中直接操作网络或数据库——违反单一职责原则,且易引发内存泄漏与线程问题;
- ✅ 所有网络/数据库操作必须在后台线程执行(Coroutine / ExecutorService / RxJava);
- ✅ 始终为 RecyclerView 设置 setHasFixedSize(true)(若 item 高度固定)以提升性能;
- ✅ onDestroy() 中务必调用 unregisterNetworkChangeReceiver(),避免 BroadcastReceiver 泄漏;
- ✅ 使用 submitList()(ListAdapter)替代 notifyDataSetChanged(),确保动画与状态一致性。
通过将数据逻辑移出 Adapter、引入响应式网络监听与本地缓存策略,你的 RecyclerView 将真正具备「离线可用、在线同步、无缝切换」的能力——这才是现代 Android 架构的最佳实践。










