Android setara dengan NSNotificationCenter


95

Dalam proses mem-porting aplikasi iPhone ke android, saya mencari cara terbaik untuk berkomunikasi di dalam aplikasi. Tampaknya niat menjadi cara yang harus dilakukan, apakah ini opsi (satu-satunya) terbaik? NSUserDefaults tampaknya memiliki bobot yang jauh lebih ringan daripada Intent dalam performa dan pengkodean.

Saya juga harus menambahkan Saya memiliki subkelas aplikasi untuk negara bagian, tetapi saya perlu membuat aktivitas lain mengetahui suatu peristiwa.


3
Untuk pendatang baru di topik ini, jawaban kedua adalah yang terbaik. Gulir ke bawah ...
Stephan

Jawaban:


6

42
Jawaban Shiki di bawah ini jauh lebih baik.
dsaff

5
@dsaff meskipun jawaban yang lebih lengkap, sama sekali tidak salah jawaban saya, saya jelas tidak pantas -1. Yang masuk akal adalah Anda memberi +1 pada jawaban Shiki.
Rui Peres

4
Shiki adalah jawaban yang lebih baik untuk pertanyaan tersebut
Ramz

4
Perhatikan bahwa hanya jawaban yang salah secara teknis dan spam yang harus diremehkan - yang ini tidak cocok dengan keduanya. +1 untuk kompensasi dan +1 untuk Shiki juga karena itu jawaban yang bagus.

351

Persamaan terbaik yang saya temukan adalah LocalBroadcastManager yang merupakan bagian dari Paket Dukungan Android .

Dari dokumentasi LocalBroadcastManager:

Pembantu untuk mendaftar dan mengirim siaran Intent ke objek lokal dalam proses Anda. Ini memiliki sejumlah keunggulan dibandingkan mengirimkan siaran global dengan sendBroadcast (Intent):

  • Anda tahu bahwa data yang Anda siarkan tidak akan meninggalkan aplikasi Anda, jadi tidak perlu khawatir tentang kebocoran data pribadi.
  • Aplikasi lain tidak mungkin mengirim siaran ini ke aplikasi Anda, jadi Anda tidak perlu khawatir memiliki lubang keamanan yang dapat mereka manfaatkan.
  • Ini lebih efisien daripada mengirimkan siaran global melalui sistem.

Saat menggunakan ini, Anda dapat mengatakan bahwa Intentan sama dengan an NSNotification. Berikut ini contohnya:

ReceiverActivity.java

Suatu aktivitas yang mengawasi pemberitahuan untuk acara bernama "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

Aktivitas kedua yang mengirim / menyiarkan notifikasi.

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

Dengan kode di atas, setiap kali tombol R.id.button_senddiklik, Intent disiarkan dan diterima oleh mMessageReceiverdalam ReceiverActivity.

Output debug akan terlihat seperti ini:

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
Terima kasih banyak telah meluangkan waktu untuk menulis tanggapan yang sangat membantu dan mendetail.
Chris Lacy

14
Anda mungkin sebaiknya tidak memanggil registerReceiver dalam metode onCreate karena ini akan membocorkan Aktivitas Anda dan metode onDestroy Anda tidak akan pernah dipanggil. onResume tampaknya merupakan pilihan yang lebih baik untuk memanggil registerReceiver, dan onPause untuk memanggil unregisterReceiver.
Stephane JAIS

4
Persamaan yang sempurna dengan NSNotificationCenter, harus menjadi jawaban yang diterima!
Leon Storey

Saya ingin menunjukkan bahwa menggunakan pemberitahuan global dapat membawa Anda ke desain yang berantakan. Pikirkan tentang apa yang akan menjadi kopling terbaik antara komponen Anda sebelum beralih ke cara yang mudah. Terkadang lebih baik menggunakan pendengar atau sesuatu yang mirip dengan pola delegasi iOS, dan seterusnya.
saulobrito

Terima kasih ini berhasil untuk saya. @Shiki tolong, apakah Anda bisa memberi saya pendapat Anda tentang pertanyaan ini stackoverflow.com/questions/25598696/…
Axel

16

Ini adalah sesuatu yang mirip dengan jawaban @Shiki, tetapi dari sudut pandang pengembang iOS dan pusat Notifikasi.

Pertama buat beberapa jenis layanan 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);
 }
}

Kemudian, Anda juga memerlukan beberapa jenis enum untuk mengamankan kesalahan dalam pengkodean dengan string - (NotificationType):

public enum NotificationType {

   LoginResponse;
   // Others

}

Berikut adalah penggunaan (tambah / hapus pengamat) misalnya dalam aktivitas:

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

dan inilah akhirnya cara kami memposting pemberitahuan ke NotificationCenter dari beberapa panggilan balik atau layanan istirahat atau apa pun:

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

itu dia, cheers!


Terima kasih atas solusinya! Saya menemukan bahwa menggunakan Bundle paramsdaripada HashMaplebih nyaman untuk melewatkan parameter dari jenis yang berbeda. Ada hubungan yang bagus antara Intentdan Bundle:intent.putExtras(params)
zubko

4

Anda dapat menggunakan ini: http://developer.android.com/reference/android/content/BroadcastReceiver.html , yang memberikan perilaku serupa.

Anda dapat mendaftarkan penerima secara terprogram melalui Context.registerReceiver (BroadcastReceiver, IntentFilter) dan itu akan menangkap maksud yang dikirim melalui Context.sendBroadcast (Intent).

Namun, perhatikan bahwa penerima tidak akan mendapatkan notifikasi jika aktivitasnya (konteks) telah dijeda.


Catatan desain cepat: BroadcastReceivers dan NSNotificationCenter keduanya dapat beroperasi sebagai agregator acara. Keuntungan dari Delegasi atau Pengamat adalah bahwa pengirim dan penerima dipisahkan (mereka sebenarnya memiliki kopling pesan atau data tetapi itu adalah salah satu jenis kopling terlemah). Diedit dengan koreksi.
AngraX

4

Saya menemukan bahwa penggunaan EventBus of Guava lib adalah cara paling sederhana untuk komunikasi gaya-langganan-publikasi antar komponen tanpa memerlukan komponen untuk mendaftar secara eksplisit satu sama lain

lihat contoh mereka di 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") );
} 

Anda bisa menambahkan lib ini hanya di Android Studio dengan menambahkan dependensi ke build.gradle Anda:

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

Lebih cocok untuk kode samping 'model' yang tidak terlalu bergantung pada platform.
karmakaze

2

Kotlin : Berikut adalah versi @ Shiki di Kotlin dengan sedikit refactor dalam sebuah fragmen.

  1. Daftarkan pengamat di Fragmen.

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. Posting pemberitahuan di mana saja. Hanya Anda yang membutuhkan konteksnya.

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

PS :

  1. Anda dapat menambahkan Constant.kt seperti saya untuk mengatur notifikasi dengan baik. Constant.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. Untuk konteks dalam sebuah fragmen, Anda dapat menggunakan activity(terkadang null) atau conextseperti yang saya gunakan.

0

Anda bisa menggunakan referensi yang lemah.

Dengan cara ini Anda dapat mengelola memori sendiri dan menambah serta menghapus pengamat sesuka Anda.

Saat Anda addObserver menambahkan parameter ini - mentransmisikan konteks tersebut dari aktivitas yang Anda tambahkan ke antarmuka kosong, tambahkan nama notifikasi, dan panggil metode untuk menjalankan antarmuka.

Metode untuk menjalankan antarmuka akan memiliki fungsi yang disebut run untuk mengembalikan data yang Anda kirimkan seperti ini

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

Buat kelas observasi yang memanggil referensi dengan antarmuka kosong. Juga buat antarmuka Themethodtorun Anda dari konteks yang diteruskan di addobserver.

Tambahkan observasi ke struktur data.

Untuk memanggilnya akan menjadi metode yang sama namun yang perlu Anda lakukan hanyalah menemukan nama notifikasi spesifik dalam struktur data, gunakan Themethodtorun.run (notification_name, data).

Ini akan mengirim panggilan balik ke mana pun Anda membuat pengamat dengan nama notifikasi tertentu. Jangan lupa untuk menghapusnya setelah Anda selesai!

Ini adalah referensi yang bagus untuk referensi yang lemah.

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

Saya sedang dalam proses mengunggah kode ini ke github. Buka mata!

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.