Android中使用Bitmap更新ImageView时应用崩溃,求解决方案
问题分析与解决方案
伙计,你的代码里有几个关键问题导致了崩溃,我帮你一一拆解:
1. 最致命的问题:跨线程操作UI组件
Android的UI组件(比如ImageView)只能在主线程中更新,你在后台Thread里直接调用imageView.setImageBitmap(bitmap),这绝对会触发崩溃——系统会抛出CalledFromWrongThreadException。这是Android开发的基础规则,千万不能违反!
2. 字节读取不完整的潜在问题
你调用Connection.inBytesCam?.read(byteArray)时,完全忽略了read()方法的返回值。DataInputStream.read(byte[])返回的是实际读取到的字节数,它不一定会一次性填满整个byteArray(比如网络传输时可能分批次到达)。如果读取的字节数小于76032,后续转Bitmap时就会因为数据不完整而出错。
3. Byte转Int的符号错误问题
Byte在Kotlin里是有符号类型(范围-128到127),直接调用byte.toInt()会把0x80-0xFF的字节转换成负数(比如0xFF的byte会变成-1),而Color.rgb()需要的是0-255的正数,这会导致颜色值错误,甚至可能让Bitmap创建失败。
修正后的完整代码
我把所有问题都修复了,还加入了循环更新的逻辑(满足你类似视频播放器的需求),同时加入了异常处理和线程生命周期管理:
class Cam : AppCompatActivity() { // 标记线程是否应该继续运行 private var isRunning = true override fun onCreate(savedInstanceState: Bundle?) { Log.d("Cam activity", "onCreate") super.onCreate(savedInstanceState) setContentView(R.layout.activity_cam) val buttonBack: Button = findViewById(R.id.button_back) val textView: TextView = findViewById(R.id.textView) val imageView: ImageView = findViewById(R.id.Image_update) // 启动图片接收线程 Thread { Thread.sleep(1000) val expectedSize = 76032 val byteArray = ByteArray(expectedSize) while (isRunning) { try { // 确保读取到完整的字节数据 var totalRead = 0 while (totalRead < expectedSize) { val read = Connection.inBytesCam?.read(byteArray, totalRead, expectedSize - totalRead) ?: 0 if (read <= 0) { // 连接断开,退出循环 break } totalRead += read } if (totalRead != expectedSize) { Log.e("Cam", "读取字节不完整,预期$expectedSize,实际$totalRead") continue } // 转换字节数组为像素数组,修复符号问题 val nrOfPixels = byteArray.size / 3 val pixels = IntArray(nrOfPixels) for (i in 0 until nrOfPixels) { // 用0xFF & byte.toInt()获取无符号的0-255数值 val r = byteArray[3 * i].toInt() and 0xFF val g = byteArray[3 * i + 1].toInt() and 0xFF val b = byteArray[3 * i + 2].toInt() and 0xFF pixels[i] = Color.rgb(r, g, b) } // 创建Bitmap val bitmap = Bitmap.createBitmap(pixels, 176, 144, Bitmap.Config.ARGB_8888) // 切换到主线程更新ImageView runOnUiThread { imageView.setImageBitmap(bitmap) textView.text = "已更新图片:${System.currentTimeMillis()}" } // 控制帧率,比如30帧/秒,可根据需求调整 Thread.sleep(33) } catch (e: Exception) { Log.e("Cam", "图片更新出错", e) // 出错后可以尝试重连,或者退出循环 break } } }.start() buttonBack.setOnClickListener { // 停止线程,避免内存泄漏 isRunning = false // 直接在主线程启动新Activity,不需要额外开线程 val intent = Intent(this@Cam, ControlActivity::class.java) startActivity(intent) } } override fun onDestroy() { super.onDestroy() // Activity销毁时停止线程,释放资源 isRunning = false // 如果Socket连接还开着,记得关闭 Connection.inBytesCam?.close() Connection.socket?.close() } }
额外的注意事项
- 线程生命周期管理:我加入了
isRunning标记,在Activity销毁时停止后台线程,避免内存泄漏和空指针异常。 - Socket资源释放:记得在
onDestroy里关闭Socket和输入流,否则会占用系统资源。 - 异常处理:加入了try-catch捕获异常,方便你查看日志定位问题。
- 帧率控制:用
Thread.sleep(33)实现大概30帧/秒的更新速度,你可以根据实际需求调整。
内容的提问来源于stack exchange,提问作者tora_ananas
相关产品推荐
相关产品推荐

