You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中使用AsyncTask读取外部存储图片时快速刷新崩溃问题

问题分析与修复方案

核心崩溃原因

  1. 后台线程操作UI组件:doInBackground中直接调用binding.refreshView.setRefreshing(false),Android规定所有UI操作必须在主线程执行,快速刷新时该操作会直接触发线程异常。
  2. 频繁UI刷新+非线程安全集合:循环内每次调用publishProgress,导致主线程频繁创建RecyclerView Adapter;同时list使用普通ArrayList,后台线程添加元素、主线程读取的并发操作会引发ConcurrentModificationException,快速刷新时并发冲突更明显。
  3. 低效的文件后缀判断:重复创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 21:45:38