shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.Formsでバックグラウンド(非アクティブ時でも)で位置情報を取得し続ける(Android編)

Android8以上でも非アクティブな状態で処理を継続する手段としてForeground Serviceがある

これを使ってカウントアップを行う簡単なサンプルアプリを作った.

shuhelohelo.hatenablog.com

さて,バックグラウンドで数を1ずつ増やすタスクを実行しているわけですが,このタスクをGPSを取得するものに置き換えればよいということ.

引き続き,前回と同じリポジトリにブランチを足して変更を加える.

GetLocationというブランチにした.

github.com

位置情報の取得

Xamarin.Essentialsには位置情報を取得するAPIが用意されている.

docs.microsoft.com

それを使ってTaskCounterクラス内のRunCounterメソッドを書き換える.

        public async Task RunCounter(CancellationToken token)
        {
            //GPSの精度をHighに ←これと
            var request = new GeolocationRequest(GeolocationAccuracy.High);

            await Task.Run(async () =>
            {
                for (long i = 0; i < long.MaxValue; i++)
                {
                    token.ThrowIfCancellationRequested();

                    await Task.Delay(1000);

                    //ここから
                    //位置情報取得
                    var location = await Geolocation.GetLocationAsync(request);

                    var message = new TickedMessage
                    {
                        Message = $"Count : {i.ToString()}, Lat = {location.Latitude}, Lon = {location.Longitude}"
                    };
                    //ここまで

                    MainThread.BeginInvokeOnMainThread(() =>
                    {
                        MessagingCenter.Send<TickedMessage>(message, nameof(TickedMessage));
                    });
                }
            }, token);
        }

こんな感じで1秒毎に位置情報を取得するタスクが完成.

位置情報を拾う部分はXamarin.EssentialsのAPIを使って以下のように書く.

var request = new GeolocationRequest(GeolocationAccuracy.High);

var location = await Geolocation.GetLocationAsync(request);

位置情報取得の許可

位置情報の取得にはPermission(許可)が必要なので,これをユーザーに確認する処理も書く.

これもXamarin.Essentials(1.5以上)のPermissionsというAPIを使う.

docs.microsoft.com

バックグラウンドタスクが実行される前に,許可を得たいので,タスク開始ボタンが押されたときに許可を得る.

開始ボタンを押したときのイベントハンドラは以下のようになる.

        private async void Button_LongRunningTaskStart_Clicked(object sender, EventArgs e)
        {
            //位置情報取得の許可状態を確認
            var status = await Permissions.CheckStatusAsync<Permissions.LocationAlways>();
            if (status != PermissionStatus.Granted)
            {
                //許可されていなかった場合はユーザーに確認する
                status = await Permissions.RequestAsync<Permissions.LocationAlways>();
                //ユーザーが拒否した場合は(´・ω・`)
                if (status != PermissionStatus.Granted)
                    return;
            }

            var message = new StartLongRunningTaskMessage();
            MessagingCenter.Send(message, nameof(StartLongRunningTaskMessage));
        }

AndroidManifestにも以下の2つのPermissionにチェックをつけておく.

f:id:shuhelohelo:20200524213319p:plain

もしくはAndroidManifest.xmlに追加する.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.xfforegroundservicepractice" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
    <application android:label="XFForegroundServicePractice.Android"></application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

実行結果

開始ボタンを押すと,位置情報の取得に関する許可を求めるダイアログが表示されるので,「Allow all the time」を選択.

f:id:shuhelohelo:20200524213803p:plain

このように一定間隔で現在地の経緯度が更新されることが確認できる.

f:id:shuhelohelo:20200524214251p:plain

ちなみに,Xamarin.Essentialsで位置情報を取得するたびに,Androidの位置情報マークが点滅するので少し鬱陶しいかもしない.

他のアプリを利用中でも動き続けるので,ランニングや散歩などを記録するアプリなどに使える.

ソースコード

github.com

GetLocationブランチが今回のコード

注意

頻繁な位置情報の取得はバッテリーの消費が激しいので,取得間隔を適宜変更する必要がある.