Modyfikowanie GUI w innym wątku JavaFX

0

Hej,
Mam dwie aplikacje: serwerowa i kliencka. Serwer jest zwykłą aplikacją serwerową a Klient aplikacją JavaFX. Potrzebuje w Kliencie sprawdzać np. co 50ms czy nadszedł komunikat i jeśli tak wykonać funkcje zmieniającą tekst przycisku tzn. button.setText("STH"); Robię to tak:

Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    Platform.runLater(createListener());                
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(MainController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }                
            }
        }); 

Gdzie metoda createListener(); zwraca interfejs Runnable w którym zmieniam tekst przycisku.
Niestety gdzie nowy wątek wywoła metodę start() moja aplikacja się zawiesza i nie mogę jej nawet przeciągnąć...
Co źle robię?

0

A co się dzieje w tym kodzie odpalanym przez Platform.runLater? Podaj kod.

Spróbuj, dla testów zwracać pustego Runnable w createListener() i zobacz czy ci się będzie interfejs przycinał. Jeśli nie to znaczy, że błąd jest właśnie w tym co tworzyłeś (oryginalnie) przez createListener().

0

Tak znalazłem problem... Ale nie wiem czym jest spowodowany. Wywołując metodę readObject() na obiekcie klasy ObjectInputStream metoda się nie kończy, dlatego program 'stoi'...

0

readObject() prawdopodobnie czeka, aż nadejdą dane.

W wątku GUI zmieniaj tylko GUI, nie rób nic więcej. Jeśli masz zczytać dane z sieci to zrób to w innym wątku, przetwórz na docelowy format, a w wątku GUIowym (czyli poprzez Platform.runLater) zmień tylko stan GUI na podstawie gotowych danych.

0

Tak wiem to, myślałem tylko że jeśli nie ma danych to funkcja się zakończy...

0

Strumienie w Javie zawsze czekają na dane i zwracają wynik. Żeby było tak jak chcesz to strumienie musiałyby próbować deserializacji za każdym razem jak wywołujesz metodę readObject(), potencjalnie powodując wielokrotną deserializację tego samego obiektu. Dokładając do tego fakt iż niektóre obiekty mogą mieć spory zserializowany rozmiar i złożoność, to ogólna złożoność pamięciowa (na bufory) i obliczeniowa (na wielokrtoną deserializację) deserializacji mogłaby urosnąć do ogromnych rozmiarów. Bez próby deserializacji nie da się stwierdzić czy przyszło wystarczająco dużo danych by z nich złożyć obiekt.

0

Kwestia jest taka że wiem jak duże dane tam są... Czy istnieje sposób żeby sprawdzić czy strumień nie jest pusty i wtedy pobrać dane a jeśli nie to pominąć pobieranie?

0

Lepiej zrobisz jeśli po prostu będziesz wczytywał poza wątkiem GUI. Możesz posłużyć się wbudowanymi w Javę kolejkami jak np LinkedBlockingQueue do przesyłania obiektów między wątkami, możesz iść na całość i użyć Akka.io, możesz też posłużyć się klasami Service czy Task z JavyFX: http://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm Zacznij od tej ostatniej opcji.

Staraj się robić w wątku GUI jak najmniej, bo potem cały interfejs będzie w kółko lagował.

A odpowiadając wprost na twoje pytanie: żeby coś takiego osiągnąć musiałbyś zrobić coś a'la BufferedInputStream tyle że z dodatkową funkcją która mówi czy jest wystarczająco dużo danych by wczytać cały obiekt. Następnie ten twój Stream musiałby pośredniczyć między InputStreamem wczytującym z sieci, a InputStreamem deserializującym obiekty, a ty musiałbyś odpytywać zarówno strumień deserializujący obiekty jak i ten twój buforujący dane. Wydaje mi się jednak, że takie rozwiązanie działałoby i tak gorzej niż te przedstawione wcześniej, bo najszybciej dostaniesz dane jak będziesz na nie cały czas czekał i je natychmiast wczytywał - dzięki temu żaden pośredni bufor nie będzie się blokował.

0

Super, dzięki, działa ;) Jeszcze mam jedno pytanie nie tworząc nowego posta. Korzystam w swoich dwóch aplikacjach z klas Socket i ServerSocket. Lokalnie wszystko działa, a jeśli chciałbym postawić serwer u siebie na komputerze i aby znajomy podłączył się od siebie to co muszę podać w hoście, IP? I czy jest jakaś różnica czy mam wew. czy zew. IP?

0

Java pod spodem korzysta ze standardowych socketów, takich samych jakie dostałbyś w C, C++, Pythonie czy czymkolwiek. Stąd pytanie nie jest o Javę, a o sieci.

Podałeś mało szczegółów, a ja nie jestem zresztą sieciowcem. Prawdopodobnie musisz zorientować się jakie masz publiczne IP, czy jesteś za NATem lub czymś podobnym, czy masz poblokowane lub sforwardowane porty i jak u ciebie działa firewall czy reguły bezpieczeństwa Javy.

IP wewnętrzne jest widoczne tylko z wewnątrz. Zewnętrzne jest widoczne z zewnątrz :] A wewnątrz czego? Wewnątrz podsieci, np VPNa, sieci lokalnej, whatever.

0

Ok, dzięki za pomoc ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1