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

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

ただいまの
回答率

90.47%

  • C#

    7447questions

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

  • Android

    6633questions

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

  • Xamarin

    519questions

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

  • Bluetooth

    240questions

    Bluetoothとは短距離の間でデータを交換するための無線通信規格である。固定・モバイル両方のデバイスから、短波の電波送信を行うことで、高いセキュリティをもつパーソナルエリアネットワーク(PAN)を構築する。

【Xamarin.Forms】AndroidにおけるBluetoothの実装(主にスレッド)

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 355

naritamago

score 7

 実現したいこと

Xamarin.FormsでAndroidスマホとBluetoothデバイスの接続をコーディングしています。
スマホ側に保存されているペアリング済デバイスを取得して情報を取得する程度までは出来たのですが、Bluetooth接続を確立して送られて来る値を表示・保存する部分を実装しようとして挫折しそうです。
各所でAndroid(Java)の実装を見て勉強していますが、BluetoothもAndroidも初心者なもので、わからなくなっています。
聞きたい事は主に以下の2点です。

  • スマホ側はサーバーの実装?(ListenUsingRfCommWithServiceRecord()を使う?)

  • Xamarin.FormsでAndroidのスレッドをどう扱えば良い?

チュートリアルとしてよく見るのはスマホ同士で片方をサーバー、片方をクライアントにして接続するものですが、今回接続したいのは画面の無いクラシックBluetooth(SPP)のセンサーです。その場合はセンサーから送られて来るデータを読み込むサーバーとして実装するのが正解でしょうか?
Bluetoothデバイス側の実装が公開されておらず、スマホからMacAddressとUUIDを指定して接続要求をすれば値が返って来るものなのかもよく分かっていませんが、PC向けの純正アプリでは接続され、値が取得できるのを確認しています。

また、MVVM(っぽい)実装を目指していて、ViewにはUI、ViewModelでViewに表示する値の取得、ModelでAndroid固有機能を呼び出すインターフェースの定義、Androidプロジェクトでインターフェースに定義した機能を実装しています。
Android固有機能の呼び出しはインターフェースを介してDependencyService呼び出してます(この解釈で合ってるのか怪しいですが…)
この場合、接続要求を待つ別スレッドはVMに書くのか、Android側に書くのとどちらが望ましいでしょうか?

 該当のソースコード

こちらの動画を参考にしています→Youtube
close処理などは省略しています。

public class BluetoothManager : IBluetoothManager
{
        //UUID:SPP
        private const string Uuid = "00001101-0000-1000-8000-00805f9b34fb";
        private BluetoothServerSocket mServerSocket;
        private BluetoothSocket mSocket;
        private BluetoothAdapter mAdapter;
        private BufferedReader reader;
        private System.IO.Stream mStream;
        private InputStreamReader mReader;

        //コンストラクタ
        public BluetoothManager()
        {
            mReader = null;
        }

        public string getDataFromDevice()
        {
            return reader.ReadLine();
        }

     private UUID getUUIDFromString()
        {
            return UUID.FromString(Uuid);
        }

        public void getAllPairedDevices()
        {
            BluetoothAdapter mAdapter = BluetoothAdapter.DefaultAdapter;
            BluetoothDevice devices = mAdapter.BondedDevices;
            if (devices != null && devices.Count > 0)
            {
                foreach (var device in devices)
                {
                    try
                    {
                        mServerSocket = mAdapter.ListenUsingRfcommWithServiceRecord(NAME, getUUIDFromString());
                    }
                    catch (IOException e)
                    {
                        System.Console.WriteLine(e);
                    }
                    try
                    {
              //ここからがよくわからない
                        mSocket = mServerSocket.Accept();
                    }catch(IOException e)
                    {
                        System.Console.WriteLine(e);
                        break;
                    }
                    if(mSocket!=null)
                    {
                        System.Console.WriteLine("connected!");
                    }
                    else{
                        System.Console.WriteLine("not connected");
                    }
                    //mStream = mSocket.InputStream;
                    //System.Console.WriteLine("#debug location:getInputStream");
                    ////Readerインスタンスの生成
                    //mReader = new InputStreamReader(mStream);
                    //System.Console.WriteLine("#debug location:getStreamReader");
                    ////stringで読めるようにBuffered型に変換
                    //reader = new BufferedReader(mReader);
                    ////if(mSocket == null)
                    ////openDeviceConnection(device);
                }
            }
        }
    }
}
public BTViewModel()
        {
            IBluetoothManager btMan = DependencyService.Get<IBluetoothManager>();
            btMan.getAllPairedDevices(); //ペアリング済デバイスのリストを取得

       //参考にしたサイトのコピペです。
            System.Threading.Thread thread = new System.Threading.Thread(() =>
            {
                while(true)
                {
                    data = btMan.getDataFromDevice();
                    Console.WriteLine("#debug {0}", data);
                }
            });
            thread.IsBackground = true;
            thread.Start();
       //コピペここまで
        }

 試したこと

スレッドの理解が及ばないままmSocket = mServerSocket.Accept();を動かそうとしていましたが、ドキュメントを読むとAccept()は別スレッドでコールバックを受け付けないといけないとのことで、挫折しました。

 追記(9/11)

f-miyuさんのご回答を参考に動かしてみましたが、機器の接続のところでIOExceptionが発生(Connect()メソッドがタイムアウトする)し、スレッドに関する動きを確認できていません。
ソース全体はgithubにあげています。問題が発生しているのは以下のメソッドです。

