☎️

[MAUI Blazor] Android で他アプリから受けたデータを画面に表示する

2023/10/26に公開

MAUI Blazor

誰やねん?というかんじですが、この子はC#(Web技術)でマルチプラットフォーム開発ができちゃうフレームワーク。Xamarin の後継ポジの子です
今回はこちらを使って Android 開発を業務ですすめることになりました
本記事は、結構詰まった部分の備忘録としています

やりたい処理

本記事は実際に稼働させる処理よりも簡潔に考えていくことにします
というわけで、ざっくり今回目標とする処理は下記のようなかんじ

  • 他のアプリからデータを受信する
  • データを受信したら画面に表示する

これだけを考えていきます

処理を分解

この2つの処理について、3ステップに分けて考えます

  1. ブロードキャストを使って値(インテント)を受信したら、ハンドラに渡す
  2. ハンドラが値を受け取ったら、画面(メイン)に渡す
  3. 画面が値を受け取ったら、画面に値を表示する

ブロードキャスト(BroadcastReceiver)

Android オペレーティング システムまたはアプリケーションによってブロードキャストされるメッセージ (AndroidIntent) にアプリケーションが応答できるようにする Android コンポーネント

https://learn.microsoft.com/ja-jp/xamarin/android/app-fundamentals/broadcast-receivers

インテント(Intent)

Intent は、別のアプリ コンポーネントからのアクションをリクエストするときに使用できるメッセージング オブジェクト

https://developer.android.com/guide/components/intents-filters?hl=ja

ハンドラ(Handler)

クライアント要求を分析し、クライアントから渡されたパラメーターを抽出し、サービスで適切なメソッドを呼び出す役割を担います

https://learn.microsoft.com/ja-jp/xamarin/android/app-fundamentals/services/out-of-process-services#implementing-a-handler

実装

1. ブロードキャストを使って値(インテント)を受信したら、ハンドラに渡す

MyReceiver.cs に、BroadcastReceiver をサブクラスとして下記のように記述します
ブロードキャスト受信時の関数名は OnReceive とする必要があります

using Android.Content;

namespace _Test
{
    [BroadcastReceiver]
    public class MyReceiver : BroadcastReceiver
    {
        private readonly MyHandler _handler;

        /// <summary>
        /// デフォルトコンストラクタ
        /// </summary>
        public MyReceiver()
        {
            // 中身は空
        }
        
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="handler">MyHandlerのインスタンス</param>
        public MyReceiver(MyHandler handler)
        {
            _handler = handler;
        }

        /// <summary>
        /// 他アプリとの連携
        /// ブロードキャストが受信されたときに呼ばれる
        /// </summary>
        /// <param name="context">ブロードキャストを受信したコンテキスト</param>
        /// <param name="intent">受信したブロードキャストに関する情報</param>
        public override void OnReceive(Context context, Intent intent)
        {
            //// データを受信
            string data = intent.GetStringExtra("text");
            
            // ハンドラを使用してUIスレッドにデータを送信する 
            var message = _handler.ObtainMessage(MyHandler.UPDATE_UI, data);
            _handler.SendMessage(message);
        }
    }
}

2. ハンドラが値を受け取ったら、画面(メイン)に渡す

MyHandler.cs に、Handler をサブクラスとして下記のように記述します

using Android.OS;

namespace _Test
{
    public class MyHandler : Handler
    {
        // メッセージの種類を示す定数
        public const int UPDATE_UI = 1;

        // UI更新のコールバックを保持するプライベート変数
        private readonly IUpdateUiCallback _callback;

        /// <summary>
        /// UI更新のコールバックを定義するインターフェース
        /// </summary>
        public interface IUpdateUiCallback
        {
            void UpdateUi(string newValue); // 新しい値を受け取ってUIを更新するメソッド
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="callback">UI更新のコールバック</param>
        public MyHandler(IUpdateUiCallback callback)
        {
            // UI更新のコールバックを保持
            _callback = callback;
        }

        /// <summary>
        /// メッセージを処理するメソッド
        /// </summary>
        /// <param name="message">OnReceive関数から受け取ったメッセージオブジェクト</param>
        public override void HandleMessage(Message message)
        {
            // メッセージの種類によって処理を分岐できる
            switch (message.What)
            {
                case UPDATE_UI:
                    string newValue = (string)message.Obj;   // メッセージオブジェクトから値を取得
                    _callback.UpdateUi(newValue);            // UI更新のコールバックを呼び出す
                    break;
            }
        }

    }
}

3. 画面が値を受け取ったら、画面に値を表示する

画面

超簡潔!Index.razor に下記を記述します

@page "/"

<div>@test</div>

コードビハインドクラス

Index.razor.cs
初期処理 OnInitialized にて、バックグラウンドで BroadcastReceiver を起動するように命令をします
MyHandler で宣言した IUpdateUiCallback をサブクラスとします

using Microsoft.AspNetCore.Components;
using Android.Media;
using Android.Content;

namespace _Test.Pages
{
    public partial class Index: MyHandler.IUpdateUiCallback
    {
        private MyReceiver _receiver;
        private MyHandler _handler;

        private string test = "";


        /// <summary>
        /// 初期設定
        /// </summary>
        protected override void OnInitialized()
        {
            base.OnInitialized();

            // MyHandlerおよびMyReceiverの初期化
            _handler = new MyHandler(this);
            _receiver = new MyReceiver(_handler);

            // Androidのアプリケーションコンテキストを取得し、MyReceiverを登録
            var context = Android.App.Application.Context;
            context.RegisterReceiver(_receiver, new IntentFilter("hoge.intent.data"));
        }

        /// <summary>
        /// MyHandler.IUpdateUiCallback インターフェースの実装
        /// バックグラウンドスレッドから呼ばれ、UIを更新する
        /// </summary>
        /// <param name="value">HandleMessageから受け取ったnewValue</param>
        public void UpdateUi(string value)
        {
            test = value;
            StateHasChanged();
        }
    }
}

これで実装できました

感想など

MAUI Blazor 自体、英語記事でさえも少ない発展途上のフレームワークと認識しています
本案件を始めたときはまだ React も覚えられておらず、「自分ができる言語で Android 開発…」と考えた時に今回のフレームワークにたどり着きました

そのため ChatGPT を使って疑問を解消することも多いですが、AIちゃんにはまず Java でソースを考えてもらって、その後 Xamarin や MAUI Blazor に書き換えてもらうやり方をしています

Android 開発、結構奥が深く初心者には難しです(´・ω・`)
Web開発しかしたことなかったので、ものすごく頭を悩ませている毎日です
とはいえ経験値がっぽがっぽでたのしいね!(ポジティブ)

でも次 Android 開発するなら React Native を使ってみたいなと思ったり(興味本位)

Discussion