Android 介面回調機制詳解
在使用介面回調的時候發現了一個經常犯的錯誤,就是回呼函數裡面的實現有可能是用多線程或者是非同步任務去做的,這就會導致我們期望函數回調完畢去返回一個主函數的結果,實際發現是行不通的,因為如果回調是多線程的話你是無法和主函數同步的,也就是返回的資料是錯誤的,這是非常隱秘的一個錯誤。那有什麼好的方法去實現資料的線性傳遞呢?先介紹下回調機制原理。
回呼函數
回呼函數就是一個通過函數指標調用的函數。如果你把函數的指標(地址)作為參數傳遞給另一個函數,當這個指標被用為調用它所指向的函數時,我們就說這是回呼函數。回呼函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。
詳細解釋:
客戶程式C調用服務程式S中的某個函數A,然後S又在某個時候反過來調用C中的某個函數B,對於C來說,這個B便叫做回呼函數。例如Win32下的視窗過程函數就是一個典型的回呼函數。一般說來,C不會自己調用B,C提供B的目的就是讓S來調用它,而且是C不得不提供。由於S並不知道C提供的B姓甚名誰,所以S會約定B的介面規範(函數原型),然後由C提前通過S的一個函數R告訴S自己將要使用B函數,這個過程稱為回呼函數的註冊,R稱為註冊函數。Web Service以及Java的RMI都用到回調機制,可以訪問遠程伺服器程式。
下面舉個通俗的例子:
某天,我打電話向你請教問題,當然是個難題,你一時想不出解決方案,我又不能拿著電話在那裡傻等,於是我們約定:等你想出辦法後打手機通知我,這樣,我就掛掉電話辦其它事情去了。過了XX分鐘,My Phone響了,你興高采烈的說問題已經搞定,應該如此這般處理。故事到此結束。這個例子說明了“非同步+回調”的編程模式。其中,你後來打手機告訴我結果便是一個“回調”過程;My Phone號碼必須在以前告訴你,這便是註冊回呼函數;My Phone號碼應該有效並且手機能夠接收到你的呼叫,這是回呼函數必須符合介面規範。
JAVA中不允許直接操作指標,那它的回調是如何?的呢?
答案:它是通過介面或者內部類來實現的。
JAVA方法回調是功能定義和功能實現分享的一種手段,是一種耦合設計思想。作為一種架構,必須有自己的運行環境,並且提供使用者的實現介面。
1. 定義介面 Callback ,包含回調方法 callback()
2. 在一個類Caller 中聲明一個Callback介面對象 mCallback
3. 在程式中賦予 Caller對象的介面成員(mCallback) 一個內部類對象如
new Callback(){
callback(){
//函數的具體實現
}
}
這樣,在需要的時候,可用Caller對象的mCallback介面成員 調用callback()方法,完成回調。
回調機制在Android架構中的使用
在Activity中定義了很多生命週期的不同狀態要調用的方法,這些方法都是空實現,系統架構要調用,使用者也要調用來實現。
執行個體(對於Android介面上Button點擊事件監聽的類比):
a.定義介面
public interface OnClickListener {
public void OnClick(Button b);
b. 定義Button
public class Button {
OnClickListener listener;
public void click() {
listener.OnClick(this);
}
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
}
c. 將介面對象OnClickListener 賦給 Button的介面成員
public class Activity {
public Activity() {
}
public static void main(String[] args) {
Button button = new Button();
button.setOnClickListener(new OnClickListener(){
@Override
public void OnClick(Button b) {
System.out.println(clicked);
}
});
button.click(); //user click,System call button.click();
}
}
機制原理如上,那資料傳遞依靠介面怎麼傳遞呢?上代碼:
/* * @author sunglasses * @category 利用xUtils架構封裝get方法,IOAuthCallBack是資料介面回調,send方法裡面 有非同步任務方法, * 傳統返回資料方法可能返回資料不正確。 */ public class xUtilsGet {public void getJson(String url,RequestParams params,final IOAuthCallBack iOAuthCallBack){ HttpUtils http = new HttpUtils(); http.configCurrentHttpCacheExpiry(1000 * 10); http.send(HttpMethod.GET, url, params, new RequestCallBack() {@Overridepublic void onFailure(HttpException arg0, String arg1) {// TODO Auto-generated method stub}@Overridepublic void onSuccess(ResponseInfo info) {// TODO Auto-generated method stubiOAuthCallBack.getIOAuthCallBack(info.result);}});}}
我用的是xUtils開發架構然後返回資料的時候用的是介面回調,介面定義如下:
public interface IOAuthCallBack {public void getIOAuthCallBack(String result);}
上層函數定義如下:
public void getCataJson(int cityId,IOAuthCallBack iOAuthCallBack) {String url = http://xxxxxxx;RequestParams params = new RequestParams(); params.addQueryStringParameter(currentCityId, cityId+);getJson(url,params,iOAuthCallBack);}
getcatajson是在你想得到資料的類中調用,然後傳入參數和介面對象,介面對象中是要對最終資料進行處理。具體機制原理如上。這樣就可以避免介面回調中的多線程資料不一致問題。