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

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

ただいまの
回答率

90.33%

【Xamarin.Forms】Firebase Cloud Messagingが動作しない【Android】

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 646

VEX

score 88

開発環境

Visual Studio 2017 Community 15.9.4
Xamarin.Forms 3.4.0.1009999
Xamarin.GooglePlayServices.Base 60.1142.1
Xamarin.GooglePlayServices.Tasks 60.1142.1
Xamarin.Firebase.Messaging 60.1142.1
Xamarin.Forms.GoogleMaps 3.0.4
Xam.Plugin.Geolocator 4.5.0.6

やりたいこと

Firebase Cloud Messagingを用い、Android端末に通知を出したい。

コード

MainActivity.cs

namespace App.Droid
{
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        static readonly string TAG = "MainActivity";

        internal static readonly string CHANNEL_ID = "my_notification_channel";
        internal static readonly int NOTIFICATION_ID = 100;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);
            IsPlayServicesAvailable();

            if (Intent.Extras != null)
            {
                foreach (var key in Intent.Extras.KeySet())
                {
                    var value = Intent.Extras.GetString(key);
                    Console.WriteLine("Key: " + key + " Value: " + value);
                }
            }

            CreateNotificationChannel();

            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            Xamarin.FormsGoogleMaps.Init(this, savedInstanceState);
            Xamarin.FormsGoogleMapsBindings.Init();
            Firebase.FirebaseApp.InitializeApp(this);

            LoadApplication(new App());
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
        {
            PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

        public bool IsPlayServicesAvailable()
        {
            int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
            if (resultCode != ConnectionResult.Success)
            {
                if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
                {
                    Console.WriteLine(GoogleApiAvailability.Instance.GetErrorString(resultCode));
                }
                else
                {
                    Console.WriteLine("このデバイスはサポートされていません。");
                    Finish();
                }
                return false;
            }
            else
            {
                Console.WriteLine("Google Play Serviceが使用できます。");
                return true;
            }
        }

        void CreateNotificationChannel()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.O)
            {
                return;
            }

            var channel = new NotificationChannel(CHANNEL_ID,
                                                  "FCM Notification",
                                                  NotificationImportance.Default)
                {
                    Description = "Firebase Cloud Messages appear in this channel"
                };

            var notificationManager = (NotificationManager)GetSystemService(Android.Content.Context.NotificationService);
            notificationManager.CreateNotificationChannel(channel);
        }
    }
}


MyFirebaseIIdService.cs

namespace App.Droid
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class MyFirebaseIIdService : FirebaseInstanceIdService
    {
        public override void OnTokenRefresh()
        {
            var refreshedToken = FirebaseInstanceId.Instance.Token;
            SendRegistrationToServer(refreshedToken);
        }

        async void SendRegistrationToServer(string token)
        {
            // サーバーへの送信処理
        }
    }
}


MyFirebaseMessagingService.cs

namespace App.Droid
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    class MyFirebaseMessagingService : FirebaseMessagingService
    {
        public override void OnMessageReceived(RemoteMessage message)
        {
            SendNotification(message.GetNotification().Body);
        }

        void SendNotification(string messageBody)
        {
            var intent = new Intent(this, typeof(MainActivity));
            intent.AddFlags(ActivityFlags.ClearTop);
            var pendingIntent = PendingIntent.GetActivity(this, 999 /* Request code */, intent, PendingIntentFlags.OneShot);

            var defaultSoundUri = RingtoneManager.GetDefaultUri(RingtoneType.Notification);
            var notificationBuilder = new NotificationCompat.Builder(this).SetSmallIcon(Resource.Mipmap.icon)
                .SetContentTitle("アプリ")
                .SetContentText(messageBody)
                .SetAutoCancel(true)
                .SetSound(defaultSoundUri)
                .SetContentIntent(pendingIntent);

            var notificationManager = NotificationManager.FromContext(this);

            notificationManager.Notify(999 /* ID of notification */, notificationBuilder.Build());
        }
    }
}


AndroidManifesto.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.App" android:installLocation="internalOnly">
    <uses-sdk android:minSdkVersion="20" android:targetSdkVersion="27" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <!-- OpenGL ESの設定 -->
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
    <application android:label="App.Android">
    <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
    <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="${applicationId}" />
      </intent-filter>
    </receiver>
    </application>
</manifest>

試したこと

Android側のlayoutに以下を追加し、MainActivityのコードを変更してみました。
Main.axml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">
    <TextView
        android:text=" "
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/msgText"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:padding="10dp" />
    <Button
      android:id="@+id/logTokenButton"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_gravity="center_horizontal"
      android:text="Log Token" />
