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

如何优化Thread代码,使EditText失焦时的图片请求后台运行不卡顿?

问题分析
  1. UI线程被阻塞:getImageLogo方法里调用googleList.join()会强制UI线程等待子线程执行完毕,直接导致界面卡顿甚至无响应——UI线程必须持续处理用户交互,绝对不能被阻塞。
  2. Bitmap内存未优化:直接解码原始尺寸的图片会占用大量内存,拖慢界面渲染速度,还可能引发内存溢出。
  3. 无重复请求处理:用户多次切换EditText焦点时,会重复发起网络请求,浪费网络资源和CPU算力。
优化方案

1. 用协程替代Thread+Join(Android官方推荐)

协程可以在不阻塞UI线程的前提下执行后台任务,还能自动切换到主线程更新UI,同时支持取消未完成的请求,避免无效操作。

2. 优化Bitmap解码

通过采样率缩小图片尺寸,减少内存占用,提升RecyclerView的渲染流畅度。

3. 加入请求取消逻辑

当用户再次触发请求时,取消之前未完成的任务,避免资源浪费。


优化后的代码

Activity类

public class AddItem extends AppCompatActivity {
    private LoadingDialog loadingDialog;
    private RecyclerView imgList;
    private ArrayList<Bitmap> bitmapList = new ArrayList<>();
    private BitmapAdapter adapter;
    private Job searchJob; // 管理搜索任务,支持取消

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化视图、adapter等代码...
        
        et_title.setOnFocusChangeListener((v, hasFocus) -> {
            if (!hasFocus) {
                String serviceName = et_title.getText().toString().trim();
                if (!TextUtils.isEmpty(serviceName)) {
                    startImageSearch(serviceName);
                } else {
                    bitmapList.clear();
                    adapter.notifyDataSetChanged();
                }
            }
        });
    }

    private void startImageSearch(String serviceName) {
        // 取消之前未完成的请求
        if (searchJob != null && searchJob.isActive()) {
            searchJob.cancel();
        }

        // 显示加载弹窗
        if (loadingDialog != null && !loadingDialog.isShowing()) {
            loadingDialog.show();
        }

        // 协程执行后台任务
        searchJob = lifecycleScope.launch(Dispatchers.IO) {
            try {
                ArrayList<Bitmap> imageList = fetchGoogleImages(serviceName);
                // 切换到主线程更新UI
                withContext(Dispatchers.Main) {
                    bitmapList.clear();
                    if (imageList != null) {
                        bitmapList.addAll(imageList);
                    }
                    adapter.notifyDataSetChanged();
                }
            } catch (Exception e) {
                Log.e("ImageSearchError", "搜索图片失败", e);
                withContext(Dispatchers.Main) {
                    bitmapList.clear();
                    adapter.notifyDataSetChanged();
                }
            } finally {
                // 关闭加载弹窗
                withContext(Dispatchers.Main) {
                    if (loadingDialog != null && loadingDialog.isShowing()) {
                        loadingDialog.dismiss();
                    }
                }
            }
        };
    }

    private ArrayList<Bitmap> fetchGoogleImages(String keyword) throws IOException {
        String newKeyword = keyword.toLowerCase(Locale.ROOT).replace(' ', '+');
        String url = "https://www.google.gr/search?bih=427&biw=1835&hl=el&gbv=1&tbm=isch&og=&ags=&q=" + newKeyword;
        
        Document document = Jsoup.connect(url).get();
        Elements imgElements = document.select("img");
        ArrayList<Bitmap> bitmapList = new ArrayList<>();
        
        for (int i = 1; i < imgElements.size() && i <= 7; i++) { // 取前7张图
            String imgSrc = imgElements.get(i).absUrl("src");
            if (TextUtils.isEmpty(imgSrc)) continue;
            
            try (InputStream inputStream = new URL(imgSrc).openStream()) {
                Bitmap bitmap = decodeBitmapFromStream(inputStream);
                bitmapList.add(bitmap);
            } catch (Exception e) {
                Log.w("ImageDecodeError", "解码图片失败: " + imgSrc, e);
            }
        }
        return bitmapList;
    }

    // 优化Bitmap解码,缩小图片尺寸
    private Bitmap decodeBitmapFromStream(InputStream inputStream) throws IOException {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        // 先读取图片尺寸,不加载到内存
        BitmapFactory.decodeStream(inputStream, null, options);
        
        // 计算采样率,将图片缩小到200dp宽高(可按需调整)
        int targetSize = dpToPx(200);
        options.inSampleSize = calculateInSampleSize(options, targetSize, targetSize);
        
        options.inJustDecodeBounds = false;
        // 重置流,重新读取
        inputStream.reset();
        return BitmapFactory.decodeStream(inputStream, null, options);
    }

    // 计算采样率,确保图片不超过目标尺寸
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

    // dp转px
    private int dpToPx(int dp) {
        return (int) (dp * getResources().getDisplayMetrics().density);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 取消任务+回收Bitmap避免内存泄漏
        if (searchJob != null) {
            searchJob.cancel();
        }
        for (Bitmap bitmap : bitmapList) {
            if (!bitmap.isRecycled()) {
                bitmap.recycle();
            }
        }
        bitmapList.clear();
    }
}

必要配置

  • 在build.gradle中添加协程依赖:implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"(版本可按需更新)
  • 在AndroidManifest.xml中添加网络权限:<uses-permission android:name="android.permission.INTERNET" />

内容的提问来源于stack exchange,提问作者DjAs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:46:16