如何在Android中定义回调函数?

159

在最近的 Google IO 大会上,有一场关于实现 RESTful 客户端应用程序的演示。不幸的是,这只是一个高层次的讨论,并没有实现的源代码。

在这个示意图中,在返回路径上会有各种不同的回调到其他方法。

google io presentation slide

我该如何声明这些方法?

我理解回调的概念——当某个事件发生后会调用一段代码,但我不知道该如何实现。目前我实现回调的唯一方法是覆盖各种方法(例如 onActivityResult)。

我觉得我对设计模式有一个基本的了解,但我总是在如何处理返回路径方面遇到问题。


3
你需要什么就有什么了。我也在寻找同样的东西时发现了这个:http://www.javaworld.com/javaworld/javatips/jw-javatip10.html。 - Sid
7个回答

231
在许多情况下,您会拥有一个接口并传递实现它的对象。例如,对话框具有 OnClickListener 接口。
仅以随意的示例为例:
// The callback interface
interface MyCallback {
    void callbackCall();
}

// The class that takes the callback
class Worker {
   MyCallback callback;

   void onEvent() {
      callback.callbackCall();
   }
}

// Option 1:

class Callback implements MyCallback {
   void callbackCall() {
      // callback code goes here
   }
}

worker.callback = new Callback();

// Option 2:

worker.callback = new MyCallback() {

   void callbackCall() {
      // callback code goes here
   }
};

我可能在选项2中弄错了语法,还太早。


6
一个很好的例子来掌握这种技术是如何通过共享的Activity使一个fragment与另一个fragment进行通信:http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity - Jordy
我尝试在一个新的活动中“实现”MyCallback接口,但不成功,系统要求我编辑其源路径。那么我该如何从旧活动中执行“回调”到新活动呢? - Antoine Murion
3
工作类中的回调变量对我来说是 null。 - iYonatan
1
有人能给出 Kotlin 的等效代码吗? - Tooniis
你所期望的最简洁易懂的例子! - mdev

55

当我的视图发生某些事情时,我会触发一个事件,我的活动在监听该事件:

// 在(自定义)视图中声明

    private OnScoreSavedListener onScoreSavedListener;
    public interface OnScoreSavedListener {
        public void onScoreSaved();
    }
    // ALLOWS YOU TO SET LISTENER && INVOKE THE OVERIDING METHOD 
    // FROM WITHIN ACTIVITY
    public void setOnScoreSavedListener(OnScoreSavedListener listener) {
        onScoreSavedListener = listener;
    }

// 在Activity中声明

    MyCustomView slider = (MyCustomView) view.findViewById(R.id.slider)
    slider.setOnScoreSavedListener(new OnScoreSavedListener() {
        @Override
        public void onScoreSaved() {
            Log.v("","EVENT FIRED");
        }
    });

如果你想了解更多关于片段之间通信(回调)的内容,请参见这里: http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity


1
那个 #CommunicatingWithActivity 的教程太棒了。终于在多次尝试后明白了如何使用回调函数。 - Moises Jimenez
非常好的回答。谢谢! - iYonatan
简单易懂。谢谢! - Glenn J. Schworak

39
无需定义新接口,您可以使用现有的接口:android.os.Handler.Callback。传递一个类型为Callback的对象,并调用回调函数的handleMessage(Message msg)方法。

但是如何做到呢? - majurageerthan

29

使用接口实现回调方法的示例。

定义接口NewInterface.java

包:javaapplication1

public interface NewInterface {
    void callback();
}

创建一个新类,NewClass.java。它将在主类中调用回调方法。

package javaapplication1;

public class NewClass {

    private NewInterface mainClass;

    public NewClass(NewInterface mClass){
        mainClass = mClass;
    }

    public void calledFromMain(){
        //Do somthing...

        //call back main
        mainClass.callback();
    }
}

主类JavaApplication1.java要实现接口NewInterface的回调方法callback()。它将创建并调用NewClass对象。然后,NewClass对象将依次回调它自己的callback()方法。

package javaapplication1;
public class JavaApplication1 implements NewInterface{

    NewClass newClass;

    public static void main(String[] args) {

        System.out.println("test...");

        JavaApplication1 myApplication = new JavaApplication1();
        myApplication.doSomething();

    }

    private void doSomething(){
        newClass = new NewClass(this);
        newClass.calledFromMain();
    }

    @Override
    public void callback() {
        System.out.println("callback");
    }

}

1
到目前为止,我们一直在使用接口进行回调,但现在Square已经开发了一个名为Event-bus Otto的库。它真的更快、更有帮助。 - Amol Patil

22

为了进一步澄清龙答案的内容(因为我花了一些时间才弄清楚该如何处理Handler.Callback),Handler可用于通过传递Message在当前线程或另一个线程中执行回调函数。 Message保存用于从回调中使用的数据。可以将Handler.Callback传递给Handler的构造函数,以避免直接扩展Handler。因此,要通过回调在当前线程中执行某些代码:

Message message = new Message();
<set data to be passed to callback - eg message.obj, message.arg1 etc - here>

Callback callback = new Callback() {
    public boolean handleMessage(Message msg) {
        <code to be executed during callback>
    }
};

Handler handler = new Handler(callback);
handler.sendMessage(message);

编辑:刚刚意识到有一种更好的方法可以获得相同的结果(除了不能精确控制回调函数何时执行):

post(new Runnable() {
    @Override
    public void run() {
        <code to be executed during callback>
    }
});

1
你的Runnable帖子在handleMessage方法里面吗? - IgorGanapolsky
我更喜欢Callback版本,因为在构造Runnable.run()时可能没有访问所需数据的权限。 - Kirby
请注意:“虽然Message的构造函数是公共的,但获取其中之一的最佳方法是调用Message.obtain()或Handler.obtainMessage()方法之一,这将从回收对象池中提取它们。” -- 来自此处 - jk7

11

您也可以使用LocalBroadcast来实现这个目的。以下是一个快速指南:

创建广播接收器:

   LocalBroadcastManager.getInstance(this).registerReceiver(
            mMessageReceiver, new IntentFilter("speedExceeded"));

private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Double currentSpeed = intent.getDoubleExtra("currentSpeed", 20);
        Double currentLatitude = intent.getDoubleExtra("latitude", 0);
        Double currentLongitude = intent.getDoubleExtra("longitude", 0);
        //  ... react to local broadcast message
    }

以下是如何触发它的方法

Intent intent = new Intent("speedExceeded");
intent.putExtra("currentSpeed", currentSpeed);
intent.putExtra("latitude", latitude);
intent.putExtra("longitude", longitude);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

在onPause中取消注册接收器:

protected void onPause() {
  super.onPause();
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}

1
假设主函数是触发事件的活动:
fun main() {

val worker = Worker()

worker.setOnCallListener(
    object: OnCallListener {
        override fun onCall() {
            // here we define what should happen
            // when the event accures
            print("event happend")
        }
    }
)

// most events will be called from Android system itself
// but in our case we have to call it manually
worker.listener.onCall()
}

Worker类有一个OnCallListener接口类型的实例,以及一个设置其值的方法:

class Worker() {
    lateinit var listener: OnCallListener

    fun setOnCallListener(listener: OnCallListener) {
        this.listener = listener
    }
}

OnCallListener 接口看起来像这样:

interface OnCallListener {
    fun onCall()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接