JavaFX桌面應用-為什么應用老是“未響應”


日常使用軟件的過程中,偶爾會遇到軟件突然卡住,再點擊幾次就變成“未響應”的情況。

在JavaFX應用中同樣也會出現這種情況,在開發過程中應該盡量避免這種情況的出現。

>> 更多JavaFX文章 >> JavaFX桌面應用開發系列文章

1. “未響應”重現

應用程序出現“未響應”這種情況往往是因為在UI線程中處理一些耗時的業務,當UI線程在處理耗時的業務時,UI就會卡住。
下面通過一個示例(獲取Google頁面title信息)來演示一下“未響應”這種情況。

這里使用 jsoup 來抓取Google頁面的title信息,需要引入jsoup的maven依賴:

<dependency>
  <groupId>org.jsoup</groupId>
  <artifactId>jsoup</artifactId>
  <version>1.13.1</version>
</dependency>

編譯AppService,實現抓取Google頁面的title信息。

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肯定是超時的,這里設置了超時10秒。

接着改造AppUI,當點擊Go按鈕的時候,調用visitGoogle並將結果顯示在界面上。

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());
    }
}

運行JavaFX應用,當點擊第一個Go按鈕之后,再點擊其他按鈕界面就會卡住,出現“未響應”的現象。

2. UI線程、業務線程分離

前面有提到,出現“未響應”這種情況是因為在UI線程中處理一些耗時的業務,當UI線程在處理耗時的業務時,UI就會卡住。
所以如果能將UI線程和業務線程分開來,這樣就能解決界面卡住的問題了。

改造一下AppUI,將調用visitGoogle的代碼放到新線程去執行。

public void click(ActionEvent event) {
    new Thread(() -> model.setText(appService.visitGoogle())).start();
}

這里直接采用new的方式創建線程,實際應用中最好是使用線程池。

雖然將業務代碼放在新線程中處理解決了界面卡住的問題,但是上面的代碼中,通過model.setText()來改變標簽(Label)的文字。
實際上會發現程序運行后會出現異常,一旦我們在非UI線程中嘗試改變UI效果,程序就會拋出下面的異常。

界面卡住的問題雖然解決了,但又出現了新的問題。

3. 在UI線程更新UI

在UI線程處理業務會導致界面卡住,在業務線程更新UI會出現異常,為了能在業務線程中更新UI,JavaFX為開發者提供了一個Platform類。
只需要在業務線程中,將更新UI的代碼放在這個類的runLater方法中執行即可。

下面再次改造AppUI

public void click(ActionEvent event) {
    new Thread(() -> {
        String title = appService.visitGoogle();
        Platform.runLater(() -> model.setText(title));
    }).start();
}

這里還是將業務代碼放在新線程中執行,但是涉及UI更新的代碼model.setText()則放在Platform.runLater()里面執行。
這樣,就解決了界面卡住以及非UI線程更新UI出現異常的問題了。

通過改造,雖然請求Google超時了,但是UI並沒有卡住,同時界面也得到了更新。

所以特別注意,在開發過程中應該盡量避免:

  1. 在UI線程中處理業務
  2. 在業務線程中更新UI

=========================================================
源碼可關注公眾號 “HiIT青年” 發送 “javafx-thread” 獲取。(如果沒有收到回復,可能是你之前取消過關注。)

HiIT青年
關注公眾號,閱讀更多文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM