安卓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
相关产品推荐
相关产品推荐

