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

Don*_*rty 41 performance service user-interface android android-intent

我目前在Android中有一个服务,它是一个示例VOIP客户端,因此它会侦听SIP消息,如果它收到一个,它会启动一个带有UI组件的活动屏幕.

然后,以下SIP消息确定要在屏幕上显示活动的内容.例如,如果它的来电将显示接听或拒绝或拨出电话,它将显示拨号屏幕.

在那一刻,我使用Intents让Activity知道它应该显示的状态.

一个例子如下:


        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");
Run Code Online (Sandbox Code Playgroud)

因此,活动将为这些意图注册广播接收器,并根据收到的最后意图切换其状态.

示例代码如下:


       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);
Run Code Online (Sandbox Code Playgroud)

这可行,但它似乎不是非常有效,Intents将获得广播系统范围和Intents必须为不同状态触发似乎它可能变得低效,我必须包括更多,以及增加复杂性.

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

有没有办法让Intents只在应用程序内部广播?

回调是一个更好的主意吗?如果是这样,为什么以及以何种方式实施它们?

Ric*_*ich 52

2015年更新:

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

原始答案

我通常将Application子类化,让我的应用内通信通过这个类(或者让Application拥有的中介做工作......无论如何,Application是服务与之通信的入口点).我有一个绑定服务,也需要更新UI(比你的更简单,但相同的想法),它基本上告诉应用程序它的新状态,然后应用程序可以以这种或那种方式将这些信息传递给当前积极的活动.您还可以维护指向当前活动活动的指针(如果有多个),并决定是否只是更新当前活动,广播启动不同活动的意图,忽略该消息等.我会也是子类Activity,让你的新活动基类告诉应用程序它当前是onResume中的活动类,并且它在onPause中被暂停(对于你的服务在后台运行并且活动都被暂停的情况).

编辑:

在回应评论时,这里有更具体的内容.

您的应用程序目前大部分由Activity派生类和Service派生类组成.本质上,您从android.app.Application类的实例获得功能.这在您的清单(默认情况下)中使用以下行声明:

<application android:icon="@drawable/icon" android:label="@string/app_name">
Run Code Online (Sandbox Code Playgroud)

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

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

<application
    android:name="com.mycompany.myapp.app.ApplicationEx"
    android:icon="@drawable/app_icon"
    android:label="@string/app_name">
Run Code Online (Sandbox Code Playgroud)

我现在可以向ApplicationEx添加用于通信的活动和服务的方法.您的全局应用程序上下文始终存在一个实例,因此如果您的应用需要全局,则这是您的起点.

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

所以........

总而言之,您将CurrentActivity属性添加到Activity类型的ApplicationEx类中(如果您像我一样将其子类化,则添加ActivityBase).在ActivityBase的onResume方法中,您将自己传递给ApplicationEx,以便将CurrentActivity设置为该活动.现在,您可以在ApplicationEx上公开方法,将信息直接传递给当前活动,而不是依赖于Intent机制.

这就像我能做到的那样清晰

  • 我在上面提供了进一步的解释......如果你有更多的,或者不清楚,请告诉我. (4认同)