Androidで一定時間ごとに現在地を取得しているアプリの開発中、長時間アプリを使用したあとにアプリを終了しようとするとOSがしばらくフリーズ(場合によってはシステムUIが停止やOSが終了etc)する問題が発生したのでまとめ。
原因
原因は現在地を取得するたびにLocationManager
のrequestLocationUpdates
を呼び出して毎回新しくLocationListener
を登録していたのが原因でした。
// この処理を一定間隔ごとに呼び出し
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
10000,
0,
new LocationListener() {
// 略
});
詳細は分かりませんがアプリ終了時にLocationManager
に登録している全てのListener
の解除か何かが行われて(アプリ側ではListener
の解除の処理は書いていませんでした)アプリの終了処理に時間が掛かってタスク管理のプロセスが停止->OSフリーズという感じかなと予想しています。
ちなみにListener
を登録しまくっていてもアプリの動作自体が明らかに遅くなることはあまり感じられず、アプリを終了したときにのみ問題が起こるという感じでした。
フリーズする時間やOSが終了するかなどは端末によって違いましたが以下のコードでNexus7(2013)で試してみると、
- 100個:問題なく終了
- 300個:数秒フリーズ
- 500個:数秒フリーズして「システムUIは応答していません。」のダイアログが表示される
- 900個:数秒フリーズして「システムUIは応答していません。」のダイアログが表示されてさらにしばらくすると画面が真っ暗になってしばらく操作できなくなる。
という感じでした。
private void test(int n) {
for (int i = 0 ; i < n; i++) {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 3000, 0, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
});
}
対応
requestLocationUpdates
を呼ぶ際にnew LocationListener()
をせず、LocationListener
をimplements
したthis
を指定することで発生しなくなりました。
(が、そもそもrequestLocationUpdates
を一定時間ごとに呼ぶ必要がないのに呼んでいたり、Listener
を登録してるのにイベントを受け取っても何も処理をしていなかったりと問題だらけだったので全面的に変更しました)
まとめ
アプリ終了時に特に重い処理を行っていないのにOSがフリーズしたりする場合があればListener
の登録箇所もチェックしてみると良いかもしれません。