強迫深究Java中的回調

前言:關於Java中的回調一直是困擾着我的一個問題,雖然感覺好像沒什麼好說的,但是作爲強迫症患者,就會深究其中無法自拔。例如:爲什麼要回調?回調和委派是什麼關係?感覺要實現委派,回調似乎是少不了的。那麼Android中的事件處理委派機制和回調機制的區別究竟是什麼?接下來就來研究一下Java中的回調吧。


一、什麼是回調?

我打算還是用大家習慣的打電話的例子來解釋回調。有一天,小明打電話給小王,問他下午要不要一起去打球?小王很想一起打,但是他下午可能會有其他的事情,基於這個考慮,小王不能立即給小明答案。於是他對小明說:“我下午看看有沒有時間,再給你答覆”。於是他們就掛了電話,小明就只要等待小王的電話就行了。這種情況就是生活中的回調例子。
客戶端(小明)向服務端(小王)請求某種服務,然後客戶端可以做其他事情,等到服務端將服務處理完之後,就通知客戶端做完它要求的事情了,可以驗收了,這個通知客戶端的過程就叫做回調。

二、爲什麼要引入回調?

那麼爲什麼要引入回調呢?其實答案已經隱藏在上面的例子中了。程序調用分爲同步調用和異步調用,區別如下:
(所謂同步調用,就是A請求B的服務,然後A就一直等待,一直到B服務完成返回給A,A才能繼續執行,這種方案實現比較簡單,但是顯然效率不高。
異步調用相對於同步調用,A發送一個請求消息之後,便可以做其他的事情,等到B完成服務之後再反過來通知A,這其實就是回調)
綜上,我們可以知道,回調是爲了解決異步調用的問題
那麼Java和C++回調的區別是什麼呢?
C++我們知道是允許指針的,於是C++中基於指針的回調就顯得很自然了,這種回調可以提供客戶端很大的靈活性,因爲此時客戶端操作的是服務端對象的實體。
Java是沒有指針的,但是也逃不開回調這個課題,於是Java中採用了一種基於接口的回調(也就是我們常說的接口回調),這種回調是安全的,不像C++,Java可以通過接口限制客戶端的調用權限。當然Java中的回調不止接口回調這一種,這裏以接口回調爲例。

三、接口回調的實例和原理。

首先編寫服務端程序(相當於上面例子中的“小王”),程序中已添加了必要的註解。

package test;

 

public class Service {


private Callback mCallback;

/**

 * 在服務端定義一個回調接口

 */

public interface Callback {

void callbackFunction();

}

 

/**

 * 客戶端初始化時需要傳入一個Callback接口的實現類。

 *

 * @param mCallback

 *            相當於客戶端拋給服務端的“鉤子”,服務端將通過這個“鉤子”實現回調。

 */

public Service(Callback mCallback) {

this.mCallback = mCallback;

}

 

/**

 * 服務端提供給客戶端的服務

 */

public void startThinking() {

for (int i = 0; i < 5; i++) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("I'm thinking!!!:" + i);

}

System.out.println("Service done!");

// 回調客戶端的回調函數,mCallback是客戶端傳過來的“鉤子”

mCallback.callbackFunction();

}

}


接着是客戶端的代碼:

package test;

 

import test.Service.Callback;

 

public class Client {

 

public static void main(String args[]) {

Callback mCallback = new Callback() {

@Override

public void callbackFunction() {

System.out.println("Back to client!");

}

};

//這裏相當於將接口的實現類傳給了服務端,提供了服務端回調的一個“鉤子”

Service mCallee = new Service(mCallback);

//請求服務端的服務,相當於例子中小明打電話問小王的過程,接下來就只要等小王打回來就行了(回調)

mCallee.startThinking();

}


執行結果:


小結:從上面可以看出,這種回調是基於接口的一種回調,客戶端將接口的實現類(“鉤子”)丟給服務端,服務端做完事情之後這個“鉤子”使回調成爲可能,通過“鉤子”回調了客戶端代碼。

大致原理如圖:


四、回調的實例,Android事件處理機制。

我們知道,Android中的事件處理有兩種方案,一種是基於監聽器的委派機制,一種是回調機制。前者是將事件響應的代碼與組件本身分離,也是較爲常見的方法。後者是通過重寫自定義組件的回調方法實現事件響應的,組件和事件響應代碼混在一起。
這裏想講的是很多人會因爲這兩個方案的命名而對回調的概念產生懷疑,事實上,我們可以查看源碼,發現這種基於監聽器的委派機制實際上也是用到了回調。例如:
button.setOnClickListener(new OnClickListener(){
@Override
... onClick(View v)
{
...;
}
});
我們可以觀察到這種邏輯跟上面討論的是一致的,將OnClickListener接口的實現類(“鉤子”)傳遞給服務端,然後等待服務端的回調。所以不能被名字混淆,認爲基於監聽器的事件處理沒有用到回調...由此可能會產生對其概念的深深懷疑...
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章