更有效的方式从服务更新UI比意图?

我目前在Android中有一个Service,它是一个示例VOIP客户端,所以它侦听出SIP消息,如果它接收到一个,它将启动一个带UI组件的Activity屏幕。

然后,以下SIP消息确定活动在屏幕上显示的内容。 例如,如果来电将显示“应答”或“拒绝”或拨出电话,则将显示拨号屏幕。

在那一刻我使用Intents来让活动知道它应该显示什么状态。

一个例子如下:


Intent i = new Intent(); i.setAction(SIPEngine.SIP_TRYING_INTENT); i.putExtra("com.net.INCOMING", true); sendBroadcast(i); Intent x = new Intent(); x.setAction(CallManager.SIP_INCOMING_CALL_INTENT); sendBroadcast(x); Log.d("INTENT SENT", "INTENT SENT INCOMING CALL AFTER PROCESSINVITE"); 

因此,活动将有一个广播接收者注册这些意图,并将根据最后收到的意图切换其状态。

示例代码如下所示:


  SipCallListener = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(SIPEngine.SIP_RINGING_INTENT.equals(action)){ Log.d("cda ", "Got RINGING action SIPENGINE"); ringingSetup(); } if(CallManager.SIP_INCOMING_CALL_INTENT.equals(action)){ Log.d("cda ", "Got PHONE RINGING action"); incomingCallSetup(); } } }; IntentFilter filter = new IntentFilter(CallManager.SIP_INCOMING_CALL_INTENT); filter.addAction(CallManager.SIP_RINGING_CALL_INTENT); registerReceiver(SipCallListener, filter); 

但是,这样做似乎效率不高,intents将会广播系统,意图不得不为不同的状态而开火,似乎它会变得低效,我必须包括的越多,以及增加复杂性。

所以我想知道是否有一个更有效和更清洁的方法来做到这一点?

有没有办法让intents只在应用程序内部播放?

callback是一个更好的主意? 如果是这样,为什么以及以什么方式执行?

2015年更新:

这个问题/答案仍然有一些活动,但是它已经超过5年了,事情发生了很大的变化。 5年前,下面的答案是我将如何处理它。 后来我写了一个非常轻量级的dependency injection解决scheme,我曾经使用了一段时间(我在评论中提到过)。 如今,我会用Dagger和RxAndroid回答这个问题。 Dagger向“服务”和所有需要通知的“活动”注入一个“中介”类,服务会将状态更新推送到中介类,中介类将公开活动的观察值以消耗状态更新代替OP的广播接收机)。

原始答案

我通常子类化应用程序,让我的应用程序内通信通过这个类(或有一个应用程序拥有一个调解人做的工作…不pipe,应用程序是服务的通信入口点)。 我有一个绑定的服务,需要更新用户界面以及(比你更简单,但同样的想法),它基本上告诉应用程序的新状态,然后应用程序可以通过这种或那种方式传递信息到当前积极的活动。 你也可以维护一个指向当前活动的活动的指针(如果有多个活动的话),并且决定是否简单地更新当前活动,广播启动不同活动的意图,忽略消息等。也是子类Activity,让你的新活动基类告诉应用程序它在onResume中当前是活动的,并且在onPause中暂停(在你的服务在后台运行并且活动全部暂停的情况下)。

编辑:

回应评论,这里有更多的细节。

您的应用程序目前主要由活动派生类和服务派生类组成。 本质上,您可以从android.app.Application类的实例中获得function。 这是你的清单(默认情况下)用下面一行声明:

 <application android:icon="@drawable/icon" android:label="@string/app_name"> 

清单中的应用程序元素不使用android:name属性,所以它只是创build默认android.app.Application类的实例来表示您的全局应用程序上下文。

在我的应用程序中,我创build了Application的一个子类(例如ApplicationEx),并通过清单告诉我的应用程序,这是要实例化为MY全局应用程序上下文的类。 例如:

 <application android:name="com.mycompany.myapp.app.ApplicationEx" android:icon="@drawable/app_icon" android:label="@string/app_name"> 

我现在可以将方法添加到ApplicationEx中用于进行通信的活动和服务。 总是有一个全局应用程序上下文的实例,所以如果您的应用程序需要全局的话,这是您的出发点。

第二部分是,不是从Service和Activity派生我的服务和活动,而是使用getAppContext方法创build每个类的子类,该方法将getApplicationContext的返回值(它们已经存在于这两个类中,因为它们派生自Context )到我的ApplicationEx类。

所以……..

所有这一切,你添加一个CurrentActivity属性到你的ApplicationEx类的types的活动(或ActivityBase,如果你是我的子类)。 在ActivityBase的onResume方法中,将自己传递给ApplicationEx,以便将CurrentActivity设置为该活动。 现在,您可以在ApplicationEx上公开方法,将信息直接传递给当前活动,而不是依赖于Intent机制。

这是我可以做到的清楚

您可以使用LocalBroadcastManager将广播意图发送到您自己的应用程序而不是系统范围内 :

助手注册并发送Intents的广播到您的过程中的本地对象。 与使用sendBroadcast(Intent)发送全局广播相比,这具有许多优点:

您知道您正在播放的数据不会离开您的应用程序,因此不必担心泄露私人数据。

其他应用程序不可能将这些广播发送到您的应用程序,所以您不必担心有可能利用的安全漏洞。

这比通过系统发送全球广播更有效率。

不过,我仍然build议使用Service方法,并在必要时通过Handler进行本地绑定和通话,以更新UI组件以提高效率。