回調的含義和用途
什么是回調?
一般來說,模塊之間都存在一定的調用關系,從調用方式上看,可以分為三類:同步調用、異步調用和回調。同步調用是一種阻塞式調用,即在函數A的函數體里通過書寫函數B的函數名來調用之,使內存中對應函數B的代碼得以執行。異步調用是一種類似消息或事件的機制解決了同步阻塞的問題,例如 A通知 B后,他們各走各的路,互不影響,不用像同步調用那樣, A通知 B后,非得等到 B走完后, A才繼續走 。回調是一種雙向的調用模式,也就是說,被調用的接口被調用時也會調用對方的接口,例如A要調用B,B在執行完又要調用A。
回調的用處
回調一般用於層間協作,上層將本層函數安裝在下層,這個函數就是回調,而下層在一定條件下觸發回調。例如作為一個驅動,是一個底層,他在收到一個數據時,除了完成本層的處理工作外,還將進行回調,將這個數據交給上層應用層來做進一步處理,這在分層的數據通信中很普遍。
Java中接口回調機制
java接口回調機制想必大家並不陌生,其思想簡單,應用廣泛,如網絡請求、界面的點擊監聽等,是一個java開發者必須要掌握的基本思想之一。
我們在做java開發時經常會遇到文件下載、請求服務器數據等基本操作,大家都知道網絡請求屬於耗時操作,我們如果在直接主線程執行這些邏輯時很可能會造成主線程堵塞,從而導致程序崩潰。我們通常都是開啟一個子線程來執行網絡請求操作。
public void doSomeWork() { new Thread(new Runnable() { @Override public void run() { // 執行邏輯,發起網絡請求,如請求后台數據,下載文件等 } }).start(); }
上面這段代碼想必你已經再熟悉不過了,不過當我們在做一次開發時,經常會多次使用網絡請求,比如多次請求服務器的數據,所以我們更願意將其寫成一個小框架:
public String doRequest(String url) { new Thread(new Runnable() { @Override public void run() { // 執行邏輯,請求后台json數據 } }).start(); //將獲取的數據轉化為String,並返回 }
那么問題來了: 我們在寫成框架時,網絡請求是在子線程中進行的,很可能數據還沒返回來的時候,doRequest方法就已經執行完了,那么這時候返回的數據就沒有任何意義了,最終的結果是我們得到的String為空的,而不是我們期待的的數據。
解決此問題的一種方法: 子線程請求到數據后,直接對數據進行處理(缺陷:失去了框架的意義)
public void doRequestAndDealData(String url) { new Thread(new Runnable() { @Override public void run() { // 執行邏輯,請求后台json數據 // 直接對獲取到的數據進行處理 } }).start(); }
對比上一段代碼,我們這里直接對返回的數據進行了處理,而沒有在方法里返回數據,但是這樣的處理邏輯就是唯一的了,並不能隨着請求的url不同而執行不同的處理邏輯,那么有沒有一種方法能將在子線程中獲取到的數據"傳出去",使其能得到成功的處理呢? 這就是接口回調的巧妙之處了!
-
先定義一個接口
public abstract class CallBackListener { public abstract void onFinish() public abstract void onError(Exception ex); }
-
在類A中通過在子線程發起網絡請求,並將接口作為參數,寫到類A中!
public class A { public void doRequest(String url,CallBackListener backListener) { new Thread(new Runnable() { @Override public void run() { try { // 執行邏輯,發起網絡請求,如請求后台數據,下載文件等 backListener.onFinish(); } catch (Exception ex) { backListener.onError(ex); } } }).start(); } }
-
在類B中調用類A的對象,發起請求,並且對請求得到的數據進行處理。
public class B { public void deal() { A a = new A(); a.doRequest("http://請求的url",new CallBackListener() { @Override public void onFinish() { //請求成功的邏輯,如下載完成后的處理,請求到數據后的處理 } @Override public void onError(Exception ex) { // 異常邏輯 } }); } }
我們在B中調用A的方法時,重寫了接口中的方法,當發起的網絡請求完成時,就會調用我們重寫后的方法,這就是接口回調,這樣根據需要來進行不同的重寫,同樣保留了框架的意義。
我們在下載完成后,界面的點擊事件監聽,后台數據請求完成時...還有很多地方都可以用到接口回調,掌握其思想后,我們也可以寫的更加規范一點了,如將A類中的接口作參數就寫為單的一個方法,setListener(CallBackListener listener);
