मुझे अपने अधिकांश ऐप "आलसी द्वारा" प्रारंभकर्ताओं के साथ ठीक काम कर रहे हैं क्योंकि आवश्यक क्रम में सब कुछ जादुई रूप से होता है।

लेकिन सभी प्रारंभकर्ता समकालिक नहीं होते हैं। उनमें से कुछ कॉलबैक लपेट रहे हैं, जिसका अर्थ है कि मुझे कॉलबैक होने तक प्रतीक्षा करने की आवश्यकता है, जिसका अर्थ है कि मुझे runBlocking और suspendCoroutine की आवश्यकता है।

लेकिन सब कुछ रिफैक्टर करने के बाद, मुझे यह IllegalStateException: runBlocking is not allowed in Android main looper thread मिलता है

क्या? आप ब्लॉक नहीं कर सकते? तुम मुझे यहाँ मार रहे हो। यदि मेरा "आलसी से" अवरुद्ध कार्य होता है तो सही तरीका क्या है?

private val cameraCaptureSession: CameraCaptureSession by lazy {
    runBlocking(Background) {
        suspendCoroutine { cont: Continuation<CameraCaptureSession> ->
            cameraDevice.createCaptureSession(Arrays.asList(readySurface, imageReader.surface), object : CameraCaptureSession.StateCallback() {
                override fun onConfigured(session: CameraCaptureSession) {
                    cont.resume(session).also {
                        Log.i(TAG, "Created cameraCaptureSession through createCaptureSession.onConfigured")
                    }
                }

                override fun onConfigureFailed(session: CameraCaptureSession) {
                    cont.resumeWithException(Exception("createCaptureSession.onConfigureFailed")).also {
                        Log.e(TAG, "onConfigureFailed: Could not configure capture session.")
                    }
                }
            }, backgroundHandler)
        }
    }
}

मैं मूल रूप से क्या हासिल करने की कोशिश कर रहा था, इसका अंदाजा लगाने के लिए कक्षा का पूरा GIST: https://gist .github.com/salamanders/aae560d9f72289d5e4b49011fd2ce62b

5
Benjamin H 14 अगस्त 2018, 08:41

1 उत्तर

सबसे बढ़िया उत्तर

यह एक सर्वविदित तथ्य है कि UI थ्रेड पर ब्लॉकिंग कॉल करने से कॉल की अवधि के लिए पूरी तरह से जमे हुए ऐप का परिणाम होता है। createCaptureSession का दस्तावेज़ीकरण विशेष रूप से बताता है

सत्र के कॉन्फ़िगरेशन को पूरा होने में कई सौ मिलीसेकंड लग सकते हैं, क्योंकि कैमरा हार्डवेयर को चालू या पुन: कॉन्फ़िगर करने की आवश्यकता हो सकती है।

इसका परिणाम बहुत आसानी से एक Application Not Responding संवाद और आपके ऐप के नष्ट होने के रूप में हो सकता है। इसलिए कोटलिन ने UI थ्रेड पर runBlocking के विरुद्ध एक स्पष्ट सुरक्षा प्रदान की है।

इसलिए इस प्रक्रिया को ठीक समय पर शुरू करने का आपका विचार, जब आप पहले ही cameraCaptureSession तक पहुंचने का प्रयास कर चुके हैं, काम नहीं कर सकता। इसके बजाय आपको उस कोड को लपेटना होगा जो इसे launch(UI) में एक्सेस करता है और अपने val को suspend fun में बदल देता है।

संक्षेप में:

private var savedSession: CameraCaptureSession? = null

private suspend fun cameraCaptureSession(): CameraCaptureSession {
    savedSession?.also { return it }
    return suspendCoroutine { cont ->
        cameraDevice.createCaptureSession(listOf(readySurface, imageReader.surface), object : CameraCaptureSession.StateCallback() {
            override fun onConfigured(session: CameraCaptureSession) {
                savedSession = session
                Log.i(TAG, "Created cameraCaptureSession through createCaptureSession.onConfigured")
                cont.resume(session)
            }

            override fun onConfigureFailed(session: CameraCaptureSession) {
                Log.e(TAG, "onConfigureFailed: Could not configure capture session.")
                cont.resumeWithException(Exception("createCaptureSession.onConfigureFailed"))
            }
        })
    }
}

fun useCamera() {
    launch(UI) {
        cameraCaptureSession().also { session ->
            session.capture(...)
        }
    }
}

ध्यान दें कि session.capture() एक suspend fun में लपेटने का एक और लक्ष्य है।

यह भी सुनिश्चित करें कि मैंने जो कोड दिया है वह केवल तभी सुरक्षित है जब आप सुनिश्चित कर सकें कि पहली कॉल फिर से शुरू होने से पहले आप cameraCaptureSession() को दोबारा कॉल नहीं करेंगे। फ़ॉलोअप थ्रेड जो इसका ध्यान रखता है।

5
Marko Topolnik 18 अगस्त 2018, 11:59
मैं 80% समझ की ओर हूँ, मेरे साथ रहो। मैंने launch(UI) { ... camstuff... } के साथ सभी कैम क्लास (आरंभीकरण और एक तस्वीर लेने) को लपेटने की कोशिश की, लेकिन वही त्रुटि मिली। मैं अंतिम "एक तस्वीर लेने" कॉल से पहले सभी सेटअप करने के साथ ठीक हो जाऊंगा (जैसे ऑनक्रेट कॉल में) लेकिन मुझे यकीन नहीं है कि उस त्रुटि से कैसे बचा जाए।
 – 
Benjamin H
14 अगस्त 2018, 19:02
क्या आपने runBlocking को तब हटा दिया था?
 – 
Marko Topolnik
14 अगस्त 2018, 19:07
आपके suspend fun cameraCaptureSession() = suspendCoroutine... उदाहरण में, ऐसा लगता है कि यह हर बार कॉल किए जाने पर फ़ंक्शन को फिर से चलाएगा, जिसे मुझे by lazy को अपना ख्याल रखने देने में खुशी हुई ताकि यह प्रत्येक ऑब्जेक्ट को एक बार इनिशियलाइज़ कर सके। लेकिन अगर वास्तव में अवरुद्ध करने के साथ-साथ आलसी द्वारा उपयोग करने का कोई तरीका नहीं है, तो मुझे लगता है कि मुझे करना होगा!
 – 
Benjamin H
14 अगस्त 2018, 21:08
 – 
Benjamin H
14 अगस्त 2018, 21:13
आप runBlocking का उपयोग by lazy के साथ कर सकते हैं, यह कोई बात नहीं है। मुद्दा इसे यूआई थ्रेड पर शुरू कर रहा है और ऐप को फ्रीज कर रहा है। आपको इस विचार को छोड़ना होगा कि आप ऐसा दिखावा कर सकते हैं कि यह एक नियमित val है जिसे आप किसी अन्य की तरह आसानी से एक्सेस कर सकते हैं।
 – 
Marko Topolnik
14 अगस्त 2018, 21:47