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

安卓APP运行K-Means聚类算法时黑屏/冻结问题求助

解决安卓APP调用K-Means聚类时黑屏/冻结的问题

看起来你遇到的核心问题是主线程阻塞——K-Means聚类属于计算密集型操作,直接在UI线程的onActivityResult方法里执行会占满UI线程资源,导致APP无法响应用户操作,系统就会出现冻结甚至黑屏的表现。偶尔能正常运行应该是碰到了极小尺寸的图片,计算量极低刚好没触发阻塞,但这只是偶然情况。

下面是具体的解决方案:

1. 将K-Means计算移到后台线程

安卓的UI线程专门负责界面渲染和用户交互,所有耗时操作都必须放到后台线程执行。推荐使用Jetpack的Coroutines(现代安卓开发的首选方案),或者入门友好的AsyncTask(虽已标记为Deprecated,但适合快速验证)。

Java版本的AsyncTask实现

如果你的项目还是Java,可以用AsyncTask封装耗时操作:

// 定义后台任务类
private class KMeansTask extends AsyncTask<Bitmap, Void, ArrayList<Integer>> {
    private ColorFinder colorFinder;
    private ArrayList<Point> rawColors;
    private Bitmap targetBitmap;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        colorFinder = new ColorFinder();
        // 可选:在这里显示加载提示,比如ProgressBar
    }

    @Override
    protected ArrayList<Integer> doInBackground(Bitmap... bitmaps) {
        targetBitmap = bitmaps[0];
        // 在后台线程执行K-Means计算
        rawColors = colorFinder.getColors(targetBitmap, 4, 10);
        ArrayList<Integer> cs = new ArrayList<>(4);
        for (Point color : rawColors){
            cs.add(hexToColor(color));
        }
        return cs;
    }

    @Override
    protected void onPostExecute(ArrayList<Integer> cs) {
        super.onPostExecute(cs);
        // 回到主线程更新UI
        image.setImageBitmap(targetBitmap);
        
        ColorDrawable drawable1 = (ColorDrawable) color1.getDrawable();
        drawable1.setColor(cs.get(0));
        textView1.setText(colorFinder.RGBtoHex(rawColors.get(0)));
        
        ColorDrawable drawable2 = (ColorDrawable)color2.getDrawable();
        drawable2.setColor(cs.get(1));
        textView2.setText(colorFinder.RGBtoHex(rawColors.get(1)));
        
        ColorDrawable drawable3 = (ColorDrawable)color3.getDrawable();
        drawable3.setColor(cs.get(2));
        textView3.setText(colorFinder.RGBtoHex(rawColors.get(2)));
        
        ColorDrawable drawable4 = (ColorDrawable)color4.getDrawable();
        drawable4.setColor(cs.get(3));
        textView4.setText(colorFinder.RGBtoHex(rawColors.get(3)));
        
        // 可选:隐藏加载提示
    }
}

// 在onActivityResult中调用任务
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK){
        try {
            final Uri imageUri = data.getData();
            final InputStream imageStream = getContentResolver().openInputStream(imageUri);
            final Bitmap selectedImage = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(imageStream), 100, 100, true);
            
            // 启动后台任务执行K-Means
            new KMeansTask().execute(selectedImage);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Toast.makeText(MainScreen.this, "Something went wrong", Toast.LENGTH_LONG).show();
        }
    } else {
        Toast.makeText(MainScreen.this, "You haven't picked image", Toast.LENGTH_LONG).show();
    }
}

2. 优化Bitmap处理,降低计算压力

虽然你已经把Bitmap缩放到100x100,但可以进一步优化解码过程,避免不必要的内存占用:

  • 使用BitmapFactory.Options的inSampleSize参数,在解码时直接缩小图片,而不是先解码再缩放
  • 用完的临时Bitmap及时调用recycle()释放内存

示例代码:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4; // 把图片缩小为原来的1/4,可根据实际调整
Bitmap tempBitmap = BitmapFactory.decodeStream(imageStream, null, options);
Bitmap selectedImage = Bitmap.createScaledBitmap(tempBitmap, 100, 100, true);
tempBitmap.recycle(); // 释放临时Bitmap的内存

3. 补充异常捕获与日志

目前你的代码只捕获了FileNotFoundException,建议在K-Means计算的代码块中添加异常捕获,打印详细日志,方便排查偶尔出现的问题:

// 在doInBackground方法中添加
try {
    rawColors = colorFinder.getColors(targetBitmap, 4, 10);
} catch (Exception e) {
    Log.e("KMeansError", "聚类计算出错", e);
    // 可选:给用户显示错误提示
    return null;
}

这样修改后,APP就不会因为K-Means的耗时计算阻塞UI线程,冻结和黑屏的问题应该就能解决了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:57:44