private void openDeviceConnection(BluetoothDevice btDevice) 
{ 
    try 
    { 
         mSocket = btDevice.CreateRfcommSocketToServiceRecord(getUUIDFromString()); 
         System.Console.WriteLine("#debug get socket [{0}]", btDevice.Name); 
        mSocket.Connect(); 
        System.Console.WriteLine("#debug connect[{0}]", btDevice.Name); 
        //input stream 
        mStream = mSocket.InputStream; 
        System.Console.WriteLine("#debug open InputStream [{0}]", btDevice.Name); 
        mReader = new InputStreamReader(mStream); 
        System.Console.WriteLine("#debug get StreamReader [{0}]", btDevice.Name); 
        reader = new BufferedReader(mReader); 
        System.Console.WriteLine("#debug get get BufferedReader [{0}]", btDevice.Name); 
     } 
     catch(IOException e) 
     { 
         close(mSocket); 
         close(mStream); 
         close(mReader); 
         e.PrintStackTrace(); 
         throw e;           
     } 
} 


btDeviceはペアリング済みデバイスの情報が格納されています。

CreateRfcommSocketToServiceRecored()の引数として読んでいるgetUUIDFromStrng()は、String型のローカル変数で定義しているUUIDをUUID型に変換して渡しているだけです。

9行目のConnect()メソッドがタイムアウトし、IOExceptionが帰ってきてしまいます。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+3

スマホ側はサーバーの実装?

送られくるものを読み込むだけなので、サーバーではなく、クライアントでいいと思います。

Xamarin.FormsでAndroidのスレッドをどう扱えば良い?

Android側にスレッドを書いた方がいいと思います。
ViewModel側では、データを受け取るイベントを購読するようにします。

Android

public class DataEventArgs : EventArgs
{
    public string Data { get; }

    public DataEventArgs(string data)
    {
        Data = data;
    }
}

public class BluetoothManager : IBluetoothManager
{
    public event EventHandler<DataEventArgs> DataReceived;

    public void Start()
    {
        var btAdapter = BluetoothAdapter.DefaultAdapter;
        var devices = btAdapter.BondedDevices;
        if (devices != null && devices.Count > 0)
        {
            foreach (var device in devices)
            {
                try
                {
                    var socket = device.CreateRfcommSocketToServiceRecord(getUUIDFromString());
                    Task.Run(() =>
                    {
                        try
                        {
                            socket.Connect();

                            var br = new BufferedReader(new InputStreamReader(socket.InputStream));

                            string data;
                            while ((data = br.ReadLine()) != null)
                            {
                                DataReceived?.Invoke(this, new DataEventArgs(data));
                            }
                        }
                        catch (IOException e)
                        {
                        }
                        finally
                        {
                            socket.Close();
                        }
                    });
                }
                catch (IOException e)
                {
                }
            }
        }
    }
}

ViewModel

btMan.DataReceived += (sender, e) => 
{
    Console.WriteLine("#debug {0}", e.Data);
};

btMan.Start();

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/09/11 00:41

    f-miyuさん、知りたいことへの回答ありがとうございます。サーバーではなくクライアント側の実装に変更したのですが、 CreateRfcomm~で作成したソケットのConnect()メソッドを呼んだところでIOExceptionが発生し、接続出来ずスレッドの動作を確認するまでに至れませんでした。SPPのセンサーだけでなくゲームコントローラーやマウスなどいろいろな機器の接続も試している(UUIDプロパティの値を変えてます)のですが、今のところ全滅です。コードが悪いのか・機器が悪いのか、問題の切り分けに有効な手段があればご教示いただけないでしょうか?

    キャンセル

  • 2018/09/11 01:02

    試したコードを載せてもらえますか?

    キャンセル

  • 2018/09/11 08:24

    githubにコード載せました→https://github.com/johta/XF-BT
    時間ができたら追記にも該当部分のコードを記載します。

    キャンセル

  • 2018/09/11 10:32

    githubを見ると、Uuidが違うと思います。
    SPPなら、
    00001101-0000-1000-8000-00805F9B34FB
    です。

    キャンセル

  • 2018/09/11 10:40

    SPPのUUIDは仰るとおりなんですが、色々な機器の接続を試していたので、最後に試したUUIDでそのままpushしちゃいました。たしかゲームコントローラだったと思います。

    キャンセル

  • 2018/09/11 10:59

    参考にされたコードがあまりよろしくないので、修正したStartメソッドを試してみてください。

    キャンセル

  • 2018/09/11 11:28 編集

    提示していただいたコードでSPPセンサーに繋げて動かすことができました!!本当にありがとうございます!!!
    やはり、スレッドの理解が肝要だったのですね。わかりやすいコードを提示していただき、ありがとうございました。

    キャンセル

0

Rxでやるのが良さそう。

https://blog.okazuki.jp/entry/2017/01/25/183152

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

関連した質問

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

  • C#

    7447questions

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

  • Android

    6633questions

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

  • Xamarin

    519questions

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

  • Bluetooth

    240questions

    Bluetoothとは短距離の間でデータを交換するための無線通信規格である。固定・モバイル両方のデバイスから、短波の電波送信を行うことで、高いセキュリティをもつパーソナルエリアネットワーク(PAN)を構築する。