かならずお読みください注意事項

Web 文字盤 8 番外編

パソコンとAndroidにおけるJavaScriptの違いと不具合の対策

stackoverflow

0 はじめに

はじめの予定では、前回でWeb文字盤の話は一区切りつけるはずでした。ところが前回の動画にも見られるように、Androidでは文がうまく変更できなかったり、文の番号が入れ替わったりする不具合が見つかりました。不都合とは都合の悪いときに限って起きるもののようです。

しかし不思議なことにWindowsやubuntuでは同じプログラムを動かしているのにこの不具合は起きません。この不具合の原因を調べ対策していくなかで次々に意外なことがわかりました。結局苦労もしましたが今回の経験から学ぶところも少なくありませんでした。そこでこれから同じことに取り組む人たちのヒントになればと考えてここにまとめることにしました。このようないきさつですので今回は重箱の隅をつんつんつつくような話です。

1 Web文字盤 開発のおおまかな手順

Web文字盤の開発は、まずWindows10のChromeでローカルに保存したファイルからスタートしました。続いてこれらのファイルをサーバにアップロードしてオンライン、さらにオフラインで動作を確認しました。その後、ubuntuのChromeで同様にオンライン、オフラインの動作を確認しました。そして最後にAndroidのChromeでもオンライン、オフラインの動作確認をしていました。

このようにパソコンで開発を進めて後でAndroidで確認する手順にした理由は、まずAndroidではローカルなhtmlファイルを開けない、つまりサーバにあげたファイルでないと動作できない仕様になっているので手間や時間が余分にかかることと、またAndroidよりもパソコンではChromeの開発者ツールで確認作業がやりやすいためです。主に作業効率が理由です。

このような手順で作業をしているとほとんどの場合で、パソコンでOKならAndroidでも問題ないことになりますので、このため徐々にAndroidでの確認テストは最後にすこしするだけになりました。このためAndroid特有の不具合に気がつくのが遅れることになりました。

2 はじめの不具合、文番号の混乱

Web文字盤は、JavaScriptで作られ10種類の文書と最後に使用していた文番号を保存し、次回には終了時と同じ表示する機能をもっています。それまでJavaScriptのプログラムはWindowsでもAndroidでも同じように動くことばかりでそういうものだと思っていました。しかしあるときAndroidでは、例えば文1で作ったはずの文字列が、再起動すると異なる文番号で表示されることに気がつきました。パソコンではこのようなことは一度も起きませんでしたのでずいぶん驚きました。これが全く同じプログラムがパソコンとAndroidで異なる動きをする初めての事例でした。何回も再現しますのでこうなったら仕方ありません。こんなこともあり得るのだと頭を切り替えました。

例1

  //開始時 local storage  から読み込み
        storage0 = localStorage.getItem('SERVICE_NAME0');

            if (storage0 == 1 ){  //初回起動時にstrage0=nullとなるのを1にして不具合を回避している
               storage0 =  1;
            } else if (storage0 == 2 ) {
               storage0 =  2;
            } else if (storage0 == 3 ) {
               storage0 =  3;
            } else if (storage0 == 4 ) {
               storage0 =  4;
            } else if (storage0 == 5 ) {
               storage0 =  5;
            } else if (storage0 == 6 ) {
               storage0 =  6;
            } else if (storage0 == 7 ) {
               storage0 =  7;
            } else if (storage0 == 8 ) {
               storage0 =  8;
            } else if (storage0 == 9 ) {
               storage0 =  9;
            } else if (storage0 == 10 ) {
               storage0 =  10;
            } else {
               storage0 =  1;
            }

幸いにも、文番号は一つの変数で管理し、文章の切り替えの部分のみで使っています。上の該当するコードを見直すと変数storage0が文番号の管理の他、ローカルストレージの読み込みにも使っています。これを作ったときも、もしかしてこれはまずいかな?と思いましたが実際にパソコンで動かしてみたら問題なかったので採用しました。しかしこれがAndroidではうまく行かないようです。そこで次の例2のように変更しました。新たな変数storage00を読み込み用に使い、これを判断してstorage0を区分けするようにしました。この結果、Androidでもそしてパソコンでも正常に動くようになりました。

例2

  //開始時 local storage  から読み込み
        storage00 = localStorage.getItem('SERVICE_NAME0');

            if (storage00 == 1 ){  //初回起動時にstrage0=nullとなるのを1にして不具合を回避している
               storage0 =  1;
            } else if (storage00 == 2 ) {
               storage0 =  2;
            } else if (storage00 == 3 ) {
               storage0 =  3;
            } else if (storage00 == 4 ) {
               storage0 =  4;
            } else if (storage00 == 5 ) {
               storage0 =  5;
            } else if (storage00 == 6 ) {
               storage0 =  6;
            } else if (storage00 == 7 ) {
               storage0 =  7;
            } else if (storage00 == 8 ) {
               storage0 =  8;
            } else if (storage00 == 9 ) {
               storage0 =  9;
            } else if (storage00 == 10 ) {
               storage0 =  10;
            } else {
               storage0 =  1;
            }

3 次の不具合、文の保存

Web文字盤には10種類の文書と最後に使用していた文番号を保存し、次回には終了時と同じ表示する機能をつけましたが、Androidでは保存の部分がうまく働いていないようです。該当する部分が以下です。この部分もパソコンでは問題なく動いています。

例3

  //終了時 local storage  へ保存
   window.onbeforeunload = function() {
            if (storage0 == 1 ){
               storage1 = text1.value;
            } else if (storage0 == 2 ) {
               storage2 = text1.value;
            } else if (storage0 == 3 ) {
               storage3 = text1.value;
            } else if (storage0 == 4 ) {
               storage4 = text1.value;
            } else if (storage0 == 5 ) {
               storage5 = text1.value;
            } else if (storage0 == 6 ) {
               storage6 = text1.value;
            } else if (storage0 == 7 ) {
               storage7 = text1.value;
            } else if (storage0 == 8 ) {
               storage8 = text1.value;
            } else if (storage0 == 9 ) {
               storage9 = text1.value;
            } else if (storage0 == 10 ) {
               storage10 = text1.value;
            } else {
            }

       localStorage.setItem('SERVICE_NAME0', storage0 );

       localStorage[SERVICE_NAME1] = JSON.stringify(storage1);
       localStorage[SERVICE_NAME2] = JSON.stringify(storage2);
       localStorage[SERVICE_NAME3] = JSON.stringify(storage3);
       localStorage[SERVICE_NAME4] = JSON.stringify(storage4);
       localStorage[SERVICE_NAME5] = JSON.stringify(storage5);
       localStorage[SERVICE_NAME6] = JSON.stringify(storage6);
       localStorage[SERVICE_NAME7] = JSON.stringify(storage7);
       localStorage[SERVICE_NAME8] = JSON.stringify(storage8);
       localStorage[SERVICE_NAME9] = JSON.stringify(storage9);
       localStorage[SERVICE_NAME10] = JSON.stringify(storage10);
   }

終了の前に、3-37行を実行せよという命令 window.onbeforeunload が2行目にあります。このやり方も「教科書的」定番ですので、ほとほと困りましたが、上で教科書的でない事例について経験しましたので、気を取り直して早速検索して調べることにしました。

この場合、キーワードは onbeforeunload android としました。英単語だけにすると外国語のサイトもヒットします。プログラミング関係では用語が統一されていますのでこのような方法で広く情報を集めることができます。実際やってみると日本語のサイトはヒットしませんでしたが、そのものズバリのサイトが見つかりました。
window.onbeforeunload doesn't trigger on Android Chrome [alt. solution?] window.onbeforeunload がAndroidのChromeで動かないよ(このページのトップ画像参照)
運良くいきなりビンゴです。この付近には同じトラブルに遭遇した人が数人集まっていました。同じ苦労をしている仲間?がいるのでいくらか気が楽になりました。こちらのサイトもそしてつぎに見たこちらのサイトも
window.onbeforeunload not working on Android/Chrome #828
この問題への解決方法のヒントとして、ともに
Android Chrome window.onunload を紹介していました。

このサイトでは、

   window.onbeforeunload = function() {
   }

の代わりに

   window.onunload = window.onbeforeunload = function() {
   }

を使う方法を提案しています。

これは dual calls at the browsers that support both events だそうです。初めて知りました。しかし試して見ると、これでAndroidのChromeでオンラインでもオフラインでも正常にうごくようになります。果たしてこれで問題が解決するでしょうか?

4 最後の問題(かな?)

上で説明した手直しを加えサーバに上げ、オンライン、オフラインで動作を確認しました。問題ありません。その上でインストールしてホーム画面にアイコンが表示されました。ここまで問題ありません。そして最後にこのアイコンから起動すると、、、また同じ問題が発生しました。まるでゾンビです。

ここまではアプリを閉じる寸前に、作成した文書と最後に使用した文番号を保存する部分がうまく動いていないことが十分すぎるほど理解できました。しかしあちこちで何人かの人々が取り組んでいるのにまだ完全ではないようです。これはどうも鬼門のようです。

目的のアプリを実用化するには、保存が必要ですがアプリを閉じる寸前に固執する必要はありません。そこで手段を変えて保存のタイミングを変更することにしました。まず文番号は、文の切り替え後に保存することに、また文は変更した際に逐次(つまり一文字入れる、削除するたびに)保存することにしました。記憶媒体へのアクセスがかなり増えますが、大昔のフロッピーディスクならまだしも現在の媒体では問題にはならないでしょう。

もう少し可能性を追求してもよろしいのでしょうが、このあたりが潮時と考え手段を変更しました。その結果当初の目的通り、WEB文字盤は動くようになりました。

5 おわりに

学校で教わったことや自分で勉強したことが実は違っていたなどということは、特に最近では珍しいことではありません。どうもおかしいな?とおもったら調べる方法は今ではすぐ身の回りにあります。インターネットがなかった頃なら、あちこち歩き回って、訪ね回って、運がよければ数ヶ月、運が悪ければそれっきり。そんな時代に私は学生でした。それから見れば今回の出来事などはまるで夢のような出来事です。この種の仕事のスピードと効率は桁違いに向上しています。今回もパソコンでペコペコやっているあいだに、アメリカの誰かやカナダの誰かやどこかの誰かのことを知り、ミラノの誰かに助けられ目的を達成することができました。

どの分野でも授業や教科書で基本をひととおり勉強したあとは、自分なりに自由に勉強して積み重ねていくことがとても大切です。特に進歩や変化が著しい分野ならなおさらです。そのため基礎勉強の他には英語が必要です。しかし英語は少しでも十分役に立ちます。今回もそうですが、何しろ英語が母語でない人が多数派でみなさんそれぞれの流儀の英語でやっています。きれいで上手な英語にはあまりお目にかかりません。(←個人的感想です)自己流でもやってみればなんとかなります。手段よりも目的が大事です。

今時の流行で、やらなくていいことには極力取り組まない風潮があります。またマニュアルを作る人も使う人も想定外をあまり意識していません。この人達も基本的には、やらなくていいことには極力取り組まない効率重視の方針のようです。まあ何事もなければそれでいいのですが、おかしな気候やおかしな流行病をみると何事もないのはある種の幸運とも思えてきます。

また普段はマニュアル遵守でいざとなったら自分で判断して行動できるのならいいのですが、巷ではマニュアル遵守しかできない人も増えているようです。こうなるとあれもこれもと追加されてマニュアルのメタボ化は進みます。一説によるとマニュアルは片手で持てないほど大きくなると二種類の扱いかたがされるそうです。一つ目は棚の高い位置に置かれ信仰の対象となり、二つ目は低い位置に置かれ忘れ去られます。今見るとうちのマニュアルは高いところにあります。困ったものです。

これを避けるためには、日頃からひとつの目的に対していくつかの手段をポケットに用意しておき、状況に応じて手段を選び目的を忘れないようにこころがけるのが大切です。しかしいつの時代もひとは手段にこだわってしまうそうでこれもよく聞く話です。

関連する話題

参考URL

stackoverflow 関係


2020/02/21 公開

研究企画課リハ工学科にもどる ←もくじはこちらです