日常開發中接口回調機制無處不在,剛開始用時卻總是暈暈乎乎,網上也有很多相關的文章介紹,但總是沒有看得太明白,今天端午假期正好花時間來總結一下,我們按如下順序介紹
一、什麼是接口回調
在應用開發中,接口回調機制是一種常用的設計手段,也可以說是一種處理問題的模型,類之間,模塊之間,都有一定的調用關係,一般來說,可以把使用某一接口的類創建的對象的引用賦給該接口聲明的接口變量,那麼該接口變量就可以調用被類實現的接口方法,實際上,當接口變量調用被類實現的接口中的方法時,就是通知相應的對象調用接口的方法,這個調用的過程稱爲接口的回調,是不是很暈乎,無奈,理論總是很抽象,後面我們會通過具體的實例來驗證,看完實例大家可以再回來體會一下,看看是不是這個理
二、回調的應用場景
既然接口回調是開發應用中處理問題的核心手段,那我們來看看它到底有什麼應用場景,也就是說具體在什麼情況下,我們要想到使用接口回調來解決問題,回調一般用於分層間的互相協作,上層將本層函數安裝在下層,這個函數就是回調,而下層在一定條件下觸發回調,例如,作爲一個驅動,是底層,它在收到一個數據時,除了完成本層的處理工作外,還將進行回調,將數據交給上層做進一步的處理,另外我們在封裝中也經常用到,還有就是 View 的點擊事件其實也是使用回調的原理
三、Java 中實現接口回調
原理:首先創建一個對象,然後再創建一個控制器對象,將回調對象需要被調用的方法告訴控制器對象,控制器對象負責檢查某個場景是否出現或某個條件是否滿足,當滿足時,自動調用回調對象方法
例如程序員 A 和 B 之間需要協作,A 告訴 B 這個任務中間的某個環節需要 B 來完成,並且完成後告訴 A,這時候程序員 A 就需要告訴 B 一個聯繫方式,使 B 完成時來通知自己,這個場景就可以使用回調,
示例代碼如下:
1.首先我們先來創建一個回調接口
這裏主要是得讓程序員 B 幹完活如何找到程序員 A
/**
* 回調接口
* 此接口爲聯繫的方式,程序員A必須要實現此接口
* Created by qiudengjiao on 2017/5/27.
*/
public interface Callback {
void event(String result);
}
2.創建回調接口的實現類,也就是程序員 A 對象,必須要實現回調接口,要不程序員 B 幹完活,沒法找到程序員 A
/**
* 層序員 A
* 層序員 A 是作爲上層應用身份出現的,下層應用(程序員 B)是不知道
* 有哪些方法,因此它想被下層應用(程序員 B)調用必須實現此接口
* Created by qiudengjiao on 2017/5/27.
*/
public class ProgrammerA implements Callback {
//B程序員對象引用
private ProgrammerB programmerB;
//在構造方法中國持有B程序員的引用
public ProgrammerA(ProgrammerB programmerB) {
this.programmerB = programmerB;
}
/**
* 程序員A通過這個方法告訴程序員B任務
*/
public void doEvent(final String event) {
//這裏用一個線程就是異步,
new Thread(new Runnable() {
@Override
public void run() {
//程序員A調用程序員B中的方法,在這裏註冊回調接口
programmerB.doWork(ProgrammerA.this, event);
}
}).start();
}
/**
* 程序員B完成任務後調用此方法告訴A,也就是程序員A的回調方法
*
* @param result
*/
@Override
public void event(String result) {
Log.e("程序員B告訴程序員A:", result);
}
}
3.創建控制類,也就是程序員 B 對象
/**
* 程序員 B
* 必須要注意,這是一個底層類,底層是不瞭解上層服務的
* Created by qiudengjiao on 2017/5/27.
*/
public class ProgrammerB {
public void doWork(Callback callback, String event) {
Log.e("程序員A告訴程序員B需要乾的事:", event);
Log.e("程序員B:", "幹活.....");
String result = "完成工作";
//程序員B在這裏調用A回調方法,告訴完成任務
callback.event(result);
}
}
接着我們來在 MainActivity 中測試一下:
/**
* Callback 測試
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
ProgrammerB programmerB = new ProgrammerB();
ProgrammerA programmerA = new ProgrammerA(programmerB);
programmerA.doEvent("編寫一個列表界面");
}
}
運行結果截圖:
到這裏我覺得大家對回調也就有了基本的認識,接下來就是需要去親自動手實踐一下,自己體會體會,畢竟實踐纔是最好的老師
四、接口回調在 Android 中的應用
1.View 的回調接口
/**
* Interface definition for a callback to be invoked when a view is clicked.
*/
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
2.回調函數
/**
* Callback 測試
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
init();
}
private void initView() {
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
}
private void init() {
ProgrammerB programmerB = new ProgrammerB();
ProgrammerA programmerA = new ProgrammerA(programmerB);
programmerA.doEvent("編寫一個列表界面");
}
/**
* 用戶點擊Button時調用的回調函數
* @param v
*/
@Override
public void onClick(View v) {
Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show();
}
}
3.View 類的 setOnClickListener 方法
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
/**
* Listener used to dispatch click events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected OnClickListener mOnClickListener;
/**
* setOnClickListener()的參數是OnClickListener接口
* Register a callback to be invoked when this view is clicked. If this view is not
* clickable, it becomes clickable.
*
* @param l The callback that will run
*
* @see #setClickable(boolean)
*/
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}
/**
* Call this view's OnClickListener, if it is defined.
*
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//回調方法
mOnClickListener.onClick(this);
return true;
}
return false;
}
}
五、總結
1. 必須明確回調函數出現的原因,也就是使用的場景
2.還有就是在工作中,項目中可能會把一部分功能交給被人或別的公司來做,實現個性化定製功能,這裏別人具體去怎麼實現,我們便無法得知,其實我們也沒必要去管,我們只需要做的就是定義好相關接口,這一設計允許了底層代碼調用高層定義的子程序,增強了程序的靈活性,這纔是回調的真正原因
3.在封裝過程中,上層模塊封裝時,很難知道下層模塊是如何實現的,因此,上層模塊只需要定義好自己需要但不能預料的接口(也就是回調接口),當下層模塊調用上層模塊時,根據當前需要的實現回調接口,並通過註冊或參數方式傳入上層模塊即可,這樣就實現了下層調用上層,並且上層還能根據傳入的引用來調用下層的具體實現,將程序的靈活性大大的增加