1. 程式人生 > >Java基礎篇:回撥機制詳解

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