Почему это обнаружение штрих-кода приводит к сбою Chrome или всей ОС Android?

#javascript #android #google-chrome

#javascript #Android #google-chrome

Вопрос:

Я создал систему сканирования штрих-кодов, используя встроенную в Chrome BarcodeDetector . Я основывался на сканере QR-кода Пола Кинлана, который отлично работает на моем телефоне, но когда я запускаю свой собственный код на своем телефоне, это часто приводит к зависанию Chrome или всего системного пользовательского интерфейса. Иногда это становится настолько плохо, что мне нужно перезагрузить телефон, удерживая нажатой кнопку питания.

Я пробовал отладку в консоли разработчика Chrome, но когда телефон зависает, консоль разработчика тоже.

Когда я комментирую фактическое обнаружение QR-кода, я могу оставить страницу открытой в Chrome на 10 минут, и она просто продолжает работать. При запущенном обнаружении QR-кода телефон зависает в любом месте от немедленного до 3 минут спустя.

Я помещаю файлы поддержки (js и HTML) в суть — я не думаю, что это проблема, потому что все работает, когда сканирование штрих-кода закомментировано.

Моя первая попытка:

 // Inspired by/based on on https://github.com/PaulKinlan/qrcode/blob/production/app/scripts/main.mjs

import WebCamManager from './scan/WebCamManager.js';

(function () {
    'use strict';

    var QRCodeCamera = function (element) {
        var root = document.getElementById(element);

        var cameraRoot = root.querySelector('.CameraRealtime');
        var cameraManager = new WebCamManager(cameraRoot);

        var cameraVideo = root.querySelector('.Camera-video');

        // Offscreen canvas is supposed to help with processing speed
        var cameraCanvas = new OffscreenCanvas(1,1);
        var context = cameraCanvas.getContext('2d');

        const detector = new BarcodeDetector({
            formats: ['qr_code'],
        });

        cameraManager.onframeready = async function (frameData) {
            cameraCanvas.width = cameraVideo.videoWidth;
            cameraCanvas.height = cameraVideo.videoHeight;
            context.drawImage(frameData, 0, 0, cameraVideo.videoWidth, cameraVideo.videoHeight);
            if (self.onframe) {
                // Comment out the line below to stop processing QR codes
                await self.onframe(cameraCanvas);
            }
        };

        var processingFrame = false;

        self.onframe = async function (cameraCanvas) {
            // There is a frame in the camera, what should we do with it?
            if (processingFrame == false) {
                processingFrame = true;

                let result = await detector.detect(cameraCanvas);
                processingFrame = false;

                if (result === undefined || result === null || result.length === 0) {
                    return
                };

                if ('vibrate' in navigator) {
                    navigator.vibrate([200]);
                }

                cameraManager.stop();

                var currentURL = new URL(window.location.href);
                var newURL;
                if (result[0].rawValue
                    amp;amp; (newURL = new URL(result[0].rawValue))
                    amp;amp; newURL.hostname == currentURL.hostname
                    amp;amp; newURL.pathname.startsWith('/pickup/qr/')
                ) {
                    window.location.href = newURL;
                } else {
                    alert('Unsupported QR Code: '   result[0].rawValue);
                }
                cameraManager.start();
            }
        };
    };

    window.addEventListener('load', function () {
        var camera = new QRCodeCamera('camera');
    });
})();
  

Я также попытался разделить обнаружение QR на worker (рабочий код в Gist), но у меня такая же проблема:

 
    const worker = new Worker('/js/scan-worker.js');
    worker.addEventListener('message', async (e) => {
        if (Array.isArray(e.data)) {
            var result = e.data;

            if (result === undefined || result === null || result.length === 0) {
                processingFrame = false;
                return
            };

            if ('vibrate' in navigator) {
                navigator.vibrate([200]);
            }

            var currentURL = new URL(window.location.href);
            var newURL;
            if (result[0].rawValue
                amp;amp; (newURL = new URL(result[0].rawValue))
                amp;amp; newURL.hostname == currentURL.hostname
                amp;amp; newURL.pathname.startsWith('/pickup/qr/')
            ) {
                worker.terminate();
                window.location.href = newURL;
            } else {
                alert('Unsupported QR Code: '   result[0].rawValue);
            }
        } else {
            var newError = document.createElement('div');
            newError.classList.add('alert', 'alert-danger');
            newError.innerHTML = e.data;
            errorContainer.prepend(newError);
            worker.terminate();
        }

        processingFrame = false;
    });

    cameraManager.onframeready = async function (frameData) {
        if (processingFrame == false) {
            cameraCanvas.width = cameraVideo.videoWidth;
            cameraCanvas.height = cameraVideo.videoHeight;
            context.drawImage(frameData, 0, 0, cameraVideo.videoWidth, cameraVideo.videoHeight);

            if (self.onframe) {
                await self.onframe(cameraCanvas, context);
            }
        }
    };

    self.onframe = async function (cameraCanvas, context) {
        // There is a frame in the camera, what should we do with it?
        if (processingFrame == false) {
            processingFrame = true;

            worker.postMessage(context.getImageData(0, 0, cameraCanvas.width, cameraCanvas.height));
        }
    };
  

Не уверен, что это что-то изменит — весь код JS выполняется через Laravel Mix.

Комментарии:

1. Он может застрять onframeready ?

2. @MartinZeitler Нет, он застревает detector.detect . Если я закомментирую эту одну строку, цикл будет выполняться вечно.

3. Взглянув на документацию BarcodeDetector , вам не нужно передавать контекст canvas в detector.detect , и я также думаю, что этот метод не следует вызывать в функции обновления фрейма. Попробуйте изолировать и запустить detector.detect onframe функции

4. @DDomen В верхней части этой страницы говорится, что она не завершена. Если вы посмотрите на саму спецификацию , вы увидите, что ImageBitmapSource там необходимо указать, например, холст. Кроме того, как бы вы получили поток изображений с холста (т. Е. С Камеры), не используя этот onframeready метод?

5. Да, извините, я неправильно прочитал документы для части источника изображения. Я хочу сказать onframe , что вы не должны ожидать интенсивной или длительной работы с процессором внутри обратного вызова API, особенно если он выполняется несколько раз в секунду, например onframeready