Android Chromeにおける入力データの欠損に関して(2016.04.17)

 弊社ではAndroid端末やiOS端末で使用できる在庫管理プログラムをHTML5で作成しています(パフォーマンス等々の問題を解決するために画面用標準クラスなどを開発して実使用に耐えるシステムを作成しています。
 このたびこのシステムを納入しているお客様から「素早く複数のバーコードのスキャンを行うとデータが欠損する場合がある」と言う報告をいただき、検証してみたところ、実際にバーコードの欠損が発生しました。
 それで、同じような問題を抱えてしまう開発者様もいるのではないかと思い、簡易ながら弊社HPで調査結果を公開しようと、本ページを作成しました。

現象の発生を確認している端末
Android4.4.2

現象の発生を確認しているブラウザ
Chrome37.0.2062.117
標準の「ブラウザ」(4.4.2-20140616)

現象
弊社システムではバーコードをスキャンした直後に商品コードに対応する商品マスターが存在するかのチェックをかけていますが、そのチェックを行っている最中に次のバーコードがスキャンされてしまった場合、入力された文字列が欠損します。
PCのchromeのブラウザなどでは処理中に入力された文字列は内部キャッシュされ、処理が完了された後にテキストボックスに展開されますが、Androidのブラウザでは処理中に入力されたデータはランダムに破棄されてしまうようです。

現象の再現を行うためのプログラム。実行結果はこちら
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Fast Scan Test</title>
        <script type="text/javascript">
            function OnLoadFunction()
            {
                var textSet = document.getElementById("idText");
                textSet.addEventListener( "keydown", KeyDownBarcode );
            }
            
            function KeyDownBarcode(e)
            {
                if( e.keyCode == 13 )
                {
                    BarcodeAnalyse();
                }

                if( e.keyCode == 10 ||
                    e.keyCode == 13 )
                {
                    e.returnValue = false;
                    e.cancelBubble = true;
                }
            }
            
            function BarcodeAnalyse()
            {
                var textSet = document.getElementById("idText" );
                var strText = textSet.value;
                textSet.value = "";
                
                //wait
                var timeStart = new Date();
                while( (new Date()).getTime() - timeStart.getTime() < 2000 );
                
                var divSet = document.getElementById( "idDiv" );
                divSet.innerHTML += "<br>" + strText;
            }
        </script>
    </head>
    <body onload="OnLoadFunction();">
        <input type="text" size="50" id="idText">
        <div id="idDiv" style="border-style: solid;border-color: pink;" >
        </div>
    </body>
</html>
上記のプログラムではテキストボックスが一つ表示されますが、このテキストボックスに文字を入力し、エンターを押すと「BarcodeAnalyse」と言う処理が実行されます。この処理はテキストボックスの中身をクリアし、2秒間ウェイト処理を入れて、入力されていたテキストボックスの内容を下段のdivタグ内に追加していきます。
Androidのchromeおよびブラウザで上記のJavaScriptを実行すると、2秒のウェイト中に入力した内容が欠損します(なぜかエンターだけは欠損しないため、空行や文字が少しだけ入力された行などが出来上がる

また、KeyDownBarcodeのイベント処理中にBarcodeAnalyseの処理が実行されているために次のイベントを処理できない可能性もあると思い、以下のようにプログラムを変更してみました。

実行結果はこちら
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Fast Scan Test</title>
        <script type="text/javascript">
            function OnLoadFunction()
            {
                var textSet = document.getElementById("idText");
                textSet.addEventListener( "keydown", KeyDownBarcode );
            }
            
            function KeyDownBarcode(e)
            {
                if( e.keyCode == 13 )
                {
                    BarcodeAnalyse();
                }

                if( e.keyCode == 10 ||
                    e.keyCode == 13 )
                {
                    e.returnValue = false;
                    e.cancelBubble = true;
                }
            }
            
            var arrayData = new Array();
            
            function BarcodeAnalyse()
            {
                var textSet = document.getElementById( "idText" );
                arrayData.push( textSet.value );
                textSet.value = "";
                
                setTimeout( BarcodeAnalyseMain, 1 );
            }
            
            function BarcodeAnalyseMain()
            {
                if( arrayData.length == 0 )
                {
                    return;
                }
                var strText = arrayData.shift();;
                
                //wait
                var timeStart = new Date();
                while( (new Date()).getTime() - timeStart.getTime() < 2000 );
                
                var divSet = document.getElementById( "idDiv" );
                divSet.innerHTML += "<br>" + strText;
            }
        </script>
    </head>
    <body onload="OnLoadFunction();">
        <input type="text" size="50" id="idText">
        <div id="idDiv" style="border-style: solid;border-color: green;" >
        </div>
    </body>
</html>
 上記では、KeyDownBarcodeイベント処理で呼び出されたBarcodeAnalyseの中ではテキストの内容をArrayにpushし、setTimerイベントをセットして処理を終わらせています。それで、動きとしてはキーボードのイベント処理が完了したのちにBarcodeAnalyseMain関数が呼ばれ、重い処理(2秒ウェイト)を実行することになります。
 この対策により多少は改善されましたが(2行目まではきちんと出るようになった)、連続的に入力した文字列の欠損に関しては回避できませんでした。。

対策
上記のようにプログラム的な改善は難しいと思われます。幸いAndroidのFireFoxではきちんと欠損なくスキャンデータが読み込まれますので、使用するブラウザを変更することによって対処します。また、本案件に関しては何とかしてGoogleに報告しようと思っています。

 以上、皆様の参考になれば幸いです。

2016.04.23:追記
 この記事を書いた後、お客様端末にてFireFoxのセットアップをしたところ、
1.画面が出たとたんにFireFoxクラッシュが多発
2.バーコードスキャンを行った時の挙動が遅すぎる
 と言う2点が発生し、FireFoxでの運用は断念しました。Operaをインストールして試してみたところ、chromeと同様、上記プログラムで欠損が発生して使い物となりませんでした。
 
 救世主となったのは、DolphineBrowserで、欠損は起こらず、クラッシュも発生せず、しかもChromeよりもサクサク動く。またテストしてみた結果、WebSocketやDOMの処理なども特に問題なく動作しました。
 AndroidのJavaScriptでシステムを組むのであれば、今後はDolphineBrowserかChromeをターゲットとしていきたいと思います。