Android เทียบเท่ากับ NSNotificationCenter


95

ในกระบวนการย้ายแอปพลิเคชัน iPhone ไปยัง Android ฉันกำลังมองหาวิธีที่ดีที่สุดในการสื่อสารภายในแอป ความตั้งใจดูเหมือนจะเป็นหนทางที่จะไปนี่เป็นทางเลือกที่ดีที่สุด (เท่านั้น) หรือไม่? NSUserDefaults ดูเหมือนจะมีน้ำหนักเบากว่า Intents มากทั้งในด้านประสิทธิภาพและการเข้ารหัส

ฉันควรเพิ่มว่าฉันมีคลาสย่อยของแอปพลิเคชันสำหรับสถานะ แต่ฉันต้องการให้กิจกรรมอื่นรับรู้เหตุการณ์


3
สำหรับผู้มาใหม่ในหัวข้อนี้คำตอบที่สองคือคำตอบที่ดีที่สุด เลื่อนลง ...
Stephan

คำตอบ:


6

คุณสามารถลองสิ่งนี้: http://developer.android.com/reference/java/util/Observer.html


42
คำตอบของชิกิด้านล่างดีกว่ามาก
dsaff

5
@dsaff แม้จะเป็นคำตอบที่สมบูรณ์กว่า แต่คำตอบของฉันไม่ผิดฉันไม่สมควรได้รับ -1 อย่างชัดเจน สิ่งที่สมเหตุสมผลคือให้คุณ +1 คำตอบของชิกิ
Rui Peres

4
Shiki เป็นคำตอบที่ดีกว่าสำหรับคำถาม
Ramz

4
โปรดทราบว่าควรลงคะแนนเฉพาะคำตอบที่ไม่ถูกต้องทางเทคนิคและคำตอบที่เป็นสแปม - คำตอบนี้ไม่เหมาะกับทั้งคู่ +1 สำหรับค่าตอบแทนและ +1 สำหรับ Shiki ด้วยเพราะนั่นเป็นคำตอบที่ดี

352

ที่ดีที่สุดเทียบเท่าที่ฉันพบคือLocalBroadcastManagerซึ่งเป็นส่วนหนึ่งของแพคเกจสนับสนุน Android

จากเอกสาร LocalBroadcastManager:

ผู้ช่วยในการลงทะเบียนและส่งการถ่ายทอดเจตนาไปยังวัตถุในพื้นที่ภายในกระบวนการของคุณ นี่เป็นข้อดีหลายประการในการส่งการออกอากาศทั่วโลกด้วย sendBroadcast (Intent):

  • คุณทราบดีว่าข้อมูลที่คุณกำลังแพร่ภาพจะไม่ออกจากแอปของคุณดังนั้นไม่ต้องกังวลว่าข้อมูลส่วนตัวจะรั่วไหล
  • เป็นไปไม่ได้ที่แอปพลิเคชันอื่นจะส่งการออกอากาศเหล่านี้ไปยังแอปของคุณดังนั้นคุณไม่จำเป็นต้องกังวลว่าจะมีช่องโหว่ด้านความปลอดภัยที่สามารถใช้ประโยชน์ได้
  • มีประสิทธิภาพมากกว่าการส่งออกอากาศทั่วโลกผ่านระบบ

เมื่อใช้สิ่งนี้คุณสามารถพูดได้ว่าIntentan เทียบเท่ากับNSNotificationไฟล์. นี่คือตัวอย่าง:

ReceiverActivity.java

"custom-event-name"กิจกรรมที่นาฬิกาสำหรับการแจ้งเตือนสำหรับเหตุการณ์ที่มีชื่อ

@Override
public void onCreate(Bundle savedInstanceState) {

  ...
  
  // Register to receive messages.
  // This is just like [[NSNotificationCenter defaultCenter] addObserver:...]
  // We are registering an observer (mMessageReceiver) to receive Intents
  // with actions named "custom-event-name".
  LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
      new IntentFilter("custom-event-name"));
}

// Our handler for received Intents. This will be called whenever an Intent
// with an action named "custom-event-name" is broadcasted.
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Get extra data included in the Intent
    String message = intent.getStringExtra("message");
    Log.d("receiver", "Got message: " + message);
  }
};

@Override
protected void onDestroy() {
  // Unregister since the activity is about to be closed.
  // This is somewhat like [[NSNotificationCenter defaultCenter] removeObserver:name:object:] 
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
  super.onDestroy();
}

SenderActivity.java

กิจกรรมที่สองที่ส่ง / ออกอากาศการแจ้งเตือน

@Override
public void onCreate(Bundle savedInstanceState) {
  
  ...
  
  // Every time a button is clicked, we want to broadcast a notification.
  findViewById(R.id.button_send).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      sendMessage();
    }
  });
}

// Send an Intent with an action named "custom-event-name". The Intent sent should 
// be received by the ReceiverActivity.
private void sendMessage() {
  Log.d("sender", "Broadcasting message");
  Intent intent = new Intent("custom-event-name");
  // You can also include some extra data.
  intent.putExtra("message", "This is my message!");
  LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

ด้วยโค้ดข้างต้นทุกครั้งที่ปุ่มR.id.button_sendมีการคลิกเจตจำนงออกอากาศและได้รับโดยในmMessageReceiverReceiverActivity

ผลลัพธ์การดีบักควรมีลักษณะดังนี้:

01-16 10:35:42.413: D/sender(356): Broadcasting message
01-16 10:35:42.421: D/receiver(356): Got message: This is my message! 

11
ขอบคุณมากที่สละเวลาเขียนคำตอบที่มีประโยชน์และมีรายละเอียดเช่นนี้
Chris Lacy

14
คุณอาจไม่ควรเรียก registerReceiver ในเมธอด onCreate เพราะจะทำให้กิจกรรมของคุณรั่วไหลและเมธอด onDestroy ของคุณจะไม่ถูกเรียก onResume ดูเหมือนจะเป็นทางเลือกที่ดีกว่าในการเรียก registerReceiver และ onPause เพื่อเรียก unregisterReceiver
Stephane JAIS

4
สมบูรณ์เทียบเท่าNSNotificationCenterควรเป็นคำตอบที่ยอมรับ!
Leon Storey

ฉันอยากจะชี้ให้เห็นว่าการใช้การแจ้งเตือนทั่วโลกอาจนำคุณไปสู่การออกแบบที่ยุ่งเหยิง ลองนึกดูว่าอะไรจะเป็นข้อต่อที่ดีที่สุดระหว่างส่วนประกอบของคุณก่อนที่จะข้ามไปสู่วิธีง่ายๆ บางครั้งการใช้ Listeners หรือสิ่งที่คล้ายกับ iOS delegate pattern และอื่น ๆ จะดีกว่า
Pedro Andrade

ขอบคุณสิ่งนี้ได้ผลสำหรับฉัน @ ชิกิได้โปรดคิดว่าจะให้ความเห็นกับคำถามนี้ได้ไหมstackoverflow.com/questions/25598696/…
Axel

16

นี่คือสิ่งที่คล้ายกับคำตอบของ @Shiki แต่จากมุมมองของนักพัฒนา iOS และศูนย์การแจ้งเตือน

ขั้นแรกให้สร้างบริการ NotificationCenter บางประเภท:

public class NotificationCenter {

 public static void addObserver(Context context, NotificationType notification, BroadcastReceiver responseHandler) {
    LocalBroadcastManager.getInstance(context).registerReceiver(responseHandler, new IntentFilter(notification.name()));
 }

 public static void removeObserver(Context context, BroadcastReceiver responseHandler) {
    LocalBroadcastManager.getInstance(context).unregisterReceiver(responseHandler);
 }

 public static void postNotification(Context context, NotificationType notification, HashMap<String, String> params) {
    Intent intent = new Intent(notification.name());
    // insert parameters if needed
    for(Map.Entry<String, String> entry : params.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        intent.putExtra(key, value);
    }
    LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
 }
}

จากนั้นคุณจะต้องมีประเภท enum เพื่อป้องกันความผิดพลาดในการเข้ารหัสด้วยสตริง - (NotificationType):

public enum NotificationType {

   LoginResponse;
   // Others

}

นี่คือการใช้งาน (เพิ่ม / ลบผู้สังเกตการณ์) เช่นในกิจกรรม:

public class LoginActivity extends AppCompatActivity{

    private BroadcastReceiver loginResponseReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
           // do what you need to do with parameters that you sent with notification

           //here is example how to get parameter "isSuccess" that is sent with notification
           Boolean result = Boolean.valueOf(intent.getStringExtra("isSuccess"));
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        //subscribe to notifications listener in onCreate of activity
        NotificationCenter.addObserver(this, NotificationType.LoginResponse, loginResponseReceiver);
    }

    @Override
    protected void onDestroy() {
        // Don't forget to unsubscribe from notifications listener
        NotificationCenter.removeObserver(this, loginResponseReceiver);
        super.onDestroy();
    }
}

และในที่สุดนี่คือวิธีที่เราโพสต์การแจ้งเตือนไปยัง NotificationCenter จากบริการโทรกลับหรือส่วนที่เหลือหรืออะไรก็ตาม:

public void loginService(final Context context, String username, String password) {
    //do some async work, or rest call etc.
    //...

    //on response, when we want to trigger and send notification that our job is finished
    HashMap<String,String> params = new HashMap<String, String>();          
    params.put("isSuccess", String.valueOf(false));
    NotificationCenter.postNotification(context, NotificationType.LoginResponse, params);
}

แค่นั้นเองไชโย!


ขอบคุณสำหรับการแก้ปัญหาของคุณ! ฉันพบว่าการใช้Bundle paramsแทนHashMapจะสะดวกกว่าสำหรับการส่งพารามิเตอร์ประเภทต่างๆ มีการเชื่อมต่อที่ดีระหว่างIntentและBundle:intent.putExtras(params)
zubko

4

คุณสามารถใช้สิ่งนี้: http://developer.android.com/reference/android/content/BroadcastReceiver.htmlซึ่งให้ลักษณะการทำงานที่คล้ายกัน

คุณสามารถลงทะเบียนผู้รับโดยใช้โปรแกรมผ่าน Context.registerReceiver (BroadcastReceiver, IntentFilter) และจะจับ Intent ที่ส่งผ่าน Context.sendBroadcast (Intent)

อย่างไรก็ตามโปรดทราบว่าผู้รับจะไม่ได้รับการแจ้งเตือนหากกิจกรรม (บริบท) ถูกหยุดชั่วคราว


หมายเหตุการออกแบบอย่างรวดเร็ว: BroadcastReceivers และ NSNotificationCenter สามารถทำงานเป็นตัวรวบรวมเหตุการณ์ได้ ข้อได้เปรียบเหนือผู้ร่วมประชุมหรือผู้สังเกตการณ์คือผู้ส่งและผู้รับจะแยกจากกัน (จริงๆแล้วมีการเชื่อมต่อข้อความหรือข้อมูล แต่เป็นประเภทการมีเพศสัมพันธ์ที่อ่อนแอที่สุดประเภทหนึ่ง) แก้ไขด้วยการแก้ไข
AngraX

4

ฉันพบว่าการใช้ EventBus ของ Guava lib เป็นวิธีที่ง่ายที่สุดสำหรับการสื่อสารในรูปแบบการสมัครสมาชิกระหว่างคอมโพเนนต์โดยไม่ต้องให้คอมโพเนนต์ลงทะเบียนอย่างชัดเจน

ดูตัวอย่างได้ที่https://code.google.com/p/guava-libraries/wiki/EventBusExplained

// Class is typically registered by the container.
class EventBusChangeRecorder {
  @Subscribe public void recordCustomerChange(ChangeEvent e) {
    recordChange(e.getChange());
  }

// somewhere during initialization
eventBus.register(this);

}

// much later
public void changeCustomer() {
  eventBus.post(new ChangeEvent("bla bla") );
} 

คุณสามารถเพิ่ม lib นี้ได้ง่ายๆบน Android Studio โดยเพิ่มการอ้างอิงให้กับ build.gradle ของคุณ:

compile 'com.google.guava:guava:17.0'

เหมาะสำหรับโค้ดด้านข้าง 'model' ซึ่งขึ้นกับแพลตฟอร์มน้อยกว่า
karmakaze

2

Kotlin : นี่คือเวอร์ชันของ @ Shiki ใน Kotlin ที่มี refactor เล็กน้อยในส่วนย่อย

  1. ลงทะเบียนผู้สังเกตการณ์ใน Fragment

Fragment.kt

class MyFragment : Fragment() {

    private var mContext: Context? = null

    private val mMessageReceiver = object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            //Do something here after you get the notification
            myViewModel.reloadData()
        }
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)

        mContext = context
    }

    override fun onStart() {
        super.onStart()
        registerSomeUpdate()
    }

    override fun onDestroy() {
        LocalBroadcastManager.getInstance(mContext!!).unregisterReceiver(mMessageReceiver)
        super.onDestroy()
    }

    private fun registerSomeUpdate() {
        LocalBroadcastManager.getInstance(mContext!!).registerReceiver(mMessageReceiver, IntentFilter(Constant.NOTIFICATION_SOMETHING_HAPPEN))
    }

}
  1. โพสต์การแจ้งเตือนได้ทุกที่ คุณต้องการบริบทเท่านั้น

    LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(Constant.NOTIFICATION_SOMETHING_HAPPEN))```
    

PS :

  1. คุณสามารถเพิ่ม Constant.kt เช่นฉันเพื่อจัดระเบียบการแจ้งเตือนได้ดี ค่าคงที่
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. สำหรับบริบทในส่วนย่อยคุณสามารถใช้activity(บางครั้งnull) หรือconextเหมือนกับสิ่งที่ฉันใช้

0

คุณสามารถใช้การอ้างอิงที่อ่อนแอ

ด้วยวิธีนี้คุณสามารถจัดการหน่วยความจำด้วยตัวคุณเองและเพิ่มและลบผู้สังเกตการณ์ได้ตามต้องการ

เมื่อคุณ addObserver เพิ่มพารามิเตอร์เหล่านี้ - ส่งบริบทนั้นจากกิจกรรมที่คุณกำลังเพิ่มในอินเทอร์เฟซว่างเพิ่มชื่อการแจ้งเตือนและเรียกใช้เมธอดเพื่อเรียกใช้อินเทอร์เฟซ

วิธีการเรียกใช้อินเทอร์เฟซจะมีฟังก์ชันที่เรียกว่า run เพื่อส่งคืนข้อมูลที่คุณกำลังส่งผ่านสิ่งนี้

public static interface Themethodtorun {
        void run(String notification_name, Object additional_data);
    }

สร้างคลาสสังเกตการณ์ที่เรียกใช้การอ้างอิงด้วยอินเทอร์เฟซที่ว่างเปล่า สร้างอินเทอร์เฟซ Themethodtorun ของคุณจากบริบทที่ส่งผ่านใน addobserver

เพิ่มการสังเกตลงในโครงสร้างข้อมูล

ในการเรียกมันจะเป็นวิธีการเดียวกัน แต่สิ่งที่คุณต้องทำคือค้นหาชื่อการแจ้งเตือนเฉพาะในโครงสร้างข้อมูลให้ใช้ Themethodtorun.run (notification_name, data)

การดำเนินการนี้จะส่งการติดต่อกลับไปยังที่ที่คุณเคยสร้างผู้สังเกตการณ์ด้วยชื่อการแจ้งเตือนเฉพาะ อย่าลืมลบออกเมื่อเสร็จสิ้น!

นี่เป็นข้อมูลอ้างอิงที่ดีสำหรับการอ้างอิงที่อ่อนแอ

http://learningviacode.blogspot.co.nz/2014/02/weak-references-in-java.html

ฉันกำลังอัปโหลดโค้ดนี้ไปยัง github ลืมตา!


0

ฉันเขียนกระดาษห่อตัวที่สามารถทำงานเดียวกันนี้ได้เทียบเท่ากับ iOS โดยใช้ LiveData

เครื่องห่อ:

class ObserverNotify {
    private val liveData = MutableLiveData<Nothing>()


    fun postNotification() {
        GlobalScope.launch {
            withContext(Dispatchers.Main) {
                liveData.value = liveData.value
            }
        }
    }

    fun observeForever(observer: () -> Unit) {
        liveData.observeForever { observer() }
    }

    fun observe(owner: LifecycleOwner, observer: () -> Unit) {
        liveData.observe(owner) { observer()}
    }

}

class ObserverNotifyWithData<T> {
    private val liveData = MutableLiveData<T>()


    fun postNotification(data: T) {
        GlobalScope.launch {
            withContext(Dispatchers.Main) {
                liveData.value = data
            }
        }
    }

    fun observeForever(observer: (T) -> Unit) {
        liveData.observeForever { observer(it) }
    }

    fun observe(owner: LifecycleOwner, observer: (T) -> Unit) {
        liveData.observe(owner) { observer(it) }
    }

}

การประกาศประเภทผู้สังเกตการณ์:

object ObserverCenter {
    val moveMusicToBeTheNextOne: ObserverNotifyWithData<Music> by lazy { ObserverNotifyWithData() }
    val playNextMusic: ObserverNotify by lazy { ObserverNotify() }
    val newFCMTokenDidHandle: ObserverNotifyWithData<String?> by lazy { ObserverNotifyWithData() }
}

ในกิจกรรมที่ต้องปฏิบัติ:

ObserverCenter.newFCMTokenDidHandle.observe(this) {
    // Do stuff
}

แจ้ง:

ObserverCenter.playNextMusic.postNotification()
ObserverCenter.newFCMTokenDidHandle.postNotification("MyData")
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.