相当于NSNotificationCenter的Android


95

在将iPhone应用程序移植到android的过程中,我正在寻找在应用程序内进行通信的最佳方法。意图似乎是解决之道,这是最好的(唯一的)选择吗?在性能和编码方面,NSUserDefaults的权重似乎比Intent轻得多。

我还应该添加我有一个状态的应用程序子类,但我需要使另一个活动知道一个事件。


3
对于这个主题的新手,第二个答案是最好的答案。向下滚动...
2015年

Answers:


5

42
Shiki在下面的回答要好得多。
dsaff 2012年

5
@dsaff尽管是一个更完整的答案,但我的答案绝不是错误的,我显然不值得-1。对您来说+1 Shiki的答案是有意义的。
瑞·佩雷斯

4
Shiki是这个问题的更好答案
Ramz

4
请注意,只有技术上错误和垃圾邮件的答案才应被否决-这两个都不适合。+1补偿,Shiki +1,因为这是一个很好的答案。

350

我发现最好的等效项是LocalBroadcastManager,它是Android支持包的一部分

从LocalBroadcastManager文档中:

帮助程序注册Intent广播并将其发送到流程中的本地对象。与使用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单击按钮时,mMessageReceiverin 都会广播并接收一个Intent 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
您可能不应该在onCreate方法中调用registerReceiver,因为这会泄漏您的Activity,并且永远不会调用onDestroy方法。onResume似乎是调用registerReceiver的更好选择,而onPause则调用unregisterReceiver。
Stephane JAIS 2013年

4
完美相当于NSNotificationCenter,应该是公认的答案!
里昂·斯托里

我想指出的是,使用全局通知可能会使您的设计混乱。在跳到简单方法之前,请考虑一下组件之间的最佳耦合。有时,最好使用侦听器或类似于iOS委托模式的东西,依此类推。
saulobrito 2014年

谢谢,这对我有用。@Shiki请您认为您可以就这个问题给我您的意见stackoverflow.com/questions/25598696/…–
Axel

16

这类似于@Shiki的答案,但从iOS开发人员和Notification Center的角度来看。

首先创建某种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();
    }
}

最终,这是我们如何通过某些回调或rest服务或其他方式将通知发布到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和之间有一个很好的联系Bundleintent.putExtras(params)
zubko '18

4

您可以使用此方法:http : //developer.android.com/reference/android/content/BroadcastReceiver.html,它具有类似的行为。

您可以通过Context.registerReceiver(BroadcastReceiver,IntentFilter)以编程方式注册接收者,它将捕获通过Context.sendBroadcast(Intent)发送的意图。

但是请注意,如果接收方的活动(上下文)已暂停,则它将不会收到通知。


快速设计注意事项:BroadcastReceivers和NSNotificationCenter都可以作为事件聚合器运行。与委托人或观察者相比,优点是发送者和接收者是分离的(它们实际上具有消息或数据耦合,但这是最弱的耦合类型之一)。改正后编辑。
AngraX 2011年

4

我发现使用Guava lib的EventBus是组件之间发布-订阅式通信的最简单方法,而无需组件之间显式注册

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

您只需在build.gradle中添加依赖项,即可在Android Studio上添加此库:

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

更适合于“模型”侧码,后者对平台的依赖性较小。
karmakaze 2015年

2

Kotlin:这是Kotlin中的@Shiki版本,在片段中有一些重构。

  1. 在Fragment中注册观察者。

片段

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中添加这些参数时-将要添加的活动的上下文强制转换为空接口,添加一个通知名称,并调用该方法以运行接口。

运行接口的方法将具有一个称为运行的函数,以返回您正在传递的数据,例如:

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

创建一个观察类,该观察类使用空接口调用引用。还要从addobserver中传递的上下文中构造Themethodtorun接口。

将观察值添加到数据结构中。

调用它的方法是相同的,但是您所要做的就是在数据结构中找到特定的通知名称,请使用Themethodtorun.run(notification_name,data)。

这会将回调发送到您创建具有特定通知名称的观察者的位置。完成操作后,请不要忘记删除它们!

对于弱引用,这是很好的参考。

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

我正在将这段代码上传到github。睁大眼睛!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.