ExpoでAndroidで現在地を取得する方法【エラー対処法】
Expoで現在地を取得するときに「expo-location」のモジュールは使うことが多いと思います。
しかし、このExpolocationをAndroidでテストしてみると、
expo-location の現在地取得部分で処理が止まってしまうという事象が発生します。
今回は、そのような状況を回避するためのコードと、具体的にどういう方法で解消しているのかということについて紹介します。
expo-locationを使用している方であれば、必ずハマるところだと思うので、ぜひ一緒に最後まで見ていきましょう!
目次(クリックで読みたい部分にジャンプできます)
【確認】止まってしまうコード
まず、どういう時に事象が発生するのか、確認しておきましょう。
(解決策をすぐ知りたい!って方はこの章は読み飛ばしてくださいね)
expoで現在地を取得するにはexpo-locationを使用して以下のように書くと思います。
import * as Location from 'expo-location';
const { status } = await Location.requestForegroundPermissionsAsync();
if (status === 'granted') {
const location = await Location.getCurrentPositionAsync()//この部分で止まる
}
参考:expo-locatation公式ドキュメント場所 - Expo Documentation
最後の、Location.getCurrentPositionAsync()の部分で、iOSは問題なく動作しているのですが、Androidでは現在地を取得するのに、とても時間がかかる(停止しているようにも見える)状態が発生します。
この状況を解消するために、次から対策を紹介します。
【対策】タイムアウトを設けて既知の位置情報を取得
事象を解決するためには、以下のようなコードを使用します。
コードの概要
getCurrentLocation関数は、ユーザーの現在のGPS位置情報を取得するための処理を行っています。
最も正確に位置情報を取得するために、位置情報を取得することができなければ最後に知られている位置情報を返すようにしています。
async function getCurrentLocation() {
const timeout = 10000;
try {
// 最初に現在の位置情報を取得を試みる
const currentPosition = await Promise.race([
Location.getCurrentPositionAsync(),
new Promise((_, reject) => setTimeout(() => reject(new Error(`Error getting GPS location after ${timeout / 1000} s`)), timeout))
]);
return currentPosition;
} catch (error) {
// 現在の位置情報の取得に失敗した場合、最後に知られている位置情報を試みる
try {
const lastKnownPosition = await Location.getLastKnownPositionAsync();
if (lastKnownPosition) {
return lastKnownPosition;
} else {
throw new Error('No known last position');
}
} catch (lastError) {
// 最後に知られている位置情報の取得も失敗した場合、エラーを返す
throw new Error(`Unable to get location: ${lastError.message}`);
}
}
}
let location = await getCurrentLocation()//位置情報を取得するとき
では、ここからこのコードの具体的な解説に入りますが、
とりあえず使ってみたいよという方は、まずは使ってみて動くかどうか試してみてからでも良いと思います。
コードの解説
①現在の位置情報の取得を試みる
まず、最初のステップとして、Location.getCurrentPositionAsync()を使用して、現在の位置情報を取得しようと試みます。
この処理は非同期で行われるため、結果が返るまでは次の処理は待機します。
②タイムアウトの設定
位置情報の取得に最大10秒(timeout変数で定義)まで待つことになります。
この時間が経過すると、Promise.raceによりタイムアウトを表すエラーが発生します。
Promise.raceは複数のプレミスのうち最初に解決または拒否されたものに基づいて結果が決まる関数です。
③エラーハンドリング
最初の位置情報取得の失敗
もし、最初の位置情報の取得がタイムアウトにより失敗するか、他の理由でエラーが発生した場合、catchブロックが実行されます。
④最後に知られている位置情報の取得
getLastKnownPositionAsync() の呼び出し
現在の位置情報が取得できなかった場合、Location.getLastKnownPositionAsync()を呼び出して、デバイスが最後に記録した位置情報を取得しようと試みます。
(今回処理が止まっているのは、現在地を取得した以降で止まっている可能性があります。私の環境で何回かテストはしてみましたが、位置情報は最新のものを取得できていました。)
ただ、可能性として情報が最新でない可能性はあります。ですが、位置情報が何も返ってこないよりかはマシです。
getLastKnownPositionAsync() の位置情報が最新でない可能性がある旨は以下公式ドキュメントにも記載。
参考:公式ドキュメント:場所 - Expo Documentation
⑤最終的なエラーハンドリング
最後に知られている位置情報の取得に失敗
もし、getLastKnownPositionAsync() も失敗するか、情報が得られない場合、再度エラーが投げられます。
今回は記載していませんが、ここでAlertモーダルを投げて再試行させるのも良いでしょう。
ここまでが、コードの解説になります。
ぜひ実際に使ってみてあなたの実装に合うかどうか試してみてください!
まとめ
今回は、Andoriodでexpo-locationを使用した、現在位置の取得をしようとした時に、処理が止まる対策を紹介しました。
早くこのexpo-locationが普通に使えるようになって欲しいですね。。
では、またどこかでお会いしましょう!