Java&Android中的回調機制
一、簡介
Android中普遍存在着各種回調。如果不懂回調,對很多知識只能是一知半解。所以我決定今兒就好好研究下Java中的回調機制。下面是我的理解過程。
二、理解
1. 知乎說
出自常溪玲的知乎:http://www.zhihu.com/question/19801131/answer/13005983
你到一個商店買東西,剛好你要的東西沒有貨,於是你在店員那裏留下了你的電話,過了幾天店裏有貨了,店員就打了你的電話,然後你接到電話後就到店裏去取了貨。在這個例子裏,你的電話號碼就叫回調函數,你把電話留給店員就叫登記回調函數,店裏後來有貨了叫做觸發了回調關聯的事件,店員給你打電話叫做調用回調函數,你到店裏去取貨叫做響應回調事件。回答完畢。
2. 博客說
出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273)
有一天小王遇到一個很難的問題,問題是“1 + 1 = ?”,就打電話問小李,小李一下子也不知道,就跟小王說,等我辦完手上的事情,就去想想答案,小王也不會傻傻的拿着電話去等小李的答案吧,於是小王就對小李說,我還要去逛街,你知道了答案就打我電話告訴我,於是掛了電話,自己辦自己的事情,過了一個小時,小李打了小王的電話,告訴他答案是2。下面是代碼展示:
/**
* 這是一個回調接口
* @author xiaanming
*/
public interface CallBack {
/**
* 這個是小李知道答案時要調用的函數告訴小王,也就是回調函數
* @param result 是答案
*/
public void solve(String result);
}
/**
* 這個是小王
* @author xiaanming
* 實現了一個回調接口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);
}
}
/**
* 這個就是小李啦
* @author xiaanming
*/
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);
}
}
/**
* 測試類
* @author xiaanming
*/
public class Test {
public static void main(String[]args){
/**
* new 一個小李
*/
Li li = new Li();
/**
* new 一個小王
*/
Wang wang = new Wang(li);
/**
* 小王問小李問題
*/
wang.askQuestion("1 + 1 = ?");
}
}
3. 我說
小王和小李可以通信的前提是相互擁有對方的引用,可以把小李當作一個博識的人,他可以回答很多人很多問題,小王則是其中一個好問問題的人。小王直接擁有小李的引用並通過引用調用小李暴露的executeMessage(CallBack callBack, String question)
方法向小李提問,並把自身的引用通過接口的方式暴露給小李。小李得知答案後,通過小王暴露的接口方法回答小王的問題。
小王爲什麼要用接口方式提供引用給小李呢?這是因爲如果直接給出小王的引用給小李的話,小李就只能解答小王的問題,而不能解答其他人的問題。而接口具有通用性,只要有人實現瞭解答的接口,小李就可以給他提供解答的服務。親畫圖如下:
接下來就不難理解下面的經典的回調方式了(●’◡’●)
4. 經典的回調方式
- 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. 線程的run()方法
- class A實現runnable接口,具有公共的run()方法
- class A中包含一個class Thread的引用b
- class Thread中有一個參數爲target的方法Thread(Runnable target)
- A的對象a調用Thread的方法Thread(Runnable target),其實就是構造方法
- b在Thread(Runnable target)方法中調用A的方法run()方法,這是虛擬機調用的
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
2. View的onClick()方法
- Activity實現View.OnClickListener接口,具有公共的onClick()方法
- Activity中包含一個View的引用view
- View中有一個參數爲l的方法setOnclickListener(OnClickListener l)
- Activity中的view調用setOnclickListener(OnClickListener l),傳入自身的引用
- view在setOnclickListener(OnClickListener l)中調用Activity中的onClick()方法
四、如何自己寫回調呢?
- 準備回調接口,設計回調方法
- 顧客實現回調接口,包含回調方法
- 顧客包含店家的引用
- 店家包含一個以接口爲參數的註冊回調方法
- 顧客調用註冊回調方法
- 店家在註冊回調方法中回調顧客的方法
簡單點說:
- 顧客擁有店家的引用
- 顧客通過接口暴露自身的方法給店家
- 店家暴露方法給顧客,讓顧客傳入自身的引用給店家
- 店家通過顧客傳入的引用調用顧客的方法
再簡單點說:
- 雙方都擁有對方的引用
- 一方通過調用對方的方法觸發回調
- 另一方通過調用對方的方法響應回調