从服务更新 UI 比意图更有效的方法? [英] More efficient way of updating UI from Service than intents?

查看:19
本文介绍了从服务更新 UI 比意图更有效的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前在 Android 中有一个服务,它是一个示例 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 消息决定了要在屏幕上显示的 Activity.例如,如果是来电,它会显示接听或拒绝,或者拨出电话会显示拨号屏幕.

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.

此刻我使用 Intent 让 Activity 知道它应该显示什么状态.

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

<小时>

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


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

示例代码如下:

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

<小时>

这有效,但它似乎效率不高,Intent 将在整个系统范围内广播,并且 Intent 必须针对不同的状态触发似乎效率低下,我必须包含的内容越多,复杂性也增加.


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?

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

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 年前,下面的答案是我会如何处理它.后来我写了一个非常轻量级的依赖注入解决方案,我用了一段时间(我在评论中提到过).现在,我会使用 Dagger 和 RxAndroid 来回答这个问题.Dagger 将中介"类注入到服务和所有需要通知的活动中,服务会将状态更新推送到中介类,中介类将公开一个 observable 供活动使用状态更新(代替 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).

原答案

我通常子类化 Application 并让我的应用程序内通信通过这个类(或者让应用程序拥有的中介来完成工作......无论如何,应用程序是服务与之通信的入口点).我有一个绑定服务,它也需要更新 UI(比你的简单得多,但想法相同),它基本上告诉应用程序它的新状态,然后应用程序可以以一种或另一种方式将此信息传递给当前活跃的活动.您还可以维护一个指向当前活动活动的指针(如果有多个活动),并决定是否简单地更新当前活动、广播启动不同活动的意图、忽略消息等.我会还子类化 Activity 并让您的新活动基类告诉应用程序它当前是 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.

您的应用程序目前大部分由 Activity 派生类和 Service 派生类组成.本质上,您可以从 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 类的实例来表示您的全局应用程序上下文.

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.

在我的应用程序中,我创建了一个 Application 的子类(例如 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.

第二部分是,我不是从 Service 和 Activity 派生我的服务和活动,而是使用 getAppContext 方法创建每个的子类,该方法转换 getApplicationContext 的返回值(这两个类中已经存在,因为它们从 Context 派生到我的 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.

所以……

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

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.

我能说的这么清楚

这篇关于从服务更新 UI 比意图更有效的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