Android使用Callback做為傳遞資料/通知的方法

在一個常見的實作上的情境:當使用者在執行某個行為之後,就執行某些行為。例如當API接收成功之後就跳轉到某個頁面,API接收失敗的時候顯示錯誤訊息,除了可以使用BroadcastReceiver,另外也可以使用Callback的方式達成。

以App常見的實作功能-接收API為例,實作的方式如下:

1.先建立一個Interface。
首先在interface裡面製作兩個function,一個是執行結束要執行的行為:loadComplete(),另一個是顯示訊息:showMsg(String msg)
/**
 * Api執行完之後的callback
 */
public interface ApiCallback {
    void showMsg(String msg); //要顯示的訊息
    void loadComplete(); //api執行完成
}

2.在觸發行為的class裡產生Callback實體。
舉例來說,如果是在API執行之後一定會有該API自己所屬的成功/失敗callback function(或稱method),也就是如果API接收成功了就執行某個function(以Volley來看是onResponse()),而失敗了就執行某個function(以Volley來看是onError())。
於是就可以在Volley的onResponse()加入interface裡的loadComplete(),而在onError()加入showMsg(String msg)
public class MyApi{

    public ApiCallback mCallback;

    public DailyNewsApi(Context context) {
        this.mContext = context;
        mCallback = (ApiCallback) mContext;
    }

    @Override
    public void onError(VolleyError error) {
       super.onError(error);
       mCallback.showMsg("登入失敗"); //傳送失敗訊息內容
    }

    @Override
    public void onResponse(String response) {
       onResponse(response);
       mCallback.loadComplete(); //傳送讀取結束之後要呼叫執行的method
    }
}

3.在等待結果發生的class加入實作Callback
假設有個用來讀取API的Class(可能是使用者可以看得到的前景Activtiy/Fragment,也有可能是背景執行的程式),在API讀取結束之前都會一直等待結果,而這個Class就要實作想執行的Callback。

在此先以一個做為等待API的Activity為例, 在等待API的過程中加入了等待中的ProgressDialog,實作的方式是在Activity加上implements ApiCallback即可。而加入implements ApiCallback之後,Android Studio也會自動提醒我們有兩個未實作的callback method,可以直接在這個Class裡面直接按右鍵,選擇"Generate"→"Implement Methods"(快速鍵: ctrl+ i),就可以自動產生callback method了,然後就在所屬的callback method裡加入自己想要執行的內容。
public class LoadingActivity extends Activity implements ApiCallback {

private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.loading);

        progressDialogInit(); //Loading對話框初始化
        progressDialog.show(); //顯示Loading對話框

    }

    @Override
    public void showMsg(String msg) {
        progressDialog.dismiss(); //關閉Loading對話框
        Toast.makeText(LoadingActivity.this, msg, Toast.LENGTH_SHORT).show(); //利用收到的訊息String直接產生在Toast上
    }

    @Override
    public void loadComplete() {
        progressDialog.dismiss(); //關閉Loading對話框
        gotoNextPage(); //執行完之後到下一個頁面
    }

     /**
     * 進度對話框初始化
     */
    private void progressDialogInit() {
        progressDialog = new ProgressDialog(LoadingActivity.this);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progressDialog.setMessage("請稍候...");
        progressDialog.setCanceledOnTouchOutside(false);
    }
}


此外,在Callback的實作過程有可能會遇到觸發的Class沒有產生Callback實體的情況,而導致下列的錯誤訊息,
java.lang.NullPointerException: Attempt to invoke interface method 'void tw.com.ukyo.ApiCallback.loadComplete()' on a null object reference

所以在產生callback實體時,有可能會有需要下列幾種情境去解決:
如果Fragmenet是觸發行為的class,而Activity為實作callback method的class
因為Fragment是依附在Activity裡面,所以將Fragment的生命週期在onAttach()時傳入的context,強制轉型為自訂的interface的方式來做為reference。其自訂的interface一樣先以本篇中的ApiCallback為例。
public class MyFragment extends Fragment{
    public ApiCallback callback;

    @Override
    public void onAttach(Context context) {
       super.onAttach(context);

       callback = (ApiCallback) context; //產生callback實體
    }
}

如果Activity是觸發行為的class,而其他的某一個Activity為實作callback method的class
/**
 * 觸發行為端的Activity
 */
public class MyActivity extends Activity{
    public ApiCallback callback;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       callback = new ImplementActivty();
    }
}
/**
 * 實作callback端的Activity
 */
public class ImplementActivty extends Activity implements ApiCallback{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.implement_activty);
    }

    @Override
    public void showMsg(String msg) {
        //Do something in this method...
    }

    @Override
    public void loadComplete() {
        //Do something in this method...        
    }
}

如果Activity是觸發行為的class,而其所屬的Fragment為實作callback method的class
/**
 * 觸發行為端的Activity
 */
public class MyActivity extends Activity{
    public ApiCallback callback;
    public MyFragment fragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       callback = (ApiCallback)fragment;//產生callback實體
    }
}

public class MyFragment extends Fragment implements ApiCallback{
    
    @Override
    public void showMsg(String msg) {
        //Do something in this method...
    }

    @Override
    public void loadComplete() {
        //Do something in this method...        
    }
}

留言

這個網誌中的熱門文章

Mac安裝JDK後仍出現沒有runtime的錯誤 No Java runtime present, requesting install

如何實作從API抓取資料顯示在列表頁(ListView)上