LAN内通信アプリを作ろう その4 JavaFXのコントロールに情報を設定する/スレッドからJavaFXの画面を呼び出す
仮眠(5時間)も取ったので作業を再開しましょう。
LAN内通信アプリは今日で終わりなのでササッと(出来ても出来なくても)
- メッセージ部分の受信
- 受信したメッセージの表示(受信ウィンドウの作成)
受信処理の改修からやっていきます。
受信処理の改修
- 受信メッセージの表示ウィンドウを作成する。
- メッセージを安定して受け取れるようにする。
受信メッセージの表示ウィンドウを作成する。
まずはこれ。
JavaFXを利用して受信したメッセージを表示する画面を作成。
送信ウィンドウを流用して作ったのがこちら。
受信時にウィンドウを呼び出す。
ここで詰まった。
今回のアプリケーションでは「メッセージを受信したときに受信メッセージを新規ウィンドウで表示する」というイメージで作成していて
「受信用の待ち受けスレッドを用意してポートを監視している」作りになっている。
最初は「受信用の待ち受けスレッドで画面を作成して表示すればいいか」と思っていたものの
javafx.stage.Stageのインスタンスを作ってshow()メソッドを呼ぶとIlegalStateExceptionを出してくる。
↓こんな感じ
(受信処理)
Stage stage = new Stage();
stage.show();
↓結果がこちら
このExceptionはなんぞや?と思って調べてみると、こんな事実に行き当たった。
曰く
「JavaFXの指定したスレッド(JavaFX application thread)以外からJavaFXの要素を操作しようとすると上記例外が投げられる」
で、回避方法を調べてみたところ、javaFX.platformなるクラスが存在することが判明。
Oracle - JavaFXドキュメンテーション クラスplatform
(日本語)
このクラスのrunLater()メソッドにrunnnableなスレッドを登録してやることでJavaFX applicationが代替実行してくれるそうな。
あ、formみたいにnewしてshow()!とは行かないのね。
さっそく調べた内容を元にStageを表示する処理を
書き換えて動作確認。
↓こんな感じに変更
(受信処理)
Platform.runLater( () -> {
Stage stage = new Stage();
stage.show();
});
↓結果がこちら(真っ白なウィンドウがnewしたStage。サイズ変更してるけど最初はかなり大きい)
…えー、この事実を受けて
画面描画の構成を変える必要が鎌首をもたげたわけですね。(ため息)
今の作りだとあまりにも汚いので・・・
直しました(1時間使って)。
ざっくりこんな感じの作りに修正
- Stage関連処理
- 画面毎のFXMLファイル
- 画面毎のStage取得用クラス
- 画面毎のControllerクラス
- 送受信処理
- ソケット通信を利用してデータを送信するスレッド継承クラス
- 受信ポートを監視し、データを受信するスレッド継承クラス
- 通常処理
- Platform.runLator()にStageの描画処理を行わせるための汎用スレッド継承クラス
- エントリポイントを含むクラス
- GUIに紐付けているコントローラークラスに、コントロールを操作するための処理を用意する
- コントローラのインスタンスを取得して用意した操作メソッドを呼び出す
- コントローラのインスタンスはFXMLLoaderクラスのgetController()メソッドを利用する。 (Object型で返却されるので、コントローラに指定してるクラスでキャストすること)
- 上記インスタンスはFXMLLoader.load()メソッドを実行した段階で生成される。 (これより早いタイミングで触ろうとするとnull参照で怒られる。ガッ!)
- バッファ領域とって
- 取得したデータ量を更新して
- 取得したデータは文字列バッファに格納していく
上記作り変えを行ったことで、
[メッセージの受信を確認] => [受信メッセージStageを取得] => [Applicationスレッドに描画タスクを依頼] => [Stageの描画]
という処理が出来るようになりました。
次はこのStageに受信したメッセージを表示させたいと思います。
JavaFxを用いて表示したGUIのコントローラに値を設定する
ここもがっつり詰まりました。
しかもnull参照で。
時間が・・時間が・・・(焦燥)
設定の仕方はこんな手順で実現できます。
書いてしまえばなんと簡単に見えることか
GUIに紐付けているコントローラークラスに、コントロールを操作するための処理を用意する
これは簡単。普通のセッターを作ってあげればOK。
コントロールをオブジェクトとして扱う方法はLAN内通信アプリを作ろう その3の「IDを設定したコントロールと同一の型、名前を持つオブジェクトをクラス内で宣言する。」の項目を参照されたし。
コントローラのインスタンスを取得して用意した操作メソッドを呼び出す
これ。これでガッツリはまりました。
ポイントは以下の通り。
で、この処理のためにfxmlLoaderの実態を持っているコントローラでgetControl()の値を返却するゲッターを作り
それをStage描画用スレッドで受け取ってそのまま返却するラッパーを作って・・・と細かいことを延々と・・・
その甲斐あって、取得したメッセージを表示するまでは成功
メッセージを安定して受け取れるようにする。
ここで大変悲しいお知らせです。
えー、昨日の自分はなんか意識が朦朧としてたので(言い訳)
無駄な処理を入れてメッセージの待ち受けを行ってました。
ので、無駄な処理を省いてこんな感じに修正
while (length >= 0) {
byte[] buffer = new byte[1024];
length = dis.read(buffer);
sBuffer.append(new String(buffer));
}
って処理ですね。
バッファサイズはlengthの値でもよかったかなー。
ソケット通信の場合、取得する情報が無くなると-1を返却してくるので
取る情報が無くなったら抜けてくれるわけですね。
今のところあんま問題ないのでこれ放置で。
本当は最初に送信するデータの量とかを相手方に伝えたりするんですが(伝送中の損失に備えるため)
まあ、今回はLAN内だしね。
暗号化もナシ。
端末間で通信してみる。
さて、とりあえず一通り実装した(ハズ)なので、LAN内の端末間で通信してみましょ。
ひとまず開発マシン(windows 7)と営業用のノートパソコン(windows 10)での結果がこんな感じ
(携帯のカメラで撮影したので非常に重い写真。開発した本体(ライブラリ込み)の70倍超)
で、悲しいかなLinuxで起動しようとしたら怒られました。
軽く調べてみたところ、なんかsun.*系のライブラリがどーたらこーたらなのでそこはもうちょっと調査が必要か。
せっかくJavaで組んだんだから多種のOSに対応したいものね。
とりあえず目的は達成?したので今回はこれまで。
直接IP打ってたりで少し使いづらいのはアレだけど。そこは改善点として・・・別機能だしね(開き直り)
取得したアドレスに/とか入ってるのが気になるところ。
追記:JavaFXのライブラリが入ってなかったっぽい・・・あれ?1.8だともう標準じゃなかったっけ・・・?
openjfxを導入したら普通に起動しました。
通信結果はこんな感じ。(VNCで表示してます。)
最終日の稼動は07:41:54で終了。
全体で23:03:51。
来週はどうなるかなー?
あ、挙げ忘れてたけど、GitHubはこちら
GitHub - PrivateMessenger
https://github.com/Shiratori1218/PrivateMessenger
Author And Source
この問題について(LAN内通信アプリを作ろう その4 JavaFXのコントロールに情報を設定する/スレッドからJavaFXの画面を呼び出す), 我々は、より多くの情報をここで見つけました https://qiita.com/Shiratori/items/7febeb43bdcbcdba6508著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .