標籤:lan center activity 回調 ext 分享 default fonts 避免
在做Android自訂控制項時遇到要自訂回呼函數的問題,想想自己還暫時沒有那麼精深的技術,趕緊返過頭回來再重新研究Java中回呼函數的問題。然而不幸的是,網上太多雜亂的文章和部落格都是轉來轉去,而且都是那一篇“C中的回呼函數.....指標.....java....”,一點看不出來是自己的思路,估計都是哪哪哪抄來的!(呵呵,要麼就是吐槽對了,要麼就是我水平太爛讀不懂還妄加評論)還有一些很不錯的文章,我會在最後參考中加上連結,大家可以看看。
那麼來開始我們的正題——什麼是回呼函數?
我們一步步深入,先從函數調用開始:
什麼是函數(方法)調用?
(別的什麼語言的都忘了……呵呵,只看Java)Java中的函數調用無非就是(1)一個類中方法為了完成一個業務在執行過程中調用另一個方法,另一個方法也可以是自己,那就是遞迴啦;(2)不同類之間的函數調用,比如Class B(調用者Caller)中要調用Class A對象的一個方法(被調用者Callee)(不管A是作為B的成員,還是作為函數參數傳進來)。方法調用是我們編程中必不可少的,可能我們平時視而不見罷了,否則一個工程的不同CLASS怎麼協同工作呢?雖然簡單,還是畫個圖說一下吧,待會對比一下可以更好的解釋回調的機制:
如Class B的函數method_B在執行過程中要調用成員a(Class A)的method_A1方法。
我們剛開始學的時候都是這樣做的。但是這樣做存在問題,讓我們來回顧一下我們當年學習的經過,不斷將這個問題解釋清楚:舉個例子吧,不然嘴笨表達不清楚——A類是鳥類,B就是我們的工具類,現在我們要在工具類中完成這樣的需求:通過工具類中的方法method_B完成不同鳥類飛的正確動作。
第一階段:顯然就兩個類完成不了這個需求!接著我們學習了繼承,我們建立了“麻雀”、“大雁”、“鴕鳥”……各種“鳥”類子類,通過不同子類來完成不同的飛法,然後在工具類中建立不同子類的對象賦值給a,這樣需求完成了。但是回頭看看,我們建立了一大堆的子類,而且還用了隱式類型轉換(子類賦值給父類),顯然不夠滿意;
第二階段:後來,我們又學習了重載函數,在鳥類中建立一系列同名同名的“飛”函數,傳入參數類型分別為麻雀、大雁……,這樣我們就不用隱式轉換了,直接調用鳥類對象a的飛方法,傳入不同的大雁、麻雀……,a就能正確的飛了。這樣需求也完成了,雖然避免了使用隱式轉換,但是那些為數眾多的子類可一個也沒有落下。這樣做顯然也不符合我們的期望(我們的期望是什嗎?就是幹最少的活,鳥就能正確的飛)。
以上實現方式的問題就顯而易見了:一方面我們要維護那些眾多的子類,增加很大的工作量;另一方面,這樣的編碼缺乏靈活性,有多少種鳥你知道嗎,每種鳥又是怎麼飛的你知道嗎,如果幾種鳥極為相似,你是建立不同的類呢還是歸為一類?所以有了下邊這種實現方式,就是採用“回呼函數”——
什麼是回呼函數?
直接吧:
如,回呼函數中必然用到介面。下邊是感覺寫得好的一段理解:
在android的學習過程中經常會聽到或者見到“回調”這個詞,那麼什麼是回調呢?所謂的回呼函數就是:在A類中定義了一個方法,這個方法中用到了一個介面和該介面中的抽象方法,但是抽象方法沒有具體的實現,需要B類去實現,B類實現該方法後,它本身不會去調用該方法,而是傳遞給A類,供A類去調用,這種機制就稱為回調。
我認為,中B類的方法method_B在調用a對象的method_A1時,method_A1執行到interface的抽象方法不知道怎麼實現,正好B在調用他的時候提供了具體實現,所以method_A1返回來調用method_B提供的實現,這就是回調。其實,回呼函數就是在一個不確定實現的方法METHOD中用interface或者它的抽象方法留個口子,留給具體調用者(調用前邊那個不確定的方法METHOD)在調用時提供具體實現來補上那個口子。從而達到更靈活地編碼的目的,也大大減少了子類的使用。就拿上邊沒完的例子繼續吧——
我們這樣來實現:先定義一個介面,介面中聲明抽象方法“飛”;在“鳥”類的“起飛”方法中把介面對象作為一個參數傳進來,剩下的該怎麼做就怎麼做,遇到要飛的地方不知道具體怎麼飛就調用介面提供的抽象方法“飛”;在工具類中調用“鳥”類的“起飛”方法時要實現了抽象方法的對象作為參數傳入,然後你想讓它怎麼飛就怎麼飛,具體實現是你調用的時候現寫的。怎麼樣,這樣的實現好吧?不用隱式轉換,不用大量子類,調用的時候遇到什麼鳥就怎麼飛,達到了我們少幹活的目的!
什麼是自訂回呼函數?
自訂回呼函數,顧名思義,就是我們自己定義的回呼函數。其實上邊那個例子就是自訂回呼函數!我們習慣上把別人定義好的回呼函數叫作回呼函數,Android系統中TextView、ImageView等和它們的子類控制項的Onclick事件響應就是典型的回調機制。關於這個這位大蝦講得比我好——詳細介紹Android中回呼函數機制,詳細看看會很有協助的!
一個簡單的自訂回呼函數的例子
最後在舉個簡單的有代碼的例子,看一下回呼函數的運行過程:
首先,我們定義一個interface:
[java] view plain copy
- public interface MyInterface {
- void sayYourName();
- }
接著,我們定義一個類,其中一個方法以介面MyInterface類型的對象作為參數:
[java] view plain copy
- public class MyClass {
-
- public MyClass() {
- Log.e("WangJ", "MyClass-constructor"); //標註建構函式
- }
-
- /* 用介面類型的對象作為輸入參數 */
- public void sayYourName(MyInterface myInterface){
- Log.e("WangJ", "MyClass-sayYourName_start"); //標註方法開始
- myInterface.sayYourName(); //遇到不知道具體實現的時候就用介面的抽象方法
- Log.e("WangJ", "MyClass-sayYourName_finish"); //方法結束
- }
- }
最後,我們在Activity中調用這個類,建立對象並調用其方法,期間實現介面中抽象方法的具體實現邏輯,供回調使用:
[java] view plain copy
- public class MainActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- MyClass myClass = new MyClass();
- myClass.sayYourName(new MyInterface() { //實現介面並作為參數傳入
- @Override
- public void sayYourName() {
- Log.e("WangJ", "callBack-interface-implementor"); //具體操作實現
- }
- });
- }
- }
好了,運行一下(我們這個例子沒有任何介面,即預設Activity的介面,看日誌):
啟動並執行順序就是我們之前理解的:在B中調用A中的方法,A中方法在運行到介面中抽象方法時返回B中尋找具體實現(這就是回調),回調完成後繼續執行下邊未完成的步驟。
好了,以上就是我所認識的回呼函數,聽起來高深,在你弄懂以後發現也沒有太大的難度。但是想想Java研發者在設計這種機制的時候是多麼有遠見啊(好崇拜,雖然不知道他是誰)!文筆有限,理解不夠,如有不足或錯誤,歡迎指正!最後如約附上那幾篇不錯的文章——
Java/Android中的函數調用&回呼函數&自訂回呼函數