Androidの古いバージョンでは以下の現象が発生しうるのでメモ。
現象
あるアプリでAsyncTaskのコンストラクタ呼び出し時に以下のようなエラーが発生。
java.lang.ExceptionInInitializerError at sample.service.servicetest.SampleService$1.run(SampleService.java:72) at java.util.Timer$TimerImpl.run(Timer.java:289) Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler.<init>(Handler.java:121) at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421) at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421) at android.os.AsyncTask.<clinit>(AsyncTask.java:152) ... 2 more
再現方法
いくつかの条件がセットになると発生する。
- UIスレッドとは別のワーカースレッドからAsyncTaskの呼び出しを行った。
- アプリの起動後一番最初に実行したAsyncTaskがワーカースレッド側である
- ワーカースレッドでAsyncTaskの呼び出し前にLooperの初期化を行っていない。
原因
補足
- Lopper.prepare()をワーカースレッド側で実行してAsyncTaskを実行すると、AsyncTask内では以後そのLooperが繰り返し使用され、onPostExecute()の結果もそのスレッドに返る。
- onPreExecute()は呼び出しもとのスレッドから実行されるが、onPostExecuteはLooper内で処理されるためLooperの所属スレッドから実行される(未確認)。
- 最新のコードではActivityThreadクラス内のmain()でAsyncTask.init()が実行されるのでstaticフィールドは初期化されている。必然的にAsyncTaskはUIスレッドのHandler(Looper)を持つため、onPostExecue()などのメソッドはUIスレッド側で実行される。