2015年10月28日水曜日

[解決]60秒以上かかる定期処理にWakefulBroadcastReceiverは使えない

Androidで定期的にバックグラウンド処理する場合、従来はPARTIAL_WAKE_LOCKなWakeLockを使う必要がありました。

AlarmManagerでタイマーを仕掛け、タイマーで起こされたBroadcastReceiverがWakeLockでCPUをスリープしないようにしてからServiceを呼んで、Serviceでバックグラウンド処理をし、完了したらBroadcastReceiverが持っているWakeLockを解除してCPUがスリープできるようにする、という流れです。

ここでWakeLockが若干面倒なので処理を簡素化するためにできたのがWakefulBroadcastReceiverです。

公式のトレーニングにも説明が載っています。

WakefulBroadcastReceiverからstartWakefulService()でServiceを起動し、Serviceは処理が済んだらWakefulBroadcastReceiver.completeWakefulIntent()を呼ぶ。
その間はCPUがスリープしないと。

完璧です。

そう思って僕も早速これに置き換えましたが、どうもServiceの処理に時間がかかってしまいます。

もともと2分くらいなのですが、10分とか1時間とかかかっているケースもあります。ただ充電中(開発者オプションで画面付けっぱなし)は2分くらいで済んでいます。

どうも、CPUがスリープしてしまっているように思えます。が、そんなハズはとWakefulBroadcastReceiverのソースを読んでみたところ・・・

wl.acquire(60*1000);

60秒でタイムアウトだそうで。

今回のケースではアウトです。
本当にありがとうございました。

--- 20151029追記 ---

今回はWakefulBroadcastReceiverを取り込んで対処しました。
ソースを自分のプロジェクトにコピペしてpackageとタイムアウト値を変更して使いました。
Apache 2.0なライセンスなので、そこら辺はちゃんとしないといけないですね。

2015年10月27日火曜日

[解決]BOOT_COMPLETEDで起動しない機種があるときはintent-filterに誤りがあるかも

AndroidManifestにこう書いていたら、Zenfone5の電源投入時にonReceiveが呼ばれませんでした。

        <receiver android:name=".BootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>

カテゴリ指定を取ってこう書いたらonReceiveが呼ばれるようになりました。

        <receiver android:name=".BootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

公式のトレーニングにもそう書いてありますね。

しかも最初は呼ばれない(enabled="false")ようにして、ユーザが明示的にONしたときにPackageManager経由で有効にする、というのが正しいやり方のようです。

・・・というか、このやり方なら「電源投入時に起動する」という設定を覚えておく必要がないのですね。