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

2023-09-12 00:14:36 作者:xx味少女

我现在有一个服务的Andr​​oid是一个样本VOIP客户端,以便侦听出来的SIP消息,如果它临危之一,它启动了UI组件的活动画面。

I currently have a Service in Android that is a sample VOIP client so it listens out for SIP messages and if it recieves one it starts up an Activity screen with UI components.

那么下面的SIP消息确定活动是怎样显示在屏幕上。 例如,如果它有来电就会显示接听或拒绝或呼出它会显示拨号画面。

Then the following SIP messages determine what the Activity is to display on the screen. For example if its an incoming call it will display Answer or Reject or an outgoing call it will show a dialling screen.

在分钟我使用意图让活动知道是什么状态,它应该显示。

At the minute I use Intents to let the Activity know what state it should display.

的一个例子是如下:

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

因此​​,活动将具有注册这些意图的广播reciever,并且将根据它接收的最后意图切换其状态。

So the activity will have a broadcast reciever registered for these intents and will switch its state according to the last intent it received.

样品code如下:

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

这工作但现在看来似乎不是非常有效,该意图会得到广播系统广泛并具有火的不同状态意图似乎是它可以成为低效,我越要包括和增加复杂性。

This works however it seems like it is not very efficient, the Intents will get broadcast system wide and Intents having to fire for different states seems like it could become inefficient the more I have to include as well as adding complexity.

所以,我在想,如果有一个不同的更高效,更清洁的方式做到这一点?

So I was wondering if there is a different more efficient and cleaner way to do this?

有没有办法让意图只有一个应用程序内的广播?

Is there a way to keep Intents broadcasting only inside an application?

回调会是一个更好的主意吗?如果是这样,为什么和在什么样的方式应该实施?

Would callbacks be a better idea? If so why and in what way should they be implemented?

推荐答案

更新2015年:

这个问题/答案仍然得到活动的一点点,但它是在5岁或事情已经改变了不少。 5年前,答案下面是如何,我会处理的。后来我写了,我用了一段时间(我在评论中提到)一个非常轻量级的依赖注入的解决方案。现在,我会回答用匕首和RxAndroid这个问题。匕首注入了调解员级到这两个服务,并且需要通知所有的活动,该服务将推动状态更新到中介类和中介类将暴露一个可观察的活动消耗的状态更新(代替OP的广播接收器)。

This question/answer still gets a little bit of activity, but it is over 5 yrs old and things have changed quite a bit. 5 years ago, the answer below was how I would have handled it. Later I wrote a very lightweight dependency injection solution that I was using for a while (which I mentioned in the comments). Nowadays, I would answer this question using Dagger and RxAndroid. Dagger to inject a "mediator" class into both the Service and all Activities that need to be notified, the Service would push the status update to the mediator class, and the mediator class would expose an observable for the activities to consume the status update (in place of the OP's broadcast receiver).

原来的答复

我一般都是子类应用,让我在应用程序的通信都要经过这个类(或者由应用程序拥有的调解员做的工作......不管,应用作为切入点与之通信的服务)。我有一个结合服务,需要更新UI以及(比你要简单得多,但是同样的想法),它主要是告诉该应用其新状态,然后该应用可以沿着以一种方式将此信息传递或另一个为当前活跃​​的活动。你还可以维护一个指针到当前活动的活性(如果有不止一个),并作出决定是否要简单地更新当前活动,广播的意图来启动不同的活性,忽略该消息,等等。我想也子类的活动,并有新的活动基地班说,这是目前国内活跃的一个在onResume中的应用,它被暂停在的onPause(因为你的服务是在后台运行的情况下和活动都暂停)。

I usually subclass Application and let my in-app communication go through this class (or have a mediator owned by the Application do the work...regardless, the Application being the entry point for the service to communicate with). I have a bound service that needs to update the UI as well (much simpler than yours, but the same idea) and it basically tells the app its new state and the app can then pass this information along in one way or another to the currently active activity. You can also maintain a pointer to the currently active activity (if there is more than one), and make decisions whether or not to simply update the current activity, broadcast the intent to launch a different activity, ignore the message, etc. I would also subclass Activity and have your new activity base class tell the Application that it is currently the active one in onResume and that it is being paused in onPause (for cases where your service is running in the background and the activities are all paused).

编辑:

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

In response to the comment, here's more specifics.

您的应用程序目前包括活动衍生和服务派生类大部分的。本质上,你从android.app.Application类的实例获得的功能。这是在你的清单中声明(默认)使用以下行:

Your application currently consists of Activity-derived and Service-derived classes for the most part. Inherently, you get functionality from an instance of the android.app.Application class. This is declared in your manifest (by default) with the following line:

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

在你的清单中的应用元素不使用android:name属性,所以它只是创建默认android.app.Application类的实例,以重新present全局应用程序上下文

The application element in your manifest doesn't use the android:name attribute, so it just creates an instance of the default android.app.Application class to represent your global application context.

在我的应用程序,创建应用程序的一个子类(ApplicationEx,例如),我告诉我的应用程序通过清单,这是该类实例作为我的全球应用程序上下文。例如:

In my apps, I create a subclass of Application (ApplicationEx, for example) and I tell my app through the manifest that this is the class to instantiate as MY global application context. For example:

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

我现在可以添加方法来ApplicationEx的活动和服务,用于通信。总是有你的全球应用程序上下文的一个实例,所以这是你的出发点,如果有什么需要是全球性的为您的应用程序。

I can now add methods to ApplicationEx for activities and services to use to communicate. There is always a single instance of your global application context, so this is your starting point if anything needs to be global for your app.

这是第二件,这是不是从服务和活动得出我的服务和活动,我创建的每个子类有getAppContext方法蒙上getApplicationContext的返回值(它已经存在于这两个类,因为它们从上下文推导),以我的ApplicationEx类。

A second piece of this is that instead of deriving my services and activities from Service and Activity, I create a subclass of each with a getAppContext method that casts the return value of getApplicationContext (which exists already in both of these classes because they derive from Context) to my ApplicationEx class.

所以........

So........

所有这一切是说,你增加一个CurrentActivity属性设置为您ApplicationEx类类型的活动(或ActivityBase如果你继承它,因为我做的)。在ActivityBase的onResume方法,你通过自己ApplicationEx为它设置CurrentActivity该活动。现在,您可以公开在ApplicationEx方法直接传递依靠意向机制的信息,以当前的活动吧。

All that being said, you add a CurrentActivity property to your ApplicationEx class of type Activity (or ActivityBase if you subclass it as I do). In ActivityBase's onResume method, you pass yourself to ApplicationEx for it to set CurrentActivity to that activity. Now, you can expose methods on ApplicationEx to pass information directly to the current activity instead of relying on the Intent mechanisms.

这是我们所清楚的,我可以把它

That's about as clear as I can make it