</LinearLayout>


MainActivity.cs

namespace App.Droid
{
    [Activity(Label = "App", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, ScreenOrientation = ScreenOrientation.Portrait)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        static readonly string TAG = "MainActivity";

        internal static readonly string CHANNEL_ID = "my_notification_channel";
        internal static readonly int NOTIFICATION_ID = 100;

        TextView msgText;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);

            SetContentView(Resource.Layout.Main);
            msgText = FindViewById<TextView>(Resource.Id.msgText);

            IsPlayServicesAvailable();

            if (Intent.Extras != null)
            {
                foreach (var key in Intent.Extras.KeySet())
                {
                    var value = Intent.Extras.GetString(key);
                    Console.WriteLine("Key: " + key + " Value: " + value);
                }
            }

            CreateNotificationChannel();

            Plugin.CurrentActivity.CrossCurrentActivity.Current.Init(this, savedInstanceState);

            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            Xamarin.FormsGoogleMaps.Init(this, savedInstanceState);
            Xamarin.FormsGoogleMapsBindings.Init();
            Firebase.FirebaseApp.InitializeApp(this);

            var logTokenButton = FindViewById<Button>(Resource.Id.logTokenButton);
            logTokenButton.Click += delegate
            {
                Android.Util.Log.Debug(TAG, "InstanceID token: " + FirebaseInstanceId.Instance.Token);
            };
        }
    }
}


このように変更すると動作することが確認できました。

知りたいこと

Firebase Cloud MessagingはAndroidのコードのみで書かないと動作しないのでしょうか?
iOSと一部共有したいので、Xamarin.FormsのAndroid、iOSを使用しているのですが、これはまずいのでしょうか?

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

どこが動作しないのでしょうか?
同じように書いて試してみましたが、バックグランドでは、きちんと通知を受け取れました。
フォアグランドの場合は、チャンネルを設定していないので、確かにうまく動かないかもです。
MyFirebaseMessagingServiceSendNotificationを以下のように修正すればいいです。

var notificationBuilder = new NotificationCompat.Builder(this).SetSmallIcon(Resource.Mipmap.icon)
    .SetContentTitle("アプリ")
    .SetContentText(messageBody)
    .SetAutoCancel(true)
    .SetSound(defaultSoundUri)
    .SetContentIntent(pendingIntent)
    .SetChannelId(MainActivity.CHANNEL_ID);

ちなみに、Plugin.FirebasePushNotificationというライブラリを利用すれば、通知を受け取った時の処理を共通化できるので便利ですよ。参考までに。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/11 20:13

    回答ありがとうございます。
    開発環境に書き忘れてしまいましたが当方Android 5.0.2 の端末で動作させております。

    試したことに書いたようにプロジェクトをAndroid内のみに留める(言い方がわからず書いています、申し訳ありません)ようにするとしっかり通知を受け取れます。
    これはフォアグラウンド、バックグラウンドに関わりません(ここも既におかしい気がします)。

    コードに書いたようにLoadApplicationでAppを呼んでそちらの画面の処理を走らせると通知が動作しなくなります。

    そのためXamarin.Formsで共通コードを使用するとサービスが動かなくなるのではないかと考えておりますが、f-miyu様の環境では動いているとなれば違いますね……。
    ちなみに他のサービスも作成していますが、何故か自動起動しません(BroadcastReceiverを使用して呼び出すようにしているのですが引っかかりません)。
    そちらはStartServiceで呼び出すことで動かしています。

    全体的にサービス周りが怪しい気がするのですが今わかっているのはこれだけになります。

    キャンセル

  • 2019/01/11 20:42

    Xamarin.Formsを使用したからといって、サービスが動かなくなるということはないと思います。
    なんか他にも処理を色々やられているようですが、原因を特定するために、特に何も処理をしないシンプルな画面で通知の処理のみを入れて試してみてはいかがでしょうか?

    キャンセル

  • 2019/01/12 15:24

    一度全て処理を行わないでページの表示のみを行ったら動作するようになりました。
    その後処理を入れると問題なく動作しました。
    ただしある程度新規動作を追加するとまた動作しなくなるようでした。
    この場合も一旦全ての処理を行わないようにすることで動作するように出来ました。
    とても不安定ですがなんとか動いているのでこのまま続けたいと思います。
    ありがとうございました。

    キャンセル

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

  • ただいまの回答率 90.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る