Android中使用AsyncTask读取外部存储图片时快速刷新崩溃问题
问题分析与修复方案
核心崩溃原因
- 后台线程操作UI组件:
doInBackground中直接调用binding.refreshView.setRefreshing(false),Android规定所有UI操作必须在主线程执行,快速刷新时该操作会直接触发线程异常。 - 频繁UI刷新+非线程安全集合:循环内每次调用
publishProgress,导致主线程频繁创建RecyclerView Adapter;同时list使用普通ArrayList,后台线程添加元素、主线程读取的并发操作会引发ConcurrentModificationException,快速刷新时并发冲突更明显。 - 低效的文件后缀判断:重复创建
Uri.fromFile(file)来判断后缀,额外消耗性能。
修复步骤
1. 迁移UI操作至主线程
将binding.refreshView.setRefreshing(false)移至onPreExecute/onPostExecute,或在后台线程用runOnUiThread包裹,确保UI操作在主线程执行。
2. 优化UI刷新逻辑
- 避免循环内频繁调用
publishProgress,改为批量更新(比如每添加10个元素刷新一次)。 - 复用RecyclerView Adapter,而非每次新建,减少资源消耗。
3. 保证集合线程安全
使用临时集合在后台线程处理数据,最后统一合并到主线程访问的list,或改用CopyOnWriteArrayList这类线程安全集合。
4. 优化文件后缀判断
直接通过文件名(转小写)判断后缀,无需重复创建Uri对象。
修改后的代码
new AsyncTask<Void, Integer, Void>() { @Override protected Void doInBackground(Void... voids) { // 后台线程通过runOnUiThread执行UI操作 getActivity().runOnUiThread(() -> binding.refreshView.setRefreshing(false)); // 后台线程用临时集合存储数据,避免并发冲突 List<satusModal> tempList = new ArrayList<>(); for (int i = 0; i < allFiles.length; i++) { File file = allFiles[i]; String fileName = file.getName().toLowerCase(); // 直接判断文件名后缀,提升效率 if (fileName.endsWith(".jpeg") || fileName.endsWith(".gif") || fileName.endsWith(".jpg") || fileName.endsWith(".png")) { satusModal modal = new satusModal(file.getAbsolutePath(), file.getName(), "whats" + i, Uri.fromFile(file)); tempList.add(modal); } // 每处理10个文件更新一次进度,避免频繁UI刷新 if (i % 10 == 0) { publishProgress(tempList.size()); } } // 合并临时集合到主线程的list list.clear(); list.addAll(tempList); // 最后一次更新进度 publishProgress(list.size()); return null; } @Override protected void onPreExecute() { super.onPreExecute(); binding.progressBar.setVisibility(View.VISIBLE); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); binding.progressBar.setVisibility(View.GONE); if (list.size() >= 1) { // 复用Adapter,仅更新数据而非重建 if (adapter == null) { adapter = new WhatsAppAdapter(list, getContext()); binding.recyclerViewFromImagesFragment.setAdapter(adapter); } else { adapter.notifyDataSetChanged(); } } } @Override protected void onPostExecute(Void unused) { super.onPostExecute(unused); binding.progressBar.setVisibility(View.GONE); if (list.size() >= 1) { if (adapter == null) { adapter = new WhatsAppAdapter(list, getContext()); binding.recyclerViewFromImagesFragment.setAdapter(adapter); } else { adapter.notifyDataSetChanged(); } } else { binding.warningNoStatus.setVisibility(View.VISIBLE); } } }.execute();
额外注意事项
- Android 7.0及以上版本,
Uri.fromFile(file)会触发FileUriExposedException,建议改用FileProvider生成内容Uri。 AsyncTask已在Android 11及以上被废弃,长期来看建议改用Coroutines协程或WorkManager实现后台任务,兼容性和稳定性更好。
内容的提问来源于stack exchange,提问作者SHAW
相关产品推荐
相关产品推荐

