NSNotificationCenter ile eşdeğer Android


95

Bir iPhone uygulamasını android üzerinden taşıma sürecinde, uygulama içinde iletişim kurmanın en iyi yolunu arıyorum. Amaçlar gitmenin yolu gibi görünüyor, bu en iyi (tek) seçenek mi? NSUserDefaults, hem performans hem de kodlamada Amaçlardan çok daha hafif görünüyor.

Ayrıca, state için bir uygulama alt sınıfım olduğunu da eklemeliyim, ancak başka bir etkinliği bir olaydan haberdar etmem gerekiyor.


3
Bu konuya yeni gelenler için ikinci cevap en iyisidir. Aşağı kaydır ...
Stephan

Yanıtlar:


6

42
Shiki'nin aşağıdaki cevabı çok daha iyi.
dsaff

5
@dsaff daha eksiksiz bir cevap olmasına rağmen, cevabım hiçbir şekilde yanlış değil, açıkça -1'i hak etmiyorum. Sizin için mantıklı olan şey Shiki'nin cevabını + 1'lemenizdir.
Rui Peres

4
Shiki's soru için daha iyi cevap
Ramz

4
Yalnızca teknik olarak yanlış ve spam yanıtlarının düşürülmesi gerektiğini unutmayın - bu, her ikisine de uymaz. Tazminat için +1 ve Shiki için de +1 çünkü bu harika bir cevap.

352

Bulduğum en iyi eşdeğer , Android Destek Paketinin bir parçası olan LocalBroadcastManager .

LocalBroadcastManager belgelerinden:

Sürecinizdeki yerel nesnelere kaydolmak ve Amaç yayınlarını göndermek için yardımcı. Bunun, sendBroadcast (Amaç) ile küresel yayın göndermeye göre bir dizi avantajı vardır:

  • Yayınladığınız verilerin uygulamanızdan ayrılmayacağını biliyorsunuz, bu nedenle özel verileri sızdırmak konusunda endişelenmenize gerek yok.
  • Diğer uygulamaların bu yayınları uygulamanıza göndermesi mümkün değildir, bu nedenle yararlanabilecekleri güvenlik açıkları konusunda endişelenmenize gerek yoktur.
  • Sistem üzerinden global bir yayın göndermekten daha verimlidir.

Bunu kullanırken Intent, an'ın an'a eşdeğer olduğunu söyleyebilirsiniz NSNotification. İşte bir örnek:

ReceiverActivity.java

Adlı etkinlik için bildirimleri izleyen bir etkinlik "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

Bildirim gönderen / yayınlayan ikinci etkinlik.

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

Düğme her zaman, yukarıdaki kod ile R.id.button_sendtıklandığında, bir Niyet yayınlanan ve tarafından alındığı mMessageReceiveryer ReceiverActivity.

Hata ayıklama çıktısı şu şekilde görünmelidir:

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
Böylesine yararlı, ayrıntılı bir yanıt yazmak için zaman ayırdığınız için çok teşekkür ederim.
Chris Lacy

14
Muhtemelen onCreate metodunuzda registerReceiver'ı çağırmamalısınız çünkü bu Activity'inizi sızdıracak ve onDestroy metodunuz asla çağrılmayacaktır. onResume, registerReceiver'ı ve onPause'u unregisterReceiver'ı çağırmak için daha iyi bir seçenek gibi görünüyor.
Stephane JAIS

4
Mükemmel eşdeğeri NSNotificationCenter, kabul edilen cevap olmalı!
Leon Storey

Global bildirimleri kullanmanın sizi karmaşık bir tasarıma götürebileceğini belirtmek isterim. Kolay yoldan geçmeden önce bileşenleriniz arasındaki en iyi bağlantının ne olacağını düşünün. Bazen dinleyicileri veya iOS temsilci kalıbına benzer bir şeyi kullanmak daha iyidir, vb.
Pedro Andrade

Teşekkürler bu benim için çalıştı. @Shiki, lütfen bana bu soru hakkında fikrini verebilir misin stackoverflow.com/questions/25598696/…
Axel

16

İşte @Shiki yanıtına benzer bir şey, ancak iOS geliştiricileri ve Bildirim merkezi açısından.

Önce bir tür NotificationCenter hizmeti oluşturun:

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

Ardından, dizelerle kodlamadaki hatalardan korunmak için bazı enum türlerine de ihtiyacınız olacak - (NotificationType):

public enum NotificationType {

   LoginResponse;
   // Others

}

Örneğin etkinliklerde kullanım (gözlemcileri ekle / kaldır):

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

ve nihayet, bazı geri arama veya dinlenme hizmetlerinden veya her neyse, NotificationCenter'a nasıl bildirim gönderiyoruz:

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

işte bu, şerefe!


Çözümünüz için teşekkürler! Bunun Bundle paramsyerine kullanmanın HashMapfarklı türlerdeki parametreleri geçmek için daha uygun olduğunu buldum . IntentVe arasında güzel bir bağlantı var Bundle:intent.putExtras(params)
zubko

4

Bunu kullanabilirsiniz: http://developer.android.com/reference/android/content/BroadcastReceiver.html , benzer bir davranış sağlar.

Alıcıları Context.registerReceiver (BroadcastReceiver, IntentFilter) aracılığıyla programlı olarak kaydedebilirsiniz ve Context.sendBroadcast (Intent) aracılığıyla gönderilen amaçları yakalar.

Bununla birlikte, bir alıcının etkinliği (bağlamı) duraklatılmışsa bildirim almayacağını unutmayın.


Hızlı bir tasarım notu: BroadcastReceivers ve NSNotificationCenter, bir olay toplayıcı olarak çalışabilir. Temsilciler veya Gözlemcilere göre avantajı, gönderen ve alıcının ayrıştırılmış olmasıdır (aslında mesaj veya veri bağlantısına sahiptirler, ancak bu en zayıf bağlantı türlerinden biridir). Düzeltme ile düzenlenmiştir.
AngraX

4

Guava lib'sinin EventBus kullanımının, bileşenlerin birbirlerine açıkça kaydolmasını gerektirmeden bileşenler arasında yayınlama-abone olma tarzı iletişim için en basit yol olduğunu buldum.

örneklerini https://code.google.com/p/guava-libraries/wiki/EventBusExplained adresinde görün

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

build.gradle'a bir bağımlılık ekleyerek bu kitaplığı yalnızca Android Studio'da ekleyebilirsiniz:

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

Platforma daha az bağımlı olabilen 'model' yan kodu için daha uygundur.
karmakaze

2

Kotlin : İşte bir @ Shiki'nin Kotlin'deki versiyonu, bir parça içinde biraz yeniden düzenlenmiş.

  1. Gözlemciyi Fragment'e kaydedin.

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. Herhangi bir yere bildirim gönderin. Sadece içeriğe ihtiyacınız var.

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

Not :

  1. Bildirimleri iyi organize etmek için benim gibi bir Constant.kt ekleyebilirsiniz. Sabit.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. Bir parçadaki bağlam için, benim kullandığımı activity(bazen null) kullanabilir veya conextbeğenebilirsiniz.

0

Zayıf referanslar kullanabilirsin.

Bu şekilde, hafızayı kendiniz yönetebilir ve istediğiniz gibi gözlemci ekleyip kaldırabilirsiniz.

AddObserver bu parametreleri eklediğinizde - bu bağlamı, eklediğiniz etkinlikten boş arabirime dökün, bir bildirim adı ekleyin ve arabirimi çalıştırmak için yöntemi çağırın.

Arabirimi çalıştırma yöntemi, bunun gibi bir şey ilettiğiniz verileri döndürmek için çalıştır adı verilen bir işleve sahip olacaktır.

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

Boş bir arayüzle bir başvuruyu çağıran bir gözlem sınıfı oluşturun. Ayrıca, eklenti sunucusunda iletilen bağlamdan Themethodtorun arayüzünüzü oluşturun.

Gözlemi bir veri yapısına ekleyin.

Çağırmak için aynı yöntem olacaktır, ancak yapmanız gereken tek şey veri yapısında belirli bildirim adını bulmaktır, Themethodtorun.run (bildirim_adı, veri) kullanın.

Bu, belirli bir bildirim adına sahip bir gözlemci oluşturduğunuz yere bir geri arama gönderecektir. İşiniz bittiğinde onları çıkarmayı unutmayın!

Bu, zayıf referanslar için iyi bir referanstır.

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

Bu kodu github'a yükleme sürecindeyim. Gözlerinizi açık tutun!


0

LiveData kullanan iOS'a eşdeğer, aynı işi yapabilen bir sarmalayıcı yazdım

Sarıcı:

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

}

Gözlemci türlerini bildirme:

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

Gözlem yapılacak aktivitede:

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

Bildirmek için:

ObserverCenter.playNextMusic.postNotification()
ObserverCenter.newFCMTokenDidHandle.postNotification("MyData")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.