วิธีกำหนด Callbacks ใน Android


152

ในช่วงล่าสุดของ Google IO มีการนำเสนอเกี่ยวกับการใช้แอปพลิเคชันไคลเอนต์ที่สงบ น่าเสียดายที่มันเป็นการสนทนาระดับสูงโดยไม่มีซอร์สโค้ดของการใช้งาน

ในแผนภาพนี้บนเส้นทางการส่งคืนมีการเรียกกลับที่แตกต่างหลากหลายไปยังวิธีการอื่น

สไลด์การนำเสนอของ Google io

ฉันจะประกาศว่าวิธีการเหล่านี้เป็นอย่างไร

ฉันเข้าใจแนวคิดของการโทรกลับ - รหัสชิ้นหนึ่งที่ถูกเรียกหลังจากเหตุการณ์บางอย่างเกิดขึ้น แต่ฉันไม่รู้วิธีใช้งาน วิธีเดียวที่ฉันใช้เรียกกลับจนถึงตอนนี้ได้เอาชนะวิธีการต่าง ๆ (ตัวอย่างเช่น onActivityResult)

ฉันรู้สึกว่าฉันมีความเข้าใจพื้นฐานเกี่ยวกับรูปแบบการออกแบบ แต่ฉันก็ยังคงพัฒนาวิธีการจัดการเส้นทางกลับ


3
สิ่งที่คุณต้องการ ผมกำลังมองหาสิ่งเดียวกันและมาข้ามนี้: javaworld.com/javaworld/javatips/jw-javatip10.html
ซิด

คำตอบ:


218

ในหลายกรณีคุณมีอินเทอร์เฟซและส่งต่อวัตถุที่ใช้งาน ตัวอย่างไดอะล็อกมี 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 มันเร็ว


5
เป็นตัวอย่างที่ดีที่จะได้รับการจับกับเทคนิคนี้เป็นวิธีที่ส่วนควรสื่อสารกับส่วนอื่นผ่านมันกิจกรรมที่ใช้ร่วมกัน: developer.android.com/guide/components/...
Jordy

ฉันพยายามที่จะ "ใช้งาน" อินเทอร์เฟซ MyCallback ในกิจกรรมใหม่ซึ่งไม่สำเร็จและระบบต้องการให้ฉันแก้ไขเส้นทางของแหล่งที่มา ดังนั้นฉันจะดำเนินการ "callBack" จากกิจกรรมเก่าไปยังกิจกรรมใหม่ได้อย่างไร
แอนทอน Murion

3
มันบอกว่าตัวแปรโทรกลับในชั้นคนงานเป็นโมฆะสำหรับฉัน
iYonatan

ใครสามารถให้ค่า kotlin เท่ากัน?
Tooniis

52

เมื่อมีบางสิ่งเกิดขึ้นในมุมมองของฉันฉันจะปิดเหตุการณ์ที่กิจกรรมของฉันกำลังฟังอยู่:

// ประกาศในมุมมอง (กำหนดเอง)

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

// ประกาศในกิจกรรม

    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

38

ไม่จำเป็นต้องกำหนดอินเทอร์เฟซใหม่เมื่อคุณสามารถใช้อินเทอร์เฟซที่มีอยู่: android.os.Handler.Callbackไม่จำเป็นต้องกำหนดอินเตอร์เฟซใหม่เมื่อคุณสามารถใช้อย่างใดอย่างหนึ่งที่มีอยู่:handleMessage(Message msg)ผ่านวัตถุประเภทโทรกลับและวิงวอนของการเรียกกลับ


แต่จะทำอย่างไร?
majurageerthan

26

ตัวอย่างการใช้เมธอด callback โดยใช้อินเตอร์เฟส

กำหนดอินเตอร์เฟซ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 จะโทรกลับเป็นวิธีการโทรกลับ () ในทางกลับกัน

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
จนถึงตอนนี้เราใช้อินเตอร์เฟสสำหรับการโทรกลับ แต่ตอนนี้สี่เหลี่ยมได้พัฒนา lib เป็น event-bus Otto. มันเร็วกว่าและมีประโยชน์จริงๆ
Amol Patil

20

เพื่อชี้แจงเล็กน้อยเกี่ยวกับคำตอบของมังกร (เนื่องจากใช้เวลาสักครู่ในการคิดว่าจะทำอย่างไรกับHandler.Callback):

Handlerสามารถนำมาใช้ในการดำเนินการเรียกกลับในปัจจุบันหรือหัวข้ออื่นโดยผ่านมันMessages Messageเก็บข้อมูลเพื่อนำไปใช้จากการเรียกกลับ a Handler.Callbackสามารถส่งผ่านไปยัง constructor ของ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 ของคุณอยู่ในวิธีการจัดการข้อความ?
IgorGanapolsky

+1 สำหรับคำตอบที่ดีที่สุด ฉันชอบCallbackเวอร์ชันที่ดีกว่าเพราะคุณอาจไม่จำเป็นต้องเข้าถึงข้อมูลที่จำเป็นRunnable.run()ในขณะที่คุณสร้างมันขึ้นมา
Kirby

หมายเหตุ: "แม้ว่าตัวสร้างข้อความจะเป็นแบบสาธารณะวิธีที่ดีที่สุดในการรับหนึ่งในนั้นคือการเรียกใช้ Message.obtain () หรือหนึ่งในวิธีการของ Handler.obtainMessage () ซึ่งจะดึงพวกเขาออกจากกลุ่มวัตถุรีไซเคิล - จากที่นี่
jk7

10

คุณสามารถใช้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);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.