質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

Q&A

解決済

2回答

8407閲覧

【Xamarin.Forms】バックグラウンドで常に位置情報を送り続けたい【Android】

退会済みユーザー

退会済みユーザー

総合スコア0

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

0グッド

0クリップ

投稿2019/01/09 06:36

編集2019/01/10 05:40

#開発環境
Visual Studio 2017 Community 15.9.4
Xamarin.Forms 3.4.0.1009999
Xamarin.Forms.GoogleMaps 3.0.4
Xam.Plugin.Geolocator 4.5.0.6
#やりたいこと
Xamarin.Formsを用い、Androidアプリを開発しています。
常に位置情報を送りたいので、サービスを実装したいのですが上手く動いてくれません。
AndroidでOS起動時に自動実行するバックグラウンドサービスの作成方法
バックグラウンドサービスからDependencyServiceをコールする方法
上記リンクを参考に実装したのですが、情報が古いのでしょうか?
AndroidのバージョンはLolipop以降を想定しています。
今後iOSにも対応する予定の為、一部DependencyService等を使用しています。
上記リンク先にあったサンプルコードを実行してみましたが動作しませんでした。
#コード
実際に実装したコードは以下になります。

######BackgroundService.cs

C#

1namespace App.Droid 2{ 3 [Service(Name = "com.CompanyName.App.BackgroundService", Exported =true, Process =":Process")] 4 public class BackgroundService : Service 5 { 6 public override IBinder OnBind(Intent intent) 7 { 8 return null; 9 } 10 11 [return: GeneratedEnum] 12 public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId) 13 { 14 // Oreo対応 15 if (Build.VERSION.SdkInt >= BuildVersionCodes.O) 16 { 17 this.RegisterForegroundService(); 18 } 19 20 Thread t = new Thread(() => 21 { 22 var bundle = new Bundle(); 23 global::Xamarin.Forms.Forms.Init(this, bundle); 24 25 App.BackgroundThread.Main(); 26 }); 27 t.Start(); 28 29 return StartCommandResult.Sticky; 30 } 31 32 void RegisterForegroundService() 33 { 34 var notification = new Notification.Builder(this) 35 .SetContentTitle("アプリ") 36 .SetContentText("Start ForegroundService") 37 .SetSmallIcon(Resource.Mipmap.icon) // Android7.0対応 38 .SetColor(ActivityCompat.GetColor(Android.App.Application.Context, Resource.Color.notification_color)) // Android7.0対応 39 .SetOngoing(true) 40 .Build(); 41 42 this.StartForeground(99999, notification); 43 } 44 45 public void StartBackgroundService() 46 { 47 Intent serviceIntent = new Intent(this, typeof(BackgroundService)); 48 49 // Lollipop対応 50 if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop && 51 Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1) 52 { 53 string packageName = this.PackageManager.GetPackageInfo(this.PackageName, 0).PackageName; 54 serviceIntent.SetPackage(packageName); 55 } 56 else 57 { 58 serviceIntent.AddFlags(ActivityFlags.NewTask); 59 } 60 61 base.StartService(serviceIntent); 62 } 63 64 public override void OnDestroy() 65 { 66 base.OnDestroy(); 67 this.StartBackgroundService(); 68 } 69 } 70}

######BootReceiver.cs

C#

1namespace App.Droid 2{ 3 [BroadcastReceiver] 4 [IntentFilter(new[] { Intent.ActionBootCompleted, 5 "android.intent.action.QUICKBOOT_POWERON", 6 "com.htc.intent.action.QUICKBOOT_POWERON", 7 "android.intent.action.PACKAGE_INSTALL", 8 "android.intent.action.PACKAGE_ADDED", 9 Intent.ActionMyPackageReplaced 10 })] 11 public class BootReceiver : BroadcastReceiver 12 { 13 public BootReceiver() : base() 14 { 15 } 16 17 public override void OnReceive(Context context, Intent intent) 18 { 19 Intent serviceIntent = new Intent(context, typeof(BackgroundService)); 20 21 // Lollipop対応 22 if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop && 23 Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1) 24 { 25 string packageName = context.PackageManager.GetPackageInfo(context.PackageName, 0).PackageName; 26 serviceIntent.SetPackage(packageName); 27 serviceIntent.SetClassName(context, packageName + ".BackgroundService"); 28 } 29 else 30 { 31 serviceIntent.AddFlags(ActivityFlags.NewTask); 32 } 33 34 // Oreo対応 35 if (Build.VERSION.SdkInt >= BuildVersionCodes.O) 36 { 37 context.StartForegroundService(serviceIntent); 38 } 39 else 40 { 41 context.StartService(serviceIntent); 42 } 43 } 44 } 45}

######BackgroundThread.cs

C#

1namespace App 2{ 3 public static class BackgroundThread 4 { 5 public static async void Main() 6 { 7 DependencyService.Get<INotificationService>().Regist(); 8 DependencyService.Get<ILocationService>().Initialize(); 9 DependencyService.Get<ILocationService>().OnLocationChanged += new OnLocationChangedDelegate(GlobalClass.Instance.LocationChanged); 10 DependencyService.Get<ILocationService>().OnLocationError += new OnLocationErrorDelegate(GlobalClass.Instance.LocationError); 11 DependencyService.Get<ILocationService>().StartListening(10000, 10, true); 12 13 while (true) 14 { 15 Plugin.Geolocator.Abstractions.Position pos = await DependencyService.Get<ILocationService>().GetPositionAsync(1000); 16 17 // 送信処理 18 Communication.AddParam("Command", "pos"); 19 Communication.AddParam("Lat", pos.Latitude.ToString()); 20 Communication.AddParam("Lng", pos.Longitude.ToString()); 21 string response = await Communication.Send(); 22 23 DependencyService.Get<INotificationService>().On("通信結果", response); 24 25 await Task.Delay(30000); 26 } 27 } 28 } 29}

#試したこと
AndroidManifesto.xmlに以下の行を追加しました。

XML

1<application android:label="App.Droid"> 2 <service android:name="com.CompanyName.App.BackgroundService"/> 3 <receiver android:name="com.CompanyName.App.BootReceiver"> 4 <intent-filter> 5 <action android:name="android.intent.action.BOOT_COMPLETED"/> 6 <action android:name="android.intent.action.QUICKBOOT_POWERON"/> 7 <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> 8 <action android:name="android.intent.action.PACKAGE_INSTALL"/> 9 <action android:name="android.intent.action.PACKAGE_ADDED"/> 10 <action android:name="android.intent.action.MY_PACKGE_REPLACED"/> 11 </intent-filter> 12 </receiver> 13</application>

しかし動きませんでした。

1度位置情報の取得、送信部分のコードを削除し、それぞれの関数の1行目にConsole.WriteLineを追加し、出力を見てみました。
Console.WriteLine("OnStartCommand");
Console.WriteLine("RegisterForegroundService");
Console.WriteLine("StartBackgroundService");
Console.WriteLine("OnDestroy");
Console.WriteLine("OnReceive");
Console.WriteLine("Main");
「BackgroundThread.cs」のwhile文の中に下記行を追加しました。
Console.WriteLine("Main While");
結果としていずれも出力されませんでした。

その後、「MainActivity.cs」に下記の行を追加しました。
StartService(new Intent(this, typeof(BackgroundService)));
結果は「OnStartCommand」、「Main」、「Main While」のみ出力されました。

タスクをキルすると「Main While」の出力が止まってしまうため、目的とする動作ではありません。

どうすれば常にタスクを維持出来るでしょうか。
また、起動した瞬間やアプリを入れ替えた瞬間に動作を開始させたいのですが「BootReceiver.cs」の処理に入ってきません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

直接の回答ではありませんが、実現されたい(と思われる)機能ごとに述べます。
なお、いずれもネイティブのAndroidアプリ開発の、それもかなり難易度の高い部類に入りますので、Xamarin はほとんど関係ないです。

①端末の起動をBootReceiverで受信

↑は私が書いたものですが、この2014年時点でもそれより過去とは動作が変わっており、現在もそのまま使える保証はないです。

②BackgroundServiceを起動

自力で StartService を呼び出してサービスが開始・実行されることは確認できているようですが、
タスクキラーで殺されても 「常にタスクを維持」 し続けるサービスを作ることは不可能です。

サービスが死んだら再起動させる、ことは次の情報で可能なようです(ただし2014年の情報)。

あるいは定期的に「サービスが生きているか?」の通知を何らかの方法で送信し、死んでいたら起動させるという事を行わないといけないかも知れません。
そのためには例えばサーバーから定期的にプッシュ通知を送信し、端末でそれを受信した時にサービスの死活管理を行う必要があるかもしれません。

③位置を取得

GetPositionAsync() は、一回の位置情報を取得するものなので、while ループでこれを何度も呼び出すことは、その都度GPSの再起動が行われたりしてムダが多いです。StartListeningAsync() を使った方がよいでしょう。


①も②も通常のAndroid開発でもデバッグしづらく難しいので、本当にこれらが必要ならば、ネイティブの Java or Kotlin/Android でトライ&エラーしてから Xamarin に移植した方が早いのではないかと思います。

(なお、同じような事を iOS で実装しようとした場合、iOS 固有のテクニックが必要になります。)

投稿2019/01/10 06:23

amay077

総合スコア1075

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/01/11 06:51

この度は実現したいことの難しさについて教えて頂きありがとうございます。 今回のものはXamarinという指定があるためJava or Kotlin/Androidに変更することは出来ないのでとりあえず動いているStartServiceで代用しようと思います。
guest

0

タスク死んだときに復活するコードとして、サービスクラスに以下のような処理入れてみてはどうでしょうか。

C#

1 public override void OnTaskRemoved(Intent rootIntent) 2 { 3 base.OnTaskRemoved(rootIntent); 4 var intent = new Intent(this, typeof(BackgroundService)); 5 ((AlarmManager)GetSystemService(Context.AlarmService)).Set(AlarmType.Rtc, Java.Lang.JavaSystem.CurrentTimeMillis() + 500, PendingIntent.GetService(this, 11, intent, 0)); 6 }

雑談

amayさんが言うようにAndroidのバックグラウンド処理はいろいろと問題が多いです。
Androidのバージョンが上がるごとに制限が厳しくなっています。
「Dozeモード」などはしっかりと調べたほうがいいです。
位置情報を定期的に取りたいということですが、
バックグラウンド中はネットワークアクセスが使えない場合もあるので注意が必要です。

多くの中華端末(HUAWEIとか)は独自の電源管理機能が実装されていて、
そもそもバックグラウンド実行が許可されていない場合があります。
そういった端末はアプリごとにバックグラウンド実行の許可をする必要があります。

iOSにはAndroidのようなバックグラウンドサービスという概念がありませんが、
位置情報に関してはバックグラウンドでの取得も可能ですが、Androidほど柔軟なことはできません。
詳しくはマニュアルを読むことをお勧めします。
https://developer.apple.com/jp/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html

上記URLが死んでました。英語ですがこちらを。
https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html

投稿2019/01/10 10:44

編集2019/01/10 10:47
yamataka3

総合スコア145

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/01/11 06:54

どれをベストアンサーにするか迷いましたが実装の難しさを教えていただいた点を踏まえて別のものにしました。申し訳ありません。 電源管理についても調査不足でした、教えて頂きありがとうございます。 今回は実装が難しそうなのでとりあえず動いているStartServiceを使用したコートで行きたいと思います。 iOSのマニュアルも教えて頂きありがとうございます。今後実装する際に参考にしようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問