Более эффективный способ обновления пользовательского интерфейса из сервиса, чем намерения? - PullRequest
41 голосов
/ 12 апреля 2010

В настоящее время у меня есть служба в Android, которая является образцом VOIP-клиента, поэтому она прослушивает SIP-сообщения и, если она получает его, запускает экран «Активность» с компонентами пользовательского интерфейса.

Затем следующие сообщения 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, вынужденный запускать для разных состояний, кажется, что это может стать неэффективным, чем больше я должен включать, так и добавлять сложность.

Так что мне было интересно, есть ли другой, более эффективный и чистый способ сделать это?

Есть ли способ сохранить трансляцию Intents только внутри приложения?

Будут ли обратные вызовы лучше? Если да, то почему и каким образом они должны быть реализованы?

Ответы [ 2 ]

52 голосов
/ 12 апреля 2010

ОБНОВЛЕНИЕ 2015:

Этот вопрос / ответ все еще немного активен, но ему уже более 5 лет, и многое изменилось. 5 лет назад ответом было то, как бы я справился с этим. Позже я написал очень легкое решение для внедрения зависимостей, которое я использовал некоторое время (о котором я упоминал в комментариях). В настоящее время я бы ответил на этот вопрос, используя Dagger и RxAndroid. Кинжал для внедрения класса-посредника как в Сервис, так и во все Действия, о которых необходимо уведомить, Служба отправит обновление статуса в класс-посредник, а класс-посредник предоставит наблюдаемую активность для действий, получающих обновление статуса ( вместо приемника вещания ОП).

Оригинальный ответ

Я обычно делю приложение на подклассы и позволяю моему общению в приложении проходить через этот класс (или у меня есть посредник, принадлежащий Приложению, который выполняет всю работу ... независимо от того, является ли приложение точкой входа для службы, с которой нужно взаимодействовать). У меня есть связанная служба, которая также должна обновить пользовательский интерфейс (гораздо проще, чем у вас, но та же идея), и она в основном сообщает приложению свое новое состояние, и приложение может затем передать эту информацию тем или иным образом текущему активная деятельность. Вы также можете поддерживать указатель на текущую активную активность (если их больше одной) и принимать решения, просто ли обновлять текущую активность, передавать намерение запустить другую активность, игнорировать сообщение и т. Д. Я хотел бы также подкласс Activity и ваш новый базовый класс активности сообщает Приложению, что он в настоящее время является активным в onResume и что он приостанавливается в onPause (для случаев, когда ваша служба работает в фоновом режиме и все действия приостановлены).

EDIT:

В ответ на комментарий приведем еще несколько подробностей.

В настоящее время ваше приложение по большей части состоит из классов, основанных на действиях и сервисах. По сути, вы получаете функциональность из экземпляра класса android.app.Application. Это объявлено в вашем манифесте (по умолчанию) со следующей строкой:

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

Элемент приложения в вашем манифесте не использует атрибут 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">

Теперь я могу добавить методы в ApplicationEx для действий и служб, чтобы использовать их для общения. Всегда есть один экземпляр вашего глобального контекста приложения, так что это отправная точка, если что-то должно быть глобальным для вашего приложения.

Вторая часть этого заключается в том, что вместо того, чтобы выводить мои сервисы и действия из Service и Activity, я создаю подкласс каждого с помощью метода getAppContext, который преобразует возвращаемое значение getApplicationContext (которое уже существует в обоих этих классах, потому что они происходит из контекста) в мой класс ApplicationEx.

Так ........

При этом вы добавляете свойство CurrentActivity в свой класс ApplicationEx типа Activity (или ActivityBase, если подклассы его, как и я). В методе ActivityRase onResume вы передаете себя ApplicationEx для него, чтобы установить CurrentActivity для этого действия. Теперь вы можете предоставить методы ApplicationEx для передачи информации непосредственно текущей деятельности, вместо того чтобы полагаться на механизмы Intent.

Это так ясно, как я могу это сделать

2 голосов
/ 13 ноября 2013

С помощью LocalBroadcastManager :

вы можете отправлять широковещательные намерения только в собственное приложение, а не по всей системе.

Помощник для регистрации и отправки трансляций содержания для локальных объектов в рамках вашего процесса. Это имеет ряд преимуществ перед отправкой глобальных трансляций с помощью sendBroadcast (Intent):

Вы знаете, что передаваемые вами данные не будут покидать ваше приложение, поэтому вам не нужно беспокоиться о утечке личных данных.

Другие приложения не могут отправлять эти трансляции в ваше приложение, поэтому вам не нужно беспокоиться о наличии дыр в безопасности, которые они могут использовать.

Это более эффективно, чем отправка глобальной трансляции через систему.

Тем не менее, я бы по-прежнему рекомендовал использовать подход Service и привязывать локально, а также обращаться к Handler, когда необходимо обновить компоненты пользовательского интерфейса для повышения эффективности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...