为什么使用LocalBroadcastManager不能替代Context.registerReceiver?

11

我需要给这个应用程序实现一个功能,它由一个在后台工作的 Service 和一个 Activity 组成(它实现了 Service 而不是 IntentService)。

我查看了一些互联网上的教程,它们都使用 LocalBroadcastManager,顺便说一下,这也是 Android 推荐的方法:

如果您不需要跨应用发送广播,请考虑使用此类与 LocalBroadcastManager 代替下面描述的更通用的方式。

我花了一整天的时间找出问题所在:只有当我使用 Context.sendBroadcast()Context.registerReceiver() 而不是 LocalBroadcastManager 的方法时,它才能正常工作。

现在我的应用程序正在工作,但我感觉我违背了最佳实践,而且我不知道为什么。 有没有什么想法,为什么会发生这种情况?

编辑:

在我写完这个问题后,我进一步研究了这个问题。 LocalBroadcastManager 是通过一个单例模式工作的,我们应该调用 LocalBroadcastManager.getInstance(this).method()。我记录了两个实例(在 ActivityService 中),它们具有不同的内存地址。 现在我又有一个问题,一个 Service 不应该拥有调用它的 Activity 相同的 Context 吗?根据这篇文章,一个 Service 运行在主线程上,因此我认为 Context 应该是相同的。

对此有什么想法吗?(抱歉,帖子有点长)

代码示例:

MyService

public class MyService extends Service {

...

// When an event is triggered, sends a broadcast

Intent myIntent = new Intent(MainActivity.MY_INTENT);
myIntent.putExtra("myMsg","msg");
sendBroadcast(myIntent);

// Previously I was trying:
// LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(myIntent);

}

我的活动

public class MainActivity {

...

private BroadcastReceiver messageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) { 
            Log.d("onReceive", "received!");
            // TODO something
        }
    };

@Override
protected void onResume() {
    super.onResume();
    registerReceiver(messageReceiver, new IntentFilter(MY_INTENT));
    // Previously I was trying:
    // LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(messageReceiver, new IntentFilter(MY_INTENT));
}
}
3个回答

15

我从未使用过LocalBroadcastManager,但听起来你必须在其中注册你的接收器(即lbm.registerReceiver(...)而不是mycontext.registerReceiver(...))。你这样做了吗?

现在我有另一个问题,一个Service难道不应该拥有与调用它的Activity相同的Context吗?从这篇文章中可以看出,Service在主线程上运行,所以我认为上下文应该是相同的。

Context类与线程无关。实际上,Service和Activity都是(间接)的Context子类 - 因此它们都是自己的Contexts!
这就是为什么可以使用“this”作为Context的原因。

但无论你把哪个上下文发送到LocalBroadcastManager.getInstance()中,你应该得到完全相同的LBM实例。我想不出任何理由你不会得到-除非你在不同的进程中运行Activity和Service?


那是我的最终问题:似乎它们在不同的进程中。我从MainActivity用startService(serviceIntent)启动了我的服务。 - Teo Inke
1
你的活动或服务上没有设置任何 android:process 属性吗? - Snild Dolkow
是的,在清单文件中服务有 android:process=":remote",我没有注意到。这就是整个问题的原因。谢谢! - Teo Inke

12

声明:

private BroadcastReceiver receiver;

初始化:

receiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        //todo
    }
};

注册:

LocalBroadcastManager.getInstance(context).registerReceiver(receiver, new IntentFilter("RECEIVER_FILTER"));
context 可以是任何类型的 Context,你可以使用应用程序上下文。
注销:
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);

广播:

Intent intent = new Intent("RECEIVER_FILTER");
intent.putExtra("EXTRA", someExtra);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

我做了差不多相同的事情,但是两个LocalBroadcastManager.getInstance(context)获取到了不同的引用。 - Teo Inke
嗯,那我不知道原因了。我检查了LocalBroadcastManager的源代码,它是在主线程中工作的(使用了一个Handler,它是通过new Handler(context.getApplicationContext().getMainLooper())创建的)。 - smb
1
我甚至调用了lbm.getInstance(),在两个地方都将this更改为getMyApplicationContext(),但仍然得到不同的引用。很奇怪。 - Teo Inke
这是一个旧答案,但在API 28上完美地为我工作。我正在使用位置服务,并尝试从服务发送lat和lon回活动。但我没有正确注册本地广播。 - zeeshan
1
我在这部分犯了一个错误:LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - Ayman Al-Absi

7

请检查您的服务和活动是否在不同的进程中运行,如果是,则 LocalBroadcastManager 无法在不同的进程中使用。(您应该在 AndroidManifest.xml 文件中查看此信息)


不同的进程是什么意思?这里的进程是什么? - sofs1
关于“进程”的含义,我建议你通过谷歌搜索来了解,然后记住:默认情况下,一个应用程序只有一个进程,以其包名命名,但开发人员可以在清单文件中添加进程,例如通过在服务名称后添加标签 android:process="",让服务在不同的进程中运行,因此如果你能在清单文件中找到关键字“process=”,并且在“=”后面跟随一个不同于包名的名称,那么你就可以知道这是一个多进程应用程序。 - Michael
明白了。你能回答这个问题吗?http://stackoverflow.com/questions/39343099/why-intent-sent-by-localbroadcastmanager-from-intentservice-is-not-received-by-b 我已经卡了将近一个星期了。 - sofs1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接