Help us understand the problem. What is going on with this article?

LAN内通信アプリを作ろう その4 JavaFXのコントロールに情報を設定する/スレッドからJavaFXの画面を呼び出す

More than 1 year has passed since last update.

仮眠(5時間)も取ったので作業を再開しましょう。

LAN内通信アプリは今日で終わりなのでササッと(出来ても出来なくても)

  • メッセージ部分の受信
  • 受信したメッセージの表示(受信ウィンドウの作成)

受信処理の改修からやっていきます。

受信処理の改修

  • 受信メッセージの表示ウィンドウを作成する。
  • メッセージを安定して受け取れるようにする。

受信メッセージの表示ウィンドウを作成する。

まずはこれ。
JavaFXを利用して受信したメッセージを表示する画面を作成。

送信ウィンドウを流用して作ったのがこちら。

2018y06m15d_224036934.jpg

受信時にウィンドウを呼び出す。

ここで詰まった。

今回のアプリケーションでは「メッセージを受信したときに受信メッセージを新規ウィンドウで表示する」というイメージで作成していて
「受信用の待ち受けスレッドを用意してポートを監視している」作りになっている。

最初は「受信用の待ち受けスレッドで画面を作成して表示すればいいか」と思っていたものの
javafx.stage.Stageのインスタンスを作ってshow()メソッドを呼ぶとIlegalStateExceptionを出してくる。

↓こんな感じ

RxThread.java
    (受信処理)

    Stage stage = new Stage();
    stage.show();

↓結果がこちら

2018y06m16d_001717192.jpg

このExceptionはなんぞや?と思って調べてみると、こんな事実に行き当たった。
曰く
「JavaFXの指定したスレッド(JavaFX application thread)以外からJavaFXの要素を操作しようとすると上記例外が投げられる」

で、回避方法を調べてみたところ、javaFX.platformなるクラスが存在することが判明。

Oracle - JavaFXドキュメンテーション クラスplatform
(日本語)

このクラスのrunLater()メソッドにrunnnableなスレッドを登録してやることでJavaFX applicationが代替実行してくれるそうな。

あ、formみたいにnewしてshow()!とは行かないのね。
さっそく調べた内容を元にStageを表示する処理を
書き換えて動作確認。

↓こんな感じに変更

RxThread.java
    (受信処理)

    Platform.runLater( () -> {
        Stage stage = new Stage();
        stage.show();
    });

↓結果がこちら(真っ白なウィンドウがnewしたStage。サイズ変更してるけど最初はかなり大きい)
2018y06m16d_004712408.jpg

…えー、この事実を受けて
画面描画の構成を変える必要が鎌首をもたげたわけですね。(ため息)
今の作りだとあまりにも汚いので・・・

直しました(1時間使って)。

ざっくりこんな感じの作りに修正


Stage関連処理

画面毎のFXMLファイル

画面毎のStage取得用クラス

画面毎のControllerクラス

送受信処理

ソケット通信を利用してデータを送信するスレッド継承クラス
受信ポートを監視し、データを受信するスレッド継承クラス

通常処理
Platform.runLator()にStageの描画処理を行わせるための汎用スレッド継承クラス
エントリポイントを含むクラス

上記作り変えを行ったことで、
[メッセージの受信を確認] => [受信メッセージStageを取得] => [Applicationスレッドに描画タスクを依頼] => [Stageの描画]
という処理が出来るようになりました。

2018y06m16d_035944134.jpg

次はこのStageに受信したメッセージを表示させたいと思います。

JavaFxを用いて表示したGUIのコントローラに値を設定する

ここもがっつり詰まりました。
しかもnull参照で。

時間が・・時間が・・・(焦燥)

設定の仕方はこんな手順で実現できます。

  1. GUIに紐付けているコントローラークラスに、コントロールを操作するための処理を用意する
  2. コントローラのインスタンスを取得して用意した操作メソッドを呼び出す

書いてしまえばなんと簡単に見えることか

GUIに紐付けているコントローラークラスに、コントロールを操作するための処理を用意する

これは簡単。普通のセッターを作ってあげればOK。
コントロールをオブジェクトとして扱う方法はLAN内通信アプリを作ろう その3の「IDを設定したコントロールと同一の型、名前を持つオブジェクトをクラス内で宣言する。」の項目を参照されたし。

コントローラのインスタンスを取得して用意した操作メソッドを呼び出す

これ。これでガッツリはまりました。
ポイントは以下の通り。

  • コントローラのインスタンスはFXMLLoaderクラスのgetController()メソッドを利用する。 (Object型で返却されるので、コントローラに指定してるクラスでキャストすること)
  • 上記インスタンスはFXMLLoader.load()メソッドを実行した段階で生成される。 (これより早いタイミングで触ろうとするとnull参照で怒られる。ガッ!)

で、この処理のためにfxmlLoaderの実態を持っているコントローラでgetControl()の値を返却するゲッターを作り
それをStage描画用スレッドで受け取ってそのまま返却するラッパーを作って・・・と細かいことを延々と・・・

その甲斐あって、取得したメッセージを表示するまでは成功

2018y06m16d_043800899.jpg

メッセージを安定して受け取れるようにする。

ここで大変悲しいお知らせです。
えー、昨日の自分はなんか意識が朦朧としてたので(言い訳)
無駄な処理を入れてメッセージの待ち受けを行ってました。

ので、無駄な処理を省いてこんな感じに修正

RxThread.java
            while (length >= 0) {
                byte[] buffer = new byte[1024];
                length = dis.read(buffer);
                sBuffer.append(new String(buffer));

            }

  1. バッファ領域とって
  2. 取得したデータ量を更新して
  3. 取得したデータは文字列バッファに格納していく

って処理ですね。
バッファサイズはlengthの値でもよかったかなー。

ソケット通信の場合、取得する情報が無くなると-1を返却してくるので
取る情報が無くなったら抜けてくれるわけですね。

今のところあんま問題ないのでこれ放置で。
本当は最初に送信するデータの量とかを相手方に伝えたりするんですが(伝送中の損失に備えるため)
まあ、今回はLAN内だしね。
暗号化もナシ。

端末間で通信してみる。

さて、とりあえず一通り実装した(ハズ)なので、LAN内の端末間で通信してみましょ。

ひとまず開発マシン(windows 7)と営業用のノートパソコン(windows 10)での結果がこんな感じ

(携帯のカメラで撮影したので非常に重い写真。開発した本体(ライブラリ込み)の70倍超)
P_20180616_060205_vHDR_On.jpg

で、悲しいかなLinuxで起動しようとしたら怒られました。
軽く調べてみたところ、なんかsun.*系のライブラリがどーたらこーたらなのでそこはもうちょっと調査が必要か。
Screenshot from 2018-06-16 06-32-05.png

せっかくJavaで組んだんだから多種のOSに対応したいものね。
とりあえず目的は達成?したので今回はこれまで。
直接IP打ってたりで少し使いづらいのはアレだけど。そこは改善点として・・・別機能だしね(開き直り)
取得したアドレスに/とか入ってるのが気になるところ。

追記:JavaFXのライブラリが入ってなかったっぽい・・・あれ?1.8だともう標準じゃなかったっけ・・・?
openjfxを導入したら普通に起動しました。

Screenshot from 2018-06-16 07-58-48.png

通信結果はこんな感じ。(VNCで表示してます。)

2018y06m16d_080211838.jpg


最終日の稼動は07:41:54で終了。
全体で23:03:51。

来週はどうなるかなー?

あ、挙げ忘れてたけど、GitHubはこちら

GitHub - PrivateMessenger
https://github.com/Shiratori1218/PrivateMessenger

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした