Trong lập trình máy tính, một cuộc gọi lại là một tham chiếu đến mã thực thi, hoặc một đoạn mã thực thi, được truyền dưới dạng đối số cho mã khác. Điều này cho phép lớp phần mềm cấp thấp hơn gọi một chương trình con (hoặc hàm) được xác định trong lớp cấp cao hơn. - Wikipedia
Gọi lại trong C bằng cách sử dụng con trỏ hàm
Trong C, gọi lại được thực hiện bằng cách sử dụng Hàm con trỏ. Con trỏ hàm - như tên cho thấy, là một con trỏ tới hàm.
Ví dụ: int (* ptrFunc) ();
Ở đây, ptrFunc là một con trỏ tới một hàm không có đối số và trả về một số nguyên. KHÔNG quên đặt trong ngoặc đơn, nếu không trình biên dịch sẽ cho rằng ptrFunc là tên hàm bình thường, không mất gì và trả về một con trỏ tới một số nguyên.
Đây là một số mã để chứng minh con trỏ hàm.
#include<stdio.h>
int func(int, int);
int main(void)
{
int result1,result2;
/* declaring a pointer to a function which takes
two int arguments and returns an integer as result */
int (*ptrFunc)(int,int);
/* assigning ptrFunc to func's address */
ptrFunc=func;
/* calling func() through explicit dereference */
result1 = (*ptrFunc)(10,20);
/* calling func() through implicit dereference */
result2 = ptrFunc(10,20);
printf("result1 = %d result2 = %d\n",result1,result2);
return 0;
}
int func(int x, int y)
{
return x+y;
}
Bây giờ chúng ta hãy cố gắng hiểu khái niệm Callback trong C bằng cách sử dụng con trỏ hàm.
Chương trình hoàn chỉnh có ba tệp: callback.c, reg_callback.h và reg_callback.c.
/* callback.c */
#include<stdio.h>
#include"reg_callback.h"
/* callback function definition goes here */
void my_callback(void)
{
printf("inside my_callback\n");
}
int main(void)
{
/* initialize function pointer to
my_callback */
callback ptr_my_callback=my_callback;
printf("This is a program demonstrating function callback\n");
/* register our callback function */
register_callback(ptr_my_callback);
printf("back inside main program\n");
return 0;
}
/* reg_callback.h */
typedef void (*callback)(void);
void register_callback(callback ptr_reg_callback);
/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"
/* registration goes here */
void register_callback(callback ptr_reg_callback)
{
printf("inside register_callback\n");
/* calling our callback function my_callback */
(*ptr_reg_callback)();
}
Nếu chúng tôi chạy chương trình này, đầu ra sẽ là
Đây là một chương trình thể hiện chức năng gọi lại bên trong register_callback bên trong my_callback trở lại bên trong chương trình chính
Hàm lớp cao hơn gọi hàm lớp thấp hơn như một cuộc gọi bình thường và cơ chế gọi lại cho phép hàm lớp thấp hơn gọi hàm lớp cao hơn thông qua một con trỏ đến hàm gọi lại.
Gọi lại trong Java bằng giao diện
Java không có khái niệm về con trỏ hàm. Nó thực hiện cơ chế gọi lại thông qua cơ chế Giao diện của nó Ở đây thay vì một con trỏ hàm, chúng ta khai báo một Giao diện có một phương thức sẽ được gọi khi callee hoàn thành nhiệm vụ của nó
Hãy để tôi chứng minh điều đó qua một ví dụ:
Giao diện gọi lại
public interface Callback
{
public void notify(Result result);
}
Người gọi hoặc Lớp cấp cao hơn
public Class Caller implements Callback
{
Callee ce = new Callee(this); //pass self to the callee
//Other functionality
//Call the Asynctask
ce.doAsynctask();
public void notify(Result result){
//Got the result after the callee has finished the task
//Can do whatever i want with the result
}
}
Hàm Callee hoặc lớp dưới
public Class Callee {
Callback cb;
Callee(Callback cb){
this.cb = cb;
}
doAsynctask(){
//do the long running task
//get the result
cb.notify(result);//after the task is completed, notify the caller
}
}
Gọi lại bằng cách sử dụng mẫu EventListener
Mẫu này được sử dụng để thông báo 0 đến n số lượng Người quan sát / Người nghe rằng một nhiệm vụ cụ thể đã kết thúc
Sự khác biệt giữa cơ chế gọi lại và cơ chế EventListener / Observer là trong cuộc gọi lại, callee thông báo cho người gọi duy nhất, trong khi trong Eventlisener / Observer, callee có thể thông báo cho bất kỳ ai quan tâm đến sự kiện đó (thông báo có thể đi đến một số phần khác của ứng dụng chưa kích hoạt tác vụ)
Hãy để tôi giải thích nó thông qua một ví dụ.
Giao diện sự kiện
public interface Events {
public void clickEvent();
public void longClickEvent();
}
Lớp học Widget
package com.som_itsolutions.training.java.exampleeventlistener;
import java.util.ArrayList;
import java.util.Iterator;
public class Widget implements Events{
ArrayList<OnClickEventListener> mClickEventListener = new ArrayList<OnClickEventListener>();
ArrayList<OnLongClickEventListener> mLongClickEventListener = new ArrayList<OnLongClickEventListener>();
@Override
public void clickEvent() {
// TODO Auto-generated method stub
Iterator<OnClickEventListener> it = mClickEventListener.iterator();
while(it.hasNext()){
OnClickEventListener li = it.next();
li.onClick(this);
}
}
@Override
public void longClickEvent() {
// TODO Auto-generated method stub
Iterator<OnLongClickEventListener> it = mLongClickEventListener.iterator();
while(it.hasNext()){
OnLongClickEventListener li = it.next();
li.onLongClick(this);
}
}
public interface OnClickEventListener
{
public void onClick (Widget source);
}
public interface OnLongClickEventListener
{
public void onLongClick (Widget source);
}
public void setOnClickEventListner(OnClickEventListener li){
mClickEventListener.add(li);
}
public void setOnLongClickEventListner(OnLongClickEventListener li){
mLongClickEventListener.add(li);
}
}
Nút lớp
public class Button extends Widget{
private String mButtonText;
public Button (){
}
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}
Hộp kiểm tra lớp
public class CheckBox extends Widget{
private boolean checked;
public CheckBox() {
checked = false;
}
public boolean isChecked(){
return (checked == true);
}
public void setCheck(boolean checked){
this.checked = checked;
}
}
Lớp hoạt động
gói com.som_itsolutions.training.java.exampleeventlistener;
public class Activity implements Widget.OnClickEventListener
{
public Button mButton;
public CheckBox mCheckBox;
private static Activity mActivityHandler;
public static Activity getActivityHandle(){
return mActivityHandler;
}
public Activity ()
{
mActivityHandler = this;
mButton = new Button();
mButton.setOnClickEventListner(this);
mCheckBox = new CheckBox();
mCheckBox.setOnClickEventListner(this);
}
public void onClick (Widget source)
{
if(source == mButton){
mButton.setButtonText("Thank you for clicking me...");
System.out.println(((Button) mButton).getButtonText());
}
if(source == mCheckBox){
if(mCheckBox.isChecked()==false){
mCheckBox.setCheck(true);
System.out.println("The checkbox is checked...");
}
else{
mCheckBox.setCheck(false);
System.out.println("The checkbox is not checked...");
}
}
}
public void doSomeWork(Widget source){
source.clickEvent();
}
}
Lớp khác
public class OtherClass implements Widget.OnClickEventListener{
Button mButton;
public OtherClass(){
mButton = Activity.getActivityHandle().mButton;
mButton.setOnClickEventListner(this);//interested in the click event //of the button
}
@Override
public void onClick(Widget source) {
if(source == mButton){
System.out.println("Other Class has also received the event notification...");
}
}
Lớp chính
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Activity a = new Activity();
OtherClass o = new OtherClass();
a.doSomeWork(a.mButton);
a.doSomeWork(a.mCheckBox);
}
}
Như bạn có thể thấy từ đoạn mã trên, chúng ta có một giao diện gọi là các sự kiện về cơ bản liệt kê tất cả các sự kiện có thể xảy ra cho ứng dụng của chúng ta. Lớp Widget là lớp cơ sở cho tất cả các thành phần UI như Nút, Hộp kiểm. Các thành phần UI này là các đối tượng thực sự nhận các sự kiện từ mã khung. Lớp widget thực hiện giao diện Sự kiện và cũng có hai giao diện lồng nhau là OnClickEventListener & OnLongClickEventListener
Hai giao diện này chịu trách nhiệm lắng nghe các sự kiện có thể xảy ra trên các thành phần UI có nguồn gốc Widget như Nút hoặc Hộp kiểm. Vì vậy, nếu chúng ta so sánh ví dụ này với ví dụ Gọi lại trước đó bằng Giao diện Java, hai giao diện này hoạt động như giao diện Gọi lại. Vì vậy, mã cấp cao hơn (Hoạt động ở đây) thực hiện hai giao diện này. Và bất cứ khi nào một sự kiện xảy ra với một widget, mã cấp cao hơn (hoặc phương thức của các giao diện này được triển khai ở mã cấp cao hơn, ở đây là Activity) sẽ được gọi.
Bây giờ hãy để tôi thảo luận về sự khác biệt cơ bản giữa mẫu Callback và Eventlistener. Như chúng tôi đã đề cập rằng sử dụng Callback, Callee chỉ có thể thông báo cho một Người gọi duy nhất. Nhưng trong trường hợp mẫu EventListener, bất kỳ phần hoặc lớp nào khác của Ứng dụng đều có thể đăng ký các sự kiện có thể xảy ra trên Nút hoặc Hộp kiểm. Ví dụ về loại lớp này là OtherClass. Nếu bạn thấy mã của OtherClass, bạn sẽ thấy rằng nó đã tự đăng ký làm người nghe cho ClickEvent có thể xảy ra trong Nút được xác định trong Hoạt động. Điều thú vị là, ngoài Hoạt động (Người gọi), OtherClass này cũng sẽ được thông báo mỗi khi sự kiện nhấp xảy ra trên Nút.