実現したいこと
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処理などは省略しています。
Android
1public class BluetoothManager : IBluetoothManager 2{ 3 //UUID:SPP 4 private const string Uuid = "00001101-0000-1000-8000-00805f9b34fb"; 5 private BluetoothServerSocket mServerSocket; 6 private BluetoothSocket mSocket; 7 private BluetoothAdapter mAdapter; 8 private BufferedReader reader; 9 private System.IO.Stream mStream; 10 private InputStreamReader mReader; 11 12 //コンストラクタ 13 public BluetoothManager() 14 { 15 mReader = null; 16 } 17 18 public string getDataFromDevice() 19 { 20 return reader.ReadLine(); 21 } 22 23 private UUID getUUIDFromString() 24 { 25 return UUID.FromString(Uuid); 26 } 27 28 public void getAllPairedDevices() 29 { 30 BluetoothAdapter mAdapter = BluetoothAdapter.DefaultAdapter; 31 BluetoothDevice devices = mAdapter.BondedDevices; 32 if (devices != null && devices.Count > 0) 33 { 34 foreach (var device in devices) 35 { 36 try 37 { 38 mServerSocket = mAdapter.ListenUsingRfcommWithServiceRecord(NAME, getUUIDFromString()); 39 } 40 catch (IOException e) 41 { 42 System.Console.WriteLine(e); 43 } 44 try 45 { 46 //ここからがよくわからない 47 mSocket = mServerSocket.Accept(); 48 }catch(IOException e) 49 { 50 System.Console.WriteLine(e); 51 break; 52 } 53 if(mSocket!=null) 54 { 55 System.Console.WriteLine("connected!"); 56 } 57 else{ 58 System.Console.WriteLine("not connected"); 59 } 60 //mStream = mSocket.InputStream; 61 //System.Console.WriteLine("#debug location:getInputStream"); 62 ////Readerインスタンスの生成 63 //mReader = new InputStreamReader(mStream); 64 //System.Console.WriteLine("#debug location:getStreamReader"); 65 ////stringで読めるようにBuffered型に変換 66 //reader = new BufferedReader(mReader); 67 ////if(mSocket == null) 68 ////openDeviceConnection(device); 69 } 70 } 71 } 72 } 73} 74
ViewModel
1public BTViewModel() 2 { 3 IBluetoothManager btMan = DependencyService.Get<IBluetoothManager>(); 4 btMan.getAllPairedDevices(); //ペアリング済デバイスのリストを取得 5 6 //参考にしたサイトのコピペです。 7 System.Threading.Thread thread = new System.Threading.Thread(() => 8 { 9 while(true) 10 { 11 data = btMan.getDataFromDevice(); 12 Console.WriteLine("#debug {0}", data); 13 } 14 }); 15 thread.IsBackground = true; 16 thread.Start(); 17 //コピペここまで 18 }
試したこと
スレッドの理解が及ばないままmSocket = mServerSocket.Accept();
を動かそうとしていましたが、ドキュメントを読むとAccept()は別スレッドでコールバックを受け付けないといけないとのことで、挫折しました。
追記(9/11)
f-miyuさんのご回答を参考に動かしてみましたが、機器の接続のところでIOExceptionが発生(Connect()メソッドがタイムアウトする)し、スレッドに関する動きを確認できていません。
ソース全体はgithubにあげています。問題が発生しているのは以下のメソッドです。
BluetoothManager
1private void openDeviceConnection(BluetoothDevice btDevice) 2{ 3 try 4 { 5 mSocket = btDevice.CreateRfcommSocketToServiceRecord(getUUIDFromString()); 6 System.Console.WriteLine("#debug get socket [{0}]", btDevice.Name); 7 mSocket.Connect(); 8 System.Console.WriteLine("#debug connect[{0}]", btDevice.Name); 9 //input stream 10 mStream = mSocket.InputStream; 11 System.Console.WriteLine("#debug open InputStream [{0}]", btDevice.Name); 12 mReader = new InputStreamReader(mStream); 13 System.Console.WriteLine("#debug get StreamReader [{0}]", btDevice.Name); 14 reader = new BufferedReader(mReader); 15 System.Console.WriteLine("#debug get get BufferedReader [{0}]", btDevice.Name); 16 } 17 catch(IOException e) 18 { 19 close(mSocket); 20 close(mStream); 21 close(mReader); 22 e.PrintStackTrace(); 23 throw e; 24 } 25}
btDevice
はペアリング済みデバイスの情報が格納されています。
CreateRfcommSocketToServiceRecored()
の引数として読んでいるgetUUIDFromStrng()
は、String型のローカル変数で定義しているUUIDをUUID型に変換して渡しているだけです。
9行目のConnect()
メソッドがタイムアウトし、IOExceptionが帰ってきてしまいます。

回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/09/10 15:41
2018/09/10 16:02
2018/09/10 23:24
2018/09/11 01:32
2018/09/11 01:40
2018/09/11 01:59
2018/09/11 04:46 編集