Jak zdefiniować wywołania zwrotne w systemie Android?

152

Podczas ostatniego Google IO odbyła się prezentacja na temat wdrażania restous client applications. Niestety była to tylko dyskusja na wysokim poziomie, bez kodu źródłowego implementacji.

Na tym diagramie na ścieżce powrotnej znajdują się różne wywołania zwrotne do innych metod.

slajd prezentacji google io

Jak mam zadeklarować, jakie są te metody?

Rozumiem ideę wywołania zwrotnego - fragmentu kodu, który jest wywoływany po wystąpieniu określonego zdarzenia, ale nie wiem, jak go zaimplementować. Jedynym sposobem, w jaki dotychczas zaimplementowałem wywołania zwrotne, było zastępowanie różnych metod (na przykład onActivityResult).

Czuję, że mam podstawową wiedzę na temat wzorca projektowego, ale ciągle się potykam, jak radzić sobie ze ścieżką powrotną.

user409841
źródło
3
Dokładnie to, czego potrzebujesz. Szukałem tego samego i natknąłem się na to: javaworld.com/javaworld/javatips/jw-javatip10.html
Sid

Odpowiedzi:

218

W wielu przypadkach masz interfejs i przekazujesz obiekt, który go implementuje. Na przykład okna dialogowe mają OnClickListener.

Tak jak w przypadku losowego przykładu:

// 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
   }
};

Prawdopodobnie zawiodłem składnię w opcji 2. Jest wcześnie.

EboMike
źródło
5
Dobrym przykładem na opanowanie tej techniki jest to, jak fragment powinien komunikować się z innym fragmentem za pośrednictwem jego udostępnionego Działania: developer.android.com/guide/components/ ...
Jordy
Próbuję „zaimplementować” interfejs MyCallback w nowym działaniu, które się nie powiodło i system wymaga ode mnie edycji w nim ścieżki źródłowej. Jak więc przeprowadzić „callback” ze starej do nowej czynności?
Antoine Murion
3
Mówi, że zmienna wywołania zwrotnego w klasie
roboczej
Czy ktoś mógłby podać ekwiwalent kotlin?
Tooniis
52

Kiedy coś się dzieje, odpalam zdarzenie, którego nasłuchuje moja aktywność:

// ZADEKLAROWANY W (NIESTANDARDOWYM) WIDOKU

    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;
    }

// ZADEKLAROWANY W DZIAŁALNOŚCI

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

Jeśli chcesz dowiedzieć się więcej o komunikacji (callbackach) między fragmentami, zajrzyj tutaj: http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity

HGPB
źródło
1
Samouczek #CommunicatingWithActivity był niesamowity. W końcu zrozumiał, jak używać wywołań zwrotnych po kilku próbach.
Moises Jimenez
Świetna odpowiedź. Dzięki!
iYonatan
Proste i łatwe do zrozumienia. Dzięki!
Glenn J. Schworak
38

Nie ma potrzeby, aby zdefiniować nowy interfejs, kiedy można użyć istniejącego: android.os.Handler.Callback. Przekaż obiekt typu Callback i wywołaj callback's handleMessage(Message msg).

smok
źródło
ale jak to zrobić?
majurageerthan
26

Przykład implementacji metody wywołania zwrotnego przy użyciu interfejsu.

Zdefiniuj interfejs, NewInterface.java .

pakiet javaapplication1;

public interface NewInterface {
    void callback();
}

Utwórz nową klasę, NewClass.java . Wywoła metodę callback w głównej klasie.

package javaapplication1;

public class NewClass {

    private NewInterface mainClass;

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

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

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

Główna klasa, JavaApplication1.java, do implementacji interfejsu NewInterface - metoda callback (). Stworzy i wywoła obiekt NewClass. Następnie obiekt NewClass wywoła po kolei swoją metodę 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");
    }

}
Amol Patil
źródło
1
do tej pory używaliśmy interfejsów do wywołania zwrotnego, ale teraz square opracował bibliotekę jako Event-bus Otto. Jest to naprawdę szybsze i pomocne.
Amol Patil
20

aby trochę wyjaśnić odpowiedź smoka (ponieważ zajęło mi trochę czasu, zanim wymyśliłem, co z tym zrobić Handler.Callback):

Handlermoże być używany do wykonywania wywołań zwrotnych w bieżącym lub innym wątku, przekazując go Messages. Messageposiada dane do wykorzystania z zwrotnego. a Handler.Callbackmożna przekazać do konstruktora programu Handler, aby uniknąć bezpośredniego rozszerzania programu Handler. w ten sposób, aby wykonać jakiś kod przez wywołanie zwrotne z bieżącego wątku:

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);

EDYCJA: właśnie sobie uświadomiłem, że jest lepszy sposób na uzyskanie tego samego wyniku (bez kontroli dokładnego czasu wykonania wywołania zwrotnego):

post(new Runnable() {
    @Override
    public void run() {
        <code to be executed during callback>
    }
});
MrGnu
źródło
1
Twój post Runnable znajduje się w metodzie handleMessage?
IgorGanapolsky
+1 za najlepszą odpowiedź. CallbackWersja podoba mi się bardziej, ponieważ niekoniecznie masz dostęp do danych potrzebnych Runnable.run()w momencie jej tworzenia
Kirby
Uwaga: „Chociaż konstruktor Message jest publiczny, najlepszym sposobem na uzyskanie jednego z nich jest wywołanie metody Message.obtain () lub jednej z metod Handler.obtainMessage (), które ściągną je z puli odzyskanych obiektów”. - stąd
jk7
10

Możesz również użyć LocalBroadcastdo tego celu. Oto krótka wskazówka

Utwórz odbiornik transmisji:

   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
    }

W ten sposób możesz to wywołać

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

Wyrejestruj odbiornik w onPause:

protected void onPause() {
  super.onPause();
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
Rohit Mandiwal
źródło