Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: Zelenac 04. 02. 2016, 23:31:25

Název: Vyvolání výjimky v Javě mi zpomaluje program
Přispěvatel: Zelenac 04. 02. 2016, 23:31:25
Ahoj,
 potřeboval bych pomoci s touto věcí:

Kód: [Vybrat]
okButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
      try {
         inetScoketAddress = new InetSocketAddress(InetAddress.getByName(textFieldIP.getText()), Integer.parseInt(textFieldPort.getText()) );
         setVisible(false);
         dispose();
      } catch (Exception e1) {
         JOptionPane.showMessageDialog(contentPanel, "IP Address must fit in range 0.0.0.0 to 255.255.255.255, port 0 to 65535.");
      }
   }
});

Jde o to, že je li při parsování řetězů s IP vyhozena vyjímka, dojde k příliš velké prodlevě mezi následným zobrazením varovného boxu, cca 2-3 vteřiny. Na tom, jestli nehcým vyskočit okno mimo try catch, nezáleží, prodleva je stejná.

Čím to? A co s tím, když na vyjímky jemnohdy třeba se spolehnout jako na kontrolní indikátory nahrazující Céčkovské:
if( delitel != 0) // teprve ted zkus delit
else return -1;
Název: Re:Vyvolání vyjímky v Javě mi zpomaluje program
Přispěvatel: Filip Jirsák 05. 02. 2016, 07:02:32
Za prvé tam odchytáváte všechny výjimky, přitom pak tvrdíte, že došlo k jedné ze dvou chyb – když dojde k jiné, bude ta hláška matoucí. Za druhé, podle mne ta prodleva není způsobená „vyhozením výjimky při parsování“, ale prostě tím, že se daný text hledá v DNS, a ono to přeci jen chvilku trvá, než se dozvíte, že zadaná adresa neexistuje. Za třetí, ten překlad názvů (= síťová komunikace) děláte v AWT vlákně, což je špatně, protože tím vlákno po dobu síťové komunikace zablokujete a nebude reagovat UI.
Název: Re:Vyvolání vyjímky v Javě mi zpomaluje program
Přispěvatel: sj 05. 02. 2016, 08:57:10
Přesně jak říkal Filip Jirsák. V kódu není žádné parsování řetězců s IP. Chytat Exception či Throwable (mimo specifické výjimečné situace) je prasárna. Btw, "IP Address must fit in range 0.0.0.0 to 255.255.255.255" - to IPv6 nepodporujete? ;-) Samotná getByName podporuje IPv6 adresy...

Navíc tak jak to máte napsáno tak pokud uživatel místo IP adresy zadá doménové jméno tak getByName to myslím vesele zbaští a žádnou výjimku nevyhodí. Možná byste spíše chtěl použít nějakou jinou funkci v http://download.java.net/jdk7/archive/b123/docs/api/java/net/InetAddress.html (příp. dle verze Javy)?

Práce v UI vlákně je samozřejmě také špatná, v limitovaném množství např. na lokální síti se to většinou neprojeví, ale jakmile se začne skládat kód využívající více podobných metod a bude se komunikovat přes internet kde ping může být i 500ms a více tak to bude tragédie.
Název: Re:Vyvolání výjimky v Javě mi zpomaluje program
Přispěvatel: Zelenac 05. 02. 2016, 11:33:05
Ok je to tak, ono to vážně hledá v DNS.

Ad vyjímky: ok jsou tam cekem minimálně dvě vyjímky, měl bych tedy odchytit každou zvlášť. JENŽE. Co když používám metodu nějaké knihovny X, která sice vyhazuje vyjímku A, ale používá další metodu Y, Z, které vyhazují vyjímky B a C. Nemůžu přece pořád prohledávat celý strom a sledovat, co všechno můžu potencionálně zachytit, až mi to nahoru probublá. Proto na to prostě prdím a používám Exception.

Ad awt vlákno: ten dialog s IP adresou je dialog, který vrací hodnotu jako funkce. Když ho nechám rozběhnout se v samostatném vlákně, nevrátí mi žádnou hodnotu - tedy ne tak jednoduše, musel bych to asi řešit zase přes eventy a to je zase psaní navíc. To je jedna věc. Druhá věc: když mi vyskočí dialog se zadáním IP adresy a portu, okno pod ním zamrzne. Ale to je přece normální a já to tak chci, že aktivní je aktuální okno a na to pod ním nejde klikat. Rovněž když vyskočí. Když teda kliknu na OK abych potvrdil zadání IP adresy, okno zamrzne. Ale zase na druhou stranu, co má dělat? Je přece dobře, že když probíhá zpracovávání něčeho, nebude se na něj dát klikat. Správně bych měl vyvolat dialog s nápisem: "Ověřuji správnost IP...", na ten by se nedalo klikat. Ne?
Název: Re:Vyvolání výjimky v Javě mi zpomaluje program
Přispěvatel: Filip Jirsák 05. 02. 2016, 12:05:23
Ad vyjímky: ok jsou tam cekem minimálně dvě vyjímky, měl bych tedy odchytit každou zvlášť. JENŽE. Co když používám metodu nějaké knihovny X, která sice vyhazuje vyjímku A, ale používá další metodu Y, Z, které vyhazují vyjímky B a C. Nemůžu přece pořád prohledávat celý strom a sledovat, co všechno můžu potencionálně zachytit, až mi to nahoru probublá. Proto na to prostě prdím a používám Exception.
Od toho jsou v Javě dva druhy výjimek, kontrolované (dědí přímo z Exception) a nekontrolované (dědí z RuntimeException). To, že metoda může vyhodit kontrolovanou výjimku, je součástí její deklarace. Když tu metodu voláte, musíte příslušnou výjimku buď zachytit a ošetřit, nebo také vaše metoda musí deklarovat, že danou výjimku může vyhodit.

Takže nemusíte prohledávat žádný strom volání. Stačí se naučit základy a pak programovat správně.

Ad awt vlákno: ten dialog s IP adresou je dialog, který vrací hodnotu jako funkce. Když ho nechám rozběhnout se v samostatném vlákně, nevrátí mi žádnou hodnotu - tedy ne tak jednoduše, musel bych to asi řešit zase přes eventy
Problém je hlavně v tom, že matláte všechno dohromady. Swing je od začátku postaven tak, aby se používal v MVC. Ale samozřejmě k tomu nedokáže autora donutit.

Funkce vracející hodnotu nemá být volána v AWT vlákně, ale ve výkonném kódu. Ten si klidně může čekat, až se funkce vrátí, a nebo můžete použít lambdu, která se spustí v okamžiku, kdy funkce získá hodnotu. Ve spoušti události tlačítka, která se provádí v AWT vlákně, nemá být žádný výkonný kód – má tam být pouze nastavení GUI do stavu, ve kterém se bude provádět nějaká činnost (tj. zamezení vstupu do všech ovládacích prvků a aktivace jediného ovládacího prvku, který umožní přerušit prováděnou akci), pak spuštění výkonného kódu v samostatném vlákně a pak provedení akce po dokončení výkonného kódu. Ve standardní knihovně je na to minimální podpora ve třídě SwingWorker, případně pro to zase existují knihovny. A celé by to pokud možno mělo být součástí nějakého controlleru, ne špagety ve formuláři.

a to je zase psaní navíc
Ano, napsat program pořádně je vždy psaní navíc. Program, který běží při dobré konstelaci hvězd na počítači autora a autor jej mistrně ovládá a neklikne vedle, což by způsobilo okamžité zhroucení programu, je vždy podstatně jednodušší. Akorát je snaha takové prasomátory nepouštět moc blízko k PC, protože napáchají víc škody, než užitku.

Druhá věc: když mi vyskočí dialog se zadáním IP adresy a portu, okno pod ním zamrzne. Ale to je přece normální a já to tak chci, že aktivní je aktuální okno a na to pod ním nejde klikat.
Jenže vy způsobíte to, že zamrzne i ten samotný dialog. Nejde zavřít, nejde přesunout, nejde s ním udělat nic. Vy to tak možná chcete, ale nechce to tak žádný uživatel.

Ale zase na druhou stranu, co má dělat? Je přece dobře, že když probíhá zpracovávání něčeho, nebude se na něj dát klikat.
To, že se nedá klikat na obsah dialogu, je něco úplně jiného, než že vám zamrzne GUI celé aplikace a ani ji neodkážete ukončit. Navíc zrovna při vyvolání síťové operace by uživatel měl mít možnost operaci přerušit, protože když vytrhne síťový kabel, ví sám velice dobře, že to nedopadne, a chtěl by překlad sám ukončit – a nečekat třeba 30 sekund nebo 2 minuty, než vyprší timeout na překlad a zjistí i aplikace, že to nejde.
Název: Re:Vyvolání výjimky v Javě mi zpomaluje program
Přispěvatel: Natix 05. 02. 2016, 12:22:24
Ad vyjímky: ok jsou tam cekem minimálně dvě vyjímky, měl bych tedy odchytit každou zvlášť. JENŽE. Co když používám metodu nějaké knihovny X, která sice vyhazuje vyjímku A, ale používá další metodu Y, Z, které vyhazují vyjímky B a C. Nemůžu přece pořád prohledávat celý strom a sledovat, co všechno můžu potencionálně zachytit, až mi to nahoru probublá. Proto na to prostě prdím a používám Exception.

Takhle se to opravdu nedělá. Pokud nějaké API vyhazuje výjimky, tak to musí jasně dokumentovat. Někdy jsou tyto výjimky i checkované, takže jejich zpracování nebo propagaci vynucuje kompilátor, ale můžou klidně být i necheckované, a tudíž je nutné se podívat do javadocu.

https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#parseInt(java.lang.String)
Kód: [Vybrat]
Throws:
NumberFormatException - if the string does not contain a parsable integer.

https://docs.oracle.com/javase/8/docs/api/java/net/InetAddress.html#getByName-java.lang.String-
Kód: [Vybrat]
Throws:
UnknownHostException - if no IP address for the host could be found, or if a scope_id was specified for a global IPv6 address.
SecurityException - if a security manager exists and its checkConnect method doesn't allow the operation
SecurityException nás tady nezajímá, protože nemůže za běžné situace nastat.

https://docs.oracle.com/javase/8/docs/api/java/net/InetSocketAddress.html#InetSocketAddress-java.net.InetAddress-int-
Kód: [Vybrat]
Throws:
IllegalArgumentException - if the port parameter is outside the specified range of valid port values.

Nic dalšího, než je deklarováno, nechytáme. Výjimky nejsou pokémoni.

Kód: [Vybrat]
try {
    inetScoketAddress = new InetSocketAddress(
        InetAddress.getByName(textFieldIP.getText()), Integer.parseInt(textFieldPort.getText()));
} catch (UnknownHostException e) {
    JOptionPane.showMessageDialog(contentPanel, "Invalid host name");
} catch (NumberFormatException | IllegalArgumentException e) {
    JOptionPane.showMessageDialog(contentPanel, "Port must be a number between 0 and 65535.");
}
Název: Re:Vyvolání výjimky v Javě mi zpomaluje program
Přispěvatel: Zelenac 05. 02. 2016, 13:05:47
Díky za rady.

Ad Swing a MVC: tedy když budu chtít v MainFrame editovat JList v závislosti na výstupu z ConnectionDialog, neměl bych tak správně činit ani návratovou hodnotou, ani Událostí mezi MainFrame a ConnectionDialog, nýbrž událostí, mezi Controlerem a ConnectionDialogem? Jak blbé je toto přímočaré řešení:
Citace
mntmNewConnection.addMouseListener(new MouseAdapter() {
         @Override
         public void mousePressed(MouseEvent e) {
            DialogNewConnection dialog = new DialogNewConnection();
            dialog.setModalityType(ModalityType.APPLICATION_MODAL);
            InetSocketAddress ipPort = dialog.showDialog();
            fireConnectMeTo(ipPort);
         }
      });

ještě k MVC. Pokud ve framu A vyvolám frame B, kde B má vrátit X, a činím tak přes MVC, potom v Controllery mám vytvořenou instanci A, ale nemám v něm instanci B (ta patří pod A). Z toho plyne, že do B musím dát "fireBBBBB(X)", poté do A musím dát "onBBBBB(X)" a pak ještě pro pořádek opět "fireBBBBB(X)", a pak ještě do Controlleru musím dát opět "onBBBBB(X)".  :(
Název: Re:Vyvolání výjimky v Javě mi zpomaluje program
Přispěvatel: hawran diskuse 05. 02. 2016, 13:53:00
http://forum.root.cz/index.php?topic=12683.msg156441#msg156441 (http://forum.root.cz/index.php?topic=12683.msg156441#msg156441)
Název: Re:Vyvolání výjimky v Javě mi zpomaluje program
Přispěvatel: Filip Jirsák 05. 02. 2016, 16:39:08
Můžete si samozřejmě myslet, že jako programátor-začátečník to zvládnete lépe, než s využitím vzorů, které popsali generace programátorů před vámi, když zjišťovali, co obvykle funguje a co obvykle vede do pekel. Pokud se ale chcete opravdu něco naučit, posbírejte zbytky pokory, pokud ještě nějaké máte, zapomeňte na to, že namatlat všechno na jednu hromadu je dobrý nápad, a přečtěte si něco o MVC a o tom, proč je dobré oddělit logiku aplikace od UI. To vaše přímočaré řešení má hned několik vad: máte tam úzkou vazbu mezi komponentami, které spolu nijak nesouvisí – když budete chtít umožnit zadat IP adresu i jinak, v konfiguračním souboru nebo na příkazové řádce, musíte změnit veškerý kód GUI, kde se teď ta IP adresa získává. Když s tou IP adresou budete chtít pracovat jinak, třeba ji uložit a při příštím spuštění programu obnovit, opět to budete muset celé přepsat. A kód se bude postupně natahovat a natahovat o další ify a budou z toho pěkné špagety. Další chyba je, že v tom kódu volaném v AWT vlákně skrytě voláte blokující operace, takže třeba při překladu toho uživatelem zadaného názvu vám ta aplikace celá zamrzne.