Androidスマホ用に初めて作った地図アプリがほぼ問題なく動作しており、3年目を迎えた。 しかし、Android OSについてはまだごく一部のことしか理解していない。
地図のレンダリングは予め起動してスタンバイしている複数のスレッドで実行しているが、 もっとスマートで効率的な方法があるかも知れない。
地図アプリがスリーブ状態でも位置情報取得を続けるために、Serviceとしている。 位置情報取得Serviceから地図表示Activityにデータ(位置情報)を送るのは broadcast に依っている。
今回、バス路線情報をレンダリングThreadで入手することにした。 その情報をどのようにして地図表示Activityに伝えるのであろうか。 レンダリングの場合、レンダリング完了時に map.invalidate() を実行するだけで、 地図表示Activityは Map#onDraw() を実行する。直ちに実行するとは限らないが、いずれ実行に至る。
情報自体はTileキャッシュにセットしている。 つまり、排他制御は必要となるが、メインThreadとレンダリングThreadはデータやプログラムを共有している。
多分、Serviceの場合は、プロセスが異なり、データ(メモリ)が共有できないため、 broadcast でデータを渡す必要があるのであろう。
いずれ、Android OSの仕組みと動作をより深く理解したいが、 まずは、メインThreadとレンダリングThreadはデータ(メモリ)を共有しているものと考えてみる。
レンダリングThreadから PopupWindow を出せるか試してみる。
PopupBusRoute pbr = new PopupBusRoute(map, map.context); pbr.showAtLocation(map.textViewTop, Gravity.BOTTOM, 0, 0);
やはり、それほど簡単な話ではなかった。レンダリングThread では map.invalidate() といったトリガーだけ として、PopupWindow の表示はメインスレッドで実行すべきであろう。
java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-3,5,main] that has not called Looper.prepare() at android.os.Handler.(Handler.java:227) at android.os.Handler. (Handler.java:129) at android.view.ViewRootImpl$ViewRootHandler. (ViewRootImpl.java:5391) at android.view.ViewRootImpl. (ViewRootImpl.java:5694) at android.view.ViewRootImpl. (ViewRootImpl.java:801) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:389) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133) at android.widget.PopupWindow.invokePopup(PopupWindow.java:1576) at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1342) at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1308) at com.example.map4.RenderThread.run(RenderThread.java:52)
別の方法も試したがうまくいかなかった。今回は、サブスレッドでの監視をやめる。 直接、メインスレッドでバス路線情報取得完了を監視する。