在将 iPhone 应用程序移植到 android 的过程中,我正在寻找在应用程序内进行通信的最佳方式。意图似乎是要走的路,这是最好的(唯一)选择吗?NSUserDefaults 在性能和编码方面似乎都比 Intents 轻得多。
我还应该添加我有一个状态的应用程序子类,但我需要让另一个活动知道一个事件。
在将 iPhone 应用程序移植到 android 的过程中,我正在寻找在应用程序内进行通信的最佳方式。意图似乎是要走的路,这是最好的(唯一)选择吗?NSUserDefaults 在性能和编码方面似乎都比 Intents 轻得多。
我还应该添加我有一个状态的应用程序子类,但我需要让另一个活动知道一个事件。
我找到的最好的等价物是LocalBroadcastManager,它是Android Support Package的一部分。
从 LocalBroadcastManager 文档中:
帮助注册并向您的流程中的本地对象发送 Intent 广播。与使用 sendBroadcast(Intent) 发送全局广播相比,这具有许多优点:
- 您知道您正在广播的数据不会离开您的应用程序,因此无需担心泄露私人数据。
- 其他应用程序不可能将这些广播发送到您的应用程序,因此您不必担心它们会利用安全漏洞。
- 它比通过系统发送全局广播更有效。
使用它时,您可以说Intent
an 等价于 an NSNotification
。这是一个例子:
一个监视名为 的事件的通知的活动"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();
}
发送/广播通知的第二个活动。
@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
,都会广播一个 Intent 并由mMessageReceiver
in接收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!
这是类似于@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);
}
就是这样,干杯!
你可以使用这个: http: //developer.android.com/reference/android/content/BroadcastReceiver.html,它给出了类似的行为。
您可以通过 Context.registerReceiver(BroadcastReceiver, IntentFilter) 以编程方式注册接收器,它将捕获通过 Context.sendBroadcast(Intent) 发送的意图。
但是请注意,如果接收者的活动(上下文)已暂停,则接收者将不会收到通知。
我发现使用 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'
Kotlin:这是 Kotlin 中的 @Shiki 版本,在片段中进行了一些重构。
片段.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))
}
}
在任何地方发布通知。只有你需要上下文。
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(Constant.NOTIFICATION_SOMETHING_HAPPEN))```
PS:
object Constant {
const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
activity
(有时null
)或conext
喜欢我使用的内容。您可以使用弱引用。
这样您就可以自己管理内存并根据需要添加和删除观察者。
当您 addObserver 添加这些参数时 - 将该上下文从您要添加的活动中转换到空接口,添加通知名称,然后调用该方法以运行接口。
运行接口的方法将有一个名为 run 的函数来返回您传递的数据,如下所示
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。睁大眼睛!
我写了一个可以做同样工作的包装器,相当于 iOS 使用 LiveData
包装:
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) }
}
}
声明观察者类型:
object ObserverCenter {
val moveMusicToBeTheNextOne: ObserverNotifyWithData<Music> by lazy { ObserverNotifyWithData() }
val playNextMusic: ObserverNotify by lazy { ObserverNotify() }
val newFCMTokenDidHandle: ObserverNotifyWithData<String?> by lazy { ObserverNotifyWithData() }
}
在活动中观察:
ObserverCenter.newFCMTokenDidHandle.observe(this) {
// Do stuff
}
通知:
ObserverCenter.playNextMusic.postNotification()
ObserverCenter.newFCMTokenDidHandle.postNotification("MyData")
@Shiki 的回答可能在 2020 年 6 月是正确的,但在 2022 年 1 月,LocalBroadcastManager 碰巧被弃用了。
经过两天的研究,我最终发现Android 指示SharedFlow “向应用程序的其余部分发送刻度,以便所有内容同时定期刷新”。
这意味着,或多或少,我们可以从 Swift 的 NSNotificationCenter 中得到什么。
这是我在我的应用程序中实现共享流的方式:
首先,您需要创建一个 InAppNotif Singleton,它实际上是您的活动的共享 ViewModel(请注意最后一点:为您的活动共享,而不是您的所有应用程序^^)
enum class InAppNotifName {
NotifNameNumber1,
NotifNameNumber2,
NotifNameNumber3
}
object InAppNotif: ViewModel() {
private val _sharedNotif = MutableSharedFlow<InAppNotifName>(0)
val sharedNotif: SharedFlow<InAppNotifName> = _sharedNotif.asSharedFlow()
private fun sendNotif(name: InAppNotifName) {
CoroutineScope(Default).launch {
_sharedNotif.emit(name)
}
}
public fun notifyNotif1() {
sendNotif(InAppNotifName.NotifNameNumber1)
}
public fun notifyNotif2() {
sendNotif(InAppNotifName.NotifNameNumber1)
}
public fun notifyNotif3() {
sendNotif(InAppNotifName.NotifNameNumber1)
}
}
第二步,如果你有很多Fragment在应用通知中接收,并且你不想重复自己,则需要创建一个“接收通知”界面
fun AnyReceivingNotif.observeInAppNotif() {
CoroutineScope(Default).launch {
InAppNotif.sharedNotif.collect {
onReceivingInAppNotif(it)
}
}
}
interface AnyReceivingNotif {
suspend fun onReceivingInAppNotif(value: InAppNotifName)
}
顺便说一句,“挂起”这个词只有在您需要在收到通知时更新 UI 时才有用。
最后,从任何要接收 InAppNotif 的对象,你需要做的就是让它符合你的 AnyReceivingNotif 接口,然后完成 onReceivingInAppNotif 函数
class MyFragment: Fragment(), AnyReceivingNotif {
override suspend fun onReceivingInAppNotif(value: InAppNotifName) {
when (value) {
InAppNotifName.NotifNameNumber1 -> { /* Do complicated things */ }
InAppNotifName.NotifNameNumber2 -> { /* Do some stuff */ }
InAppNotifName.NotifNameNumber3 -> {
withContext(Default){
/* Update the UI */
}
}
}
}
}