Android еквівалентно NSNotificationCenter


95

У процесі перенесення програми iPhone на андроїд я шукаю найкращий спосіб спілкування в програмі. Здається, наміри - це шлях, чи це найкращий (єдиний) варіант? NSUserDefaults здається набагато меншим, ніж Intents, як у продуктивності, так і в кодуванні.

Я також повинен додати, що у мене є підклас програми для стану, але мені потрібно повідомити іншу діяльність про подію.


3
Для новачків цієї теми друга відповідь є найкращою. Прокрутіть вниз ...
Стефан

Відповіді:


5

Ви можете спробувати це: http://developer.android.com/reference/java/util/Observer.html


42
Відповідь Шикі нижче набагато краща.
dsaff

5
@dsaff, незважаючи на більш повну відповідь, жодним чином моя відповідь не є неправильною, я явно не заслуговую -1. Що має сенс, це для вас поставити +1 відповіді Шикі.
Rui Peres

4
Шикі

4
Зверніть увагу, що лише технічно неправильні відповіді та відповіді на спам слід відхиляти - цей не входить до жодного. +1 за компенсацію та +1 для Шикі теж, бо це чудова відповідь.

351

Найкращий еквівалент, який я знайшов, це LocalBroadcastManager, який є частиною пакету підтримки Android .

З документації LocalBroadcastManager:

Помічник для реєстрації та передачі трансляцій Intents до локальних об’єктів у вашому процесі. Це має ряд переваг перед надсиланням глобальних трансляцій за допомогою sendBroadcast (Intent):

  • Ви знаєте, що дані, які ви транслюєте, не залишатимуть вашу програму, тому не потрібно турбуватися про витік приватних даних.
  • Інші програми не можуть надсилати ці трансляції до вашої програми, тому вам не потрібно турбуватися про наявність дірок у безпеці, які вони можуть використати.
  • Це ефективніше, ніж надсилання глобальної трансляції через систему.

Використовуючи це, ви можете сказати, що an Intentє еквівалентом 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клацнули, умисел транслюється і приймається mMessageReceiverв ReceiverActivity.

Вивід налагодження повинен виглядати так:

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
Щиро дякую, що знайшли час написати таку корисну, детальну відповідь.
Кріс Лейсі

14
Ймовірно, вам не слід викликати registerReceiver у вашому методі onCreate, оскільки це призведе до витоку вашої активності, а ваш метод onDestroy ніколи не буде викликаний. onResume виглядає кращим вибором для виклику registerReceiver, а onPause - для виклику unregisterReceiver.
Stephane JAIS

4
Ідеальним еквівалентом NSNotificationCenter, повинна бути прийнята відповідь!
Леон Сторі

Я хотів би зазначити, що використання глобальних сповіщень може призвести до невдалого дизайну. Подумайте, яке було б найкраще поєднання між вашими компонентами, перш ніж переходити до простого шляху. Іноді просто краще використовувати прослуховувачі або щось подібне до шаблону делегатів iOS тощо.
saulobrito

Дякую, це спрацювало для мене. @Shiki, будь ласка, ти думаєш, що ти міг би дати мені свою думку з цього питання stackoverflow.com/questions/25598696/…
Аксель

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

Потім вам також знадобиться певний тип перелічення, щоб захистити від помилок у кодуванні за допомогою рядків - (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), і він буде фіксувати наміри, надіслані через 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") );
} 

ви можете додати цю бібліотеку просто в Android Studio, додавши залежність до вашого build.gradle:

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

Більше підходить для бічного коду "моделі", який може бути менш залежним від платформи.
Кармакадзе

2

Котлін : Ось версія @ Shiki у Котліні з невеликим рефактором у фрагменті.

  1. Зареєструйте спостерігача у фрагменті.

Фрагмент.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, як я, щоб добре організувати сповіщення. Постійний.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. Для контексту у фрагменті ви можете використовувати activity(іноді null) або conextподобатись тому, що я використовував.

0

Ви можете використовувати слабкі посилання.

Таким чином ви могли б самостійно керувати пам’яттю та додавати та видаляти спостерігачів як завгодно.

Коли ви додаєтеObserver, додайте ці параметри - додайте цей контекст до активності, яку ви додаєте, до порожнього інтерфейсу, додайте ім'я сповіщення та викликайте метод для запуску інтерфейсу.

Метод для запуску інтерфейсу мав би функцію, яка називається run, щоб повернути дані, які ви передаєте щось подібне

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

Створіть клас спостереження, який викликає посилання з порожнім інтерфейсом. Також побудуйте свій інтерфейс Themethodtorun з контексту, що передається в addobserver.

Додайте спостереження до структури даних.

Для виклику це був би той самий метод, проте все, що вам потрібно зробити, це знайти конкретне ім'я сповіщення в структурі даних, скористатися Themethodtorun.run (message_name, дані).

Це призведе до зворотного дзвінка туди, де ви коли-небудь створювали спостерігача з певним іменем сповіщення. Не забудьте їх видалити, коли закінчите!

Це хороший довідник для слабких посилань.

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

Я зараз завантажую цей код на github. Тримайте очі відкритими!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.