Android equivalente a NSNotificationCenter


95

No processo de portar um aplicativo do iPhone para o Android, estou procurando a melhor maneira de me comunicar dentro do aplicativo. As intenções parecem ser o caminho a percorrer. Esta é a melhor (única) opção? NSUserDefaults parece muito mais leve do que os Intents em desempenho e codificação.

Devo também acrescentar que tenho uma subclasse de aplicativo para estado, mas preciso tornar outra atividade ciente de um evento.


3
Para os iniciantes neste tópico, a segunda resposta é a melhor. Role para baixo ...
Stephan

Respostas:


5

42
A resposta de Shiki abaixo é muito melhor.
dsaff

5
@dsaff apesar de ser uma resposta mais completa, de forma alguma a minha resposta está errada, claramente não mereço -1. O que faz sentido é você marcar a resposta de Shiki com +1.
Rui Peres

4
A resposta de Shiki é a melhor para a pergunta
Ramz

4
Observe que apenas respostas tecnicamente incorretas e de spam devem ser rejeitadas - esta não se encaixa em nenhuma das duas. +1 para compensação e +1 para Shiki também, porque essa é uma ótima resposta.

351

O melhor equivalente que encontrei é LocalBroadcastManager, que faz parte do Android Support Package .

Da documentação LocalBroadcastManager:

Ajudante para registrar e enviar transmissões de Intents para objetos locais dentro de seu processo. Isso tem uma série de vantagens sobre o envio de transmissões globais com sendBroadcast (Intent):

  • Você sabe que os dados que está transmitindo não sairão do seu aplicativo, então não precisa se preocupar com o vazamento de dados privados.
  • Não é possível que outros aplicativos enviem essas transmissões ao seu aplicativo, então você não precisa se preocupar em ter brechas de segurança que eles possam explorar.
  • É mais eficiente do que enviar uma transmissão global pelo sistema.

Ao usar isso, você pode dizer que um Intenté equivalente a um NSNotification. Aqui está um exemplo:

ReceiverActivity.java

Uma atividade que observa as notificações do evento nomeado "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

A segunda atividade que envia / transmite notificações.

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

Com o código acima, toda vez que o botão R.id.button_sendé clicado, um Intent é transmitido e recebido pormMessageReceiver em ReceiverActivity.

A saída de depuração deve ser semelhante a esta:

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
Muito obrigado por dedicar seu tempo para escrever uma resposta tão útil e detalhada.
Chris Lacy

14
Você provavelmente não deve chamar registerReceiver em seu método onCreate, pois isso vazará sua Activity e seu método onDestroy nunca será chamado. onResume parece uma escolha melhor para chamar registerReceiver e onPause para chamar unregisterReceiver.
Stephane JAIS

4
Equivalente perfeito a NSNotificationCenter, deve ser a resposta aceita!
Leon Storey

Gostaria de salientar que o uso de notificações globais pode levar você a um design confuso. Pense sobre qual seria o melhor acoplamento entre seus componentes antes de pular para o caminho mais fácil. Às vezes, é melhor usar ouvintes ou algo semelhante ao padrão de delegado do iOS e assim por diante.
saulobrito de

Obrigado, isso funcionou para mim. @Shiki, por favor, você acha que poderia me dar sua opinião sobre esta questão stackoverflow.com/questions/25598696/…
Axel

16

Aqui está algo semelhante à resposta @Shiki, mas do ponto de vista dos desenvolvedores iOS e do centro de notificação.

Primeiro crie algum tipo de serviço 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);
 }
}

Então, você também precisará de algum tipo de enum para evitar erros na codificação com strings - (NotificationType):

public enum NotificationType {

   LoginResponse;
   // Others

}

Aqui está o uso (adicionar / remover observadores), por exemplo, em atividades:

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

e aqui está, finalmente, como postamos uma notificação no NotificationCenter de algum serviço de retorno de chamada ou de descanso ou qualquer outro:

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

é isso, saúde!


Obrigado pela sua solução! Descobri que usar em Bundle paramsvez de HashMapé mais conveniente para passar parâmetros de diferentes tipos. Há uma boa conexão entre Intente Bundle:intent.putExtras(params)
zubko

4

Você pode usar isto: http://developer.android.com/reference/android/content/BroadcastReceiver.html , que apresenta um comportamento semelhante.

Você pode registrar os receptores programaticamente por meio de Context.registerReceiver (BroadcastReceiver, IntentFilter) e ele irá capturar intents enviados por meio de Context.sendBroadcast (Intent).

Observe, entretanto, que um receptor não receberá notificações se sua atividade (contexto) tiver sido pausada.


Uma nota rápida de design: BroadcastReceivers e NSNotificationCenter podem operar como um agregador de eventos. A vantagem sobre Delegados ou Observadores é que o emissor e o receptor são desacoplados (na verdade, eles têm acoplamento de mensagem ou dados, mas esse é um dos tipos de acoplamento mais fracos). Editado com correção.
AngraX

4

Descobri que o uso de EventBus de Guava lib é a maneira mais simples de comunicação no estilo de publicação-assinatura entre componentes sem exigir que os componentes se registrem explicitamente uns com os outros

veja seu exemplo em 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") );
} 

você pode adicionar esta lib simplesmente no Android Studio adicionando uma dependência ao seu build.gradle:

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

Mais adequado para o código lateral do 'modelo', que pode ser menos dependente da plataforma.
karmakaze

2

Kotlin : Aqui está uma versão de @Shiki em Kotlin com uma pequena refatoração em um fragmento.

  1. Registre o observador no 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. Publique a notificação em qualquer lugar. Só você precisa do contexto.

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

PS :

  1. você pode adicionar um Constant.kt como eu para organizar bem as notificações. Constant.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. Para o contexto em um fragmento, você pode usar activity(às vezes null) ou conextgostar do que usei.

0

Você pode usar referências fracas.

Desta forma, você pode gerenciar a memória sozinho e adicionar e remover observadores como quiser.

Quando você addObserver adiciona esses parâmetros - transmita esse contexto da atividade que você está adicionando na interface vazia, adicione um nome de notificação e chame o método para executar a interface.

O método para executar a interface teria uma função que é chamada run para retornar os dados que você está passando algo assim

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

Crie uma classe de observação que invoca uma referência com uma interface vazia. Também construa sua interface Themethodtorun a partir do contexto que está sendo passado no addobserver.

Adicione a observação a uma estrutura de dados.

Para chamá-lo seria o mesmo método, porém tudo que você precisa fazer é encontrar o nome da notificação específica na estrutura de dados, use o Themethodtorun.run (notification_name, data).

Isso enviará um retorno de chamada para onde você criou um observador com um nome de notificação específico. Não se esqueça de removê-los quando terminar!

Esta é uma boa referência para referências fracas.

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

Estou enviando este código para o github. Mantenha os olhos abertos!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.