Kotlin/Anko/OpenCV/CameraView 无法创建处理程序,因为线程尚未调用 Looper.prepare()
Posted
技术标签:
【中文标题】Kotlin/Anko/OpenCV/CameraView 无法创建处理程序,因为线程尚未调用 Looper.prepare()【英文标题】:Kotlin/Anko/OpenCV/CameraView can't create handler because thread hasn't called Looper.prepare() 【发布时间】:2019-03-14 03:44:32 【问题描述】:所以,我目前正在尝试使用 OpenCV 将使用 CameraView 拍摄的照片转换为灰度。拍照后,我使用Anko 进行异步处理。处理完成后,我将图像写入 MediaStore。 Activity 有一个 CameraView 和一个 Button 来拍照。
但是,当我按下按钮时,出现以下异常:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)
at com.otaliastudios.cameraview.CameraUtils.decodeBitmap(CameraUtils.java:101)
at com.otaliastudios.cameraview.CameraUtils.decodeBitmap(CameraUtils.java:83)
at com.example.joao.waterprototype.MainActivity$onCreate$2$onPictureTaken$1.invoke(MainActivity.kt:55)
at com.example.joao.waterprototype.MainActivity$onCreate$2$onPictureTaken$1.invoke(MainActivity.kt:46)
at org.jetbrains.anko.AsyncKt$doAsync$1.invoke(Async.kt:143)
at org.jetbrains.anko.AsyncKt$doAsync$1.invoke(Async.kt)
at org.jetbrains.anko.AsyncKt$sam$java_util_concurrent_Callable$0.call(Async.kt)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
上述问题发生的地方在下面的sn-p中有描述:
camera.addCameraListener(object : CameraListener()
override fun onPictureTaken(jpeg: ByteArray?)
Toast.makeText(applicationContext, "Image Taken", Toast.LENGTH_SHORT).show()
doAsync()
lateinit var result : Bitmap
CameraUtils.decodeBitmap(jpeg)
val mat = Mat()
val bmp32 = it.copy(Bitmap.Config.ARGB_8888, true)
Utils.bitmapToMat(bmp32, mat)
val greyMat = Mat()
cvtColor(mat, greyMat, COLOR_BGRA2GRAY)
result = Bitmap.createBitmap(greyMat.cols(), greyMat.rows(), Bitmap.Config.ARGB_8888)
Utils.matToBitmap(greyMat, result)
uiThread
val title = "$UUID.randomUUID().jpg"
val savedImageURL = MediaStore.Images.Media.insertImage(
contentResolver,
result,
title,
"Image of $title"
)
val uri = Uri.parse(savedImageURL)
Toast.makeText(applicationContext, "Image Created at $uri.path", Toast.LENGTH_SHORT).show()
)
这些异常的一个共同主题是在使用 Async 上下文时使用 Toast,但我已经仔细检查过它,并且我很小心不要在那里使用它们。事实上,在 decodeBitmap 调用中发生了上述错误。这很奇怪,因为 CameraView 文档坚持说该函数是在工作线程上调用的。你们中的任何人都可以向我解释发生了什么以及如何解决它吗?
为了更容易重现,这里是完整的 MainActivity.kt:
package com.example.joao.waterprototype
import android.graphics.Bitmap
import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.WindowManager
import android.widget.Toast
import com.otaliastudios.cameraview.CameraListener
import com.otaliastudios.cameraview.CameraUtils
import com.otaliastudios.cameraview.Gesture
import com.otaliastudios.cameraview.GestureAction
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread
import org.opencv.android.OpenCVLoader
import org.opencv.android.Utils
import org.opencv.core.Mat
import org.opencv.imgproc.Imgproc.COLOR_BGRA2GRAY
import org.opencv.imgproc.Imgproc.cvtColor
import java.util.*
class MainActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
camera.setLifecycleOwner(this)
take_photo.setOnClickListener
camera.capturePicture()
camera.mapGesture(Gesture.PINCH, GestureAction.ZOOM); // Pinch to zoom!
camera.mapGesture(Gesture.TAP, GestureAction.FOCUS_WITH_MARKER); // Tap to focus!
camera.mapGesture(Gesture.LONG_TAP, GestureAction.CAPTURE); // Long tap to shoot!
camera.addCameraListener(object : CameraListener()
override fun onPictureTaken(jpeg: ByteArray?)
Toast.makeText(applicationContext, "Image Taken", Toast.LENGTH_SHORT).show()
doAsync()
lateinit var result : Bitmap
CameraUtils.decodeBitmap(jpeg)
val mat = Mat()
val bmp32 = it.copy(Bitmap.Config.ARGB_8888, true)
Utils.bitmapToMat(bmp32, mat)
val greyMat = Mat()
cvtColor(mat, greyMat, COLOR_BGRA2GRAY)
result = Bitmap.createBitmap(greyMat.cols(), greyMat.rows(), Bitmap.Config.ARGB_8888)
Utils.matToBitmap(greyMat, result)
uiThread
val title = "$UUID.randomUUID().jpg"
val savedImageURL = MediaStore.Images.Media.insertImage(
contentResolver,
result,
title,
"Image of $title"
)
val uri = Uri.parse(savedImageURL)
Toast.makeText(applicationContext, "Image Created at $uri.path", Toast.LENGTH_SHORT).show()
)
companion object
// Used to load the 'native-lib' library on application startup.
init
if (!OpenCVLoader.initDebug())
Log.e("OpenCV", "Cannot connect to OpenCV Manager")
else
Log.e("OpenCV", "Connected Successfully")
这里是activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_
android:layout_ >
<com.otaliastudios.cameraview.CameraView
android:id="@+id/camera"
android:keepScreenOn="true"
android:layout_
android:layout_ />
<Button
android:id="@+id/take_photo"
android:layout_
android:layout_
android:text="Take Photo"
android:textAlignment="center"/>
</LinearLayout>
【问题讨论】:
【参考方案1】:我相信 decodeBitmmap 是从 UI 线程启动的假设,即使实际工作是在工作线程上完成的。这是有问题的代码......在调用者线程(第 101 行)上执行:
final Handler ui = new Handler();
关于类似问题有一点讨论here
【讨论】:
以上是关于Kotlin/Anko/OpenCV/CameraView 无法创建处理程序,因为线程尚未调用 Looper.prepare()的主要内容,如果未能解决你的问题,请参考以下文章