Java基礎篇:回撥機制詳解
一、什麼是回撥:
回撥是一種雙向的呼叫模式,程式模組之間通過這樣的介面呼叫完成通訊聯絡,回撥的核心就是回撥方將本身即this傳遞給呼叫方,這樣呼叫方就可以在呼叫完畢之後再告訴回撥方它想要知道的資訊。
回撥函式用於層間協作,上層將本層函式安裝在下層,這個函式就是回撥,而下層在一定條件下觸發回撥,例如作為一個驅動,是一個底層,他在收到一個數據時,除了完成本層的處理工作外,還將進行回撥,它將這個資料交給上層應用層來做進一步處理,這在分層的資料通訊中很普遍。其實回撥和API非常接近,他們的共性都是跨層呼叫的函式。但區別是API是低層提供給高層的呼叫,一般這個函式對高層都是已知的;而回調正好相反,他是高層提供給底層的呼叫,對於低層他是未知的,必須由高層進行安裝。這個安裝函式其實就是一個低層提供的API,安裝後低層不知道這個回撥的名字,但它通過一個函式指標來儲存這個回撥,在需要呼叫時,只需引用這個函式指標和相關的引數指標。
其實:回撥就是該函式寫在高層,低層通過一個函式指標儲存這個函式,在某個事件的觸發下,低層通過該函式指標呼叫高層那個函式。從呼叫方式上看,可以分為兩類:同步回撥、非同步回撥。
二、同步回撥與非同步回撥:
1、同步回撥:
同步呼叫是一種阻塞式呼叫,是最基本並且最簡單的一種呼叫方式,類A的方法a()呼叫類B的方法b(),一直等待b()方法執行完畢,a()方法才能繼續往下走。這種呼叫方式適用於方法b()執行時間不長的情況,因為b()方法執行時間一長或者直接阻塞的話,a()方法的餘下程式碼是無法執行下去的,這樣會造成整個流程的阻塞。
2、非同步回撥:
(1)非同步呼叫是為了解決同步呼叫可能出現阻塞,導致整個流程卡住而產生的一種呼叫方式。類A的方法方法a()通過新起執行緒的方式呼叫類B的方法b(),程式碼接著直接往下執行,這樣無論方法b()執行時間多久,都不會阻塞住方法a()的執行。但是這種方式,由於方法a()不等待方法b()的執行完成,在方法a()需要方法b()執行結果的情況下,必須通過一定的方式對方法b()的執行結果進行監聽。為了完成這點,就需要另開一個執行緒了。
(2)非同步呼叫在應用程式框架中具有廣泛的應用,並且特指多執行緒情況下。它同Windows的訊息迴圈機制,訊息響應,訊息佇列,事件驅動機制以及設計模式中的觀察者模式等都是緊密相關的。 在單執行緒方式下,計算機是一臺嚴格意義上的馮·諾依曼式機器,一段程式碼呼叫另一段程式碼時,只能採用同步呼叫,必須等待這段程式碼執行完返回結果後,呼叫方才能繼續往下執行。有了多執行緒的支援,可以採用非同步呼叫,呼叫方和被調方可以屬於兩個不同的執行緒,呼叫方啟動被調方執行緒後,不等對方返回結果就繼續執行後續程式碼。被調方執行完畢後,通過某種手段通知呼叫方:結果已經出來,請酌情處理。非同步回撥常見於請求伺服器資料,當取到資料時,會進行回撥。
三、非同步回撥例子:
上面講了那麼多,其實所謂回撥,就是A類中呼叫了B類的某個方法C,然後B類反過來呼叫A類的方法D,D這個方法就叫回調方法。
別人說的比較經典的回撥方式:
- Class A實現介面CallBack callback——背景1
- class A中包含一個class B的引用b ——背景2
- class B有一個引數為callback的方法f(CallBack callback) ——背景3
- A的物件a呼叫B的方法 f(CallBack callback) ——A類呼叫B類的某個方法 C
- 然後b就可以在f(CallBack callback)方法中呼叫A的方法 ——B類呼叫A類的某個方法D
有一天小王遇到一個很難的問題,問題是“1 + 1 = ?”,就打電話問小李,小李一下子也不知道,就跟小王說,等我辦完手上的事情,就去想想答案,小王也不會傻傻的拿著電話去等小李的答案吧,於是小王就對小李說,我還要去逛街,你知道了答案就打我電話告訴我,於是掛了電話,自己辦自己的事情,過了一個小時,小李打了小王的電話,告訴他答案是2
/**
* 這是一個回撥介面
*/
public interface CallBack {
/**
* 這個是小李知道答案時要呼叫的函式告訴小王,也就是回撥函式
* @param result 是答案
*/
public void solve(String result);
}
/**
* 這個是小王
* 實現了一個回撥介面CallBack,相當於----->背景一
*/
public class Wang implements CallBack {
/**
* 小李物件的引用
* 相當於----->背景二
*/
private Li li;
/**
* 小王的構造方法,持有小李的引用
* @param li
*/
public Wang(Li li){
this.li = li;
}
/**
* 小王通過這個方法去問小李的問題
* @param question 就是小王要問的問題,1 + 1 = ?
*/
public void askQuestion(final String question){
//這裡用一個執行緒就是非同步
new Thread(new Runnable() {
@Override
public void run() {
/**
* 小王呼叫小李中的方法,在這裡註冊回撥介面
* 這就相當於A類呼叫B的方法C
*/
li.executeMessage(Wang.this, question);
}
}).start();
//小網問完問題掛掉電話就去幹其他的事情了,誑街去了
play();
}
public void play(){
System.out.println("我要逛街去了");
}
/**
* 小李知道答案後呼叫此方法告訴小王,就是所謂的小王的回撥方法
*/
@Override
public void solve(String result) {
System.out.println("小李告訴小王的答案是--->" + result);
}
}
/**
* 這個就是小李
*/
public class Li {
/**
* 相當於B類有引數為CallBack callBack的f()---->背景三
* @param callBack
* @param question 小王問的問題
*/
public void executeMessage(CallBack callBack, String question){
System.out.println("小王問的問題--->" + question);
//模擬小李辦自己的事情需要很長時間
for(int i=0; i<10000;i++){
}
/**
* 小李辦完自己的事情之後想到了答案是2
*/
String result = "答案是2";
/**
* 於是就打電話告訴小王,呼叫小王中的方法
* 這就相當於B類反過來呼叫A的方法D
*/
callBack.solve(result);
}
}
/**
* 測試類
*/
public class Test {
public static void main(String[]args){
/**
* new 一個小李
*/
Li li = new Li();
/**
* new 一個小王
*/
Wang wang = new Wang(li);
/**
* 小王問小李問題
*/
wang.askQuestion("1 + 1 = ?");
}
}
參考博文:
https://blog.csdn.net/xiaanming/article/details/8703708