日常使用軟件的過程中,偶爾會遇到軟件突然卡住,再點擊幾次就變成“未響應(yīng)”的情況。
在JavaFX應(yīng)用中同樣也會出現(xiàn)這種情況,在開發(fā)過程中應(yīng)該盡量避免這種情況的出現(xiàn)。
1. “未響應(yīng)”重現(xiàn)
應(yīng)用程序出現(xiàn)“未響應(yīng)”這種情況往往是因為在UI線程中處理一些耗時的業(yè)務(wù),當(dāng)UI線程在處理耗時的業(yè)務(wù)時,UI就會卡住。
下面通過一個示例(獲取Google頁面title信息)來演示一下“未響應(yīng)”這種情況。
這里使用 jsoup 來抓取Google頁面的title信息,需要引入jsoup的maven依賴:
1
2
3
4
5
|
< dependency > < groupId >org.jsoup</ groupId > < artifactId >jsoup</ artifactId > < version >1.13.1</ version > </ dependency > |
編譯AppService,實現(xiàn)抓取Google頁面的title信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class AppService { public static final AppService INSTANCE = new AppService(); private AppService() { } public String visitGoogle() { try { Document document = Jsoup.parse( new URL( "https://www.google.com" ), 10_000); return document.head().getElementsByTag( "title" ).get( 0 ).text(); } catch (Exception e) { return e.getMessage(); } } } |
因為沒有F墻,這里訪問Google肯定是超時的,這里設(shè)置了超時10秒。
接著改造AppUI,當(dāng)點擊Go按鈕的時候,調(diào)用visitGoogle并將結(jié)果顯示在界面上。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class AppUI implements Initializable { public Label text; private AppService appService = AppService.INSTANCE; private AppModel model = new AppModel(); @Override public void initialize(URL location, ResourceBundle resources) { text.textProperty().bindBidirectional(model.textProperty()); model.setText( "Hello JavaFX." ); } public void click(ActionEvent event) { model.setText(appService.visitGoogle()); } } |
運(yùn)行JavaFX應(yīng)用,當(dāng)點擊第一個Go按鈕之后,再點擊其他按鈕界面就會卡住,出現(xiàn)“未響應(yīng)”的現(xiàn)象。
2. UI線程、業(yè)務(wù)線程分離
前面有提到,出現(xiàn)“未響應(yīng)”這種情況是因為在UI線程中處理一些耗時的業(yè)務(wù),當(dāng)UI線程在處理耗時的業(yè)務(wù)時,UI就會卡住。
所以如果能將UI線程和業(yè)務(wù)線程分開來,這樣就能解決界面卡住的問題了。
改造一下AppUI,將調(diào)用visitGoogle的代碼放到新線程去執(zhí)行。
1
2
3
|
public void click(ActionEvent event) { new Thread(() -> model.setText(appService.visitGoogle())).start(); } |
這里直接采用new的方式創(chuàng)建線程,實際應(yīng)用中最好是使用線程池。
雖然將業(yè)務(wù)代碼放在新線程中處理解決了界面卡住的問題,但是上面的代碼中,通過model.setText()來改變標(biāo)簽(Label)的文字。
實際上會發(fā)現(xiàn)程序運(yùn)行后會出現(xiàn)異常,一旦我們在非UI線程中嘗試改變UI效果,程序就會拋出下面的異常。
界面卡住的問題雖然解決了,但又出現(xiàn)了新的問題。
3. 在UI線程更新UI
在UI線程處理業(yè)務(wù)會導(dǎo)致界面卡住,在業(yè)務(wù)線程更新UI會出現(xiàn)異常,為了能在業(yè)務(wù)線程中更新UI,JavaFX為開發(fā)者提供了一個Platform類。
只需要在業(yè)務(wù)線程中,將更新UI的代碼放在這個類的runLater方法中執(zhí)行即可。
下面再次改造AppUI
- public void click(ActionEvent event) {
- new Thread(() -> {
- String title = appService.visitGoogle();
- Platform.runLater(() -> model.setText(title));
- }).start();
- }
這里還是將業(yè)務(wù)代碼放在新線程中執(zhí)行,但是涉及UI更新的代碼model.setText()則放在Platform.runLater()里面執(zhí)行。
這樣,就解決了界面卡住以及非UI線程更新UI出現(xiàn)異常的問題了。
通過改造,雖然請求Google超時了,但是UI并沒有卡住,同時界面也得到了更新。
所以特別注意,在開發(fā)過程中應(yīng)該盡量避免:
在UI線程中處理業(yè)務(wù)在業(yè)務(wù)線程中更新UI
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://www.cnblogs.com/itqn/p/13388247.html