Статьи

Android с нуля: понимание трансляций Android

Большинство из нас слишком хорошо знакомы с объявлениями в общественных местах. Это громкие сообщения, предназначенные для информирования больших групп людей о чем-то важном. Приложения Android также иногда имеют дело с объявлениями — объявлениями, генерируемыми либо другими приложениями, либо самой операционной системой Android. Такие объявления называются широковещательными сообщениями, и они рассматриваются как важная форма асинхронного межпроцессного взаимодействия на платформе Android.

В этом уроке я покажу вам, как создавать, отправлять и получать как локальные, так и общесистемные трансляции. Я также покажу вам, как использовать стороннюю библиотеку под названием EventBus, которая может служить альтернативой локальным трансляциям.

Трансляции Android отправляются в виде объектов Intent . Поэтому перед созданием трансляции необходимо создать объект Intent . В этой серии есть подробное руководство о намерениях . Если вы еще не читали его, сейчас самое время сделать это.

Каждое намерение, которое вы отправляете в качестве трансляции, должно иметь название действия. Хотя именем действия может быть любая строка, я рекомендую всегда добавлять к имени пакета приложение, чтобы избежать конфликтов с трансляциями других приложений.

В следующем фрагменте кода показано, как создать новое намерение с именем действия com.tutsplus.my.first.broadcast :

1
Intent myIntent = new Intent(«com.tutsplus.my.first.broadcast»);

Чтобы отправить намерение в виде широковещания, все, что вам нужно сделать, — это вызвать метод sendBroadcast() и передать объект Intent в качестве аргумента.

1
sendBroadcast(myIntent);

Обратите внимание, что метод sendBroadcast() создает глобальную трансляцию, которую может принимать не только ваше приложение, но и любое другое приложение, установленное на устройстве пользователя.

Чтобы иметь возможность получать широковещательную рассылку, в вашем приложении должен быть правильно настроен широковещательный приемник. Вы можете создать широковещательный приемник, расширив абстрактный класс BroadcastReceiver и переопределив его onReceive() . Например, вот как вы создаете широковещательный приемник, который печатает сообщение каждый раз, когда получает широковещательную рассылку:

1
2
3
4
5
public class MyBroadcastReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        Log.d(«MYAPP», «I received a broadcast»);
    }
}

Приемник вещания работает, только если он зарегистрирован. Самый простой способ сделать это — объявить его в файле манифеста проекта, используя тег receiver .

Кроме того, внутри объявления получателя широковещания вы должны включить теги intent-filter которые определяют имена действий, в которых заинтересован получатель широковещания.

Следующий фрагмент кода регистрирует MyBroadcastReceiver и настраивает его для ответа на действие с именем com.tutsplus.my.first.broadcast :

1
2
3
4
5
<receiver android:name=».MyBroadcastReceiver»>
    <intent-filter>
        <action android:name=»com.tutsplus.my.first.broadcast» />
    </intent-filter>
</receiver>

Операционная система Android выпускает несколько глобальных трансляций. Большинство из них содержат ценную информацию об изменениях в состоянии устройства Android. Например, каждый раз, когда пользователь совершает новый исходящий вызов, отправляется трансляция android.intent.action.NEW_OUTGOING_CALL . Точно так же каждый раз, когда пользователь включает или выключает режим полета, выдается трансляция android.intent.action.AIRPLANE_MODE .

Прием системных трансляций аналогичен получению обычных трансляций. Однако большинство системных трансляций содержат дополнительную информацию в виде дополнений. Вы можете получить все эти дополнения как объект Bundle , вызвав метод getExtras() . Например, широковещательное намерение android.intent.action.AIRPLANE_MODE всегда содержит дополнительное вызываемое state , которое является логическим значением, указывающим, был ли включен режим полета.

В следующем фрагменте кода показано, как записать значение дополнительного state :

01
02
03
04
05
06
07
08
09
10
11
// Check if you have the right broadcast intent
if (intent.getAction().equals(«android.intent.action.AIRPLANE_MODE»)) {
    // Get all extras
    Bundle extras = intent.getExtras();
 
    // Fetch the boolean extra using getBoolean()
    boolean state = extras.getBoolean(«state»);
 
    // Log the value of the extra
    Log.d(«MYAPP», «AIRPLANE MODE: » + state);
}

Обратите внимание, что приведенный выше код будет работать только в том случае, если получатель широковещательной передачи был зарегистрирован со следующим фильтром намерений:

1
2
3
<intent-filter>
    <action android:name=»android.intent.action.AIRPLANE_MODE» />
</intent-filter>

По понятным причинам глобальные трансляции никогда не должны содержать конфиденциальную информацию. Однако вы можете передавать такую ​​информацию локально, используя класс LocalBroadcastManager , который является частью библиотеки поддержки Android.

Если вы используете последнюю версию Android Studio, вам не придется вручную добавлять зависимость для библиотеки поддержки Android. Однако, если вы хотите реализовать локальные трансляции в старом проекте, убедитесь, что в файле build.gradle модуля приложения присутствует следующая строка:

1
compile ‘com.android.support:support-v4:23.4.0’

Чтобы создать новый экземпляр класса LocalBroadcastManager , вы должны вызвать его метод getInstance() и передать свою деятельность или службу в качестве контекста. Например, вот как вы должны создать экземпляр внутри действия с именем MyActivity :

1
2
LocalBroadcastManager localBroadcastManager
   = LocalBroadcastManager.getInstance(MyActivity.this);

Теперь вы можете отправлять локальные трансляции, используя метод sendBroadcast() класса LocalBroadcastManager . Следующий фрагмент кода создает новое намерение с именем действия my-local-broadcast и отправляет его как локальное вещание:

1
2
3
4
5
// Create intent with action
Intent myIntent = new Intent(«my-local-broadcast»);
 
// Send local broadcast
localBroadcastManager.sendBroadcast(myIntent);

Прием местной трансляции немного более сложен. Местный широковещательный приемник не должен быть зарегистрирован в файле манифеста проекта. Вместо этого он должен быть зарегистрирован динамически с помощью метода registerReceiver() класса LocalBroadcastManager . В дополнение к экземпляру широковещательного приемника, для метода registerReceiver() требуется объект IntentFilter указывающий имя действия, на которое должен отвечать широковещательный получатель.

Вот как вы можете создать и зарегистрировать широковещательный приемник, который может реагировать на действие my-local-broadcast :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
// Create a broadcast receiver as usual
BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      Log.d(«MYAPP», «Received a local broadcast»);
    }
};
 
// Create intent filter for it
IntentFilter myFilter = new IntentFilter(«my-local-broadcast»);
 
// Register it
localBroadcastManager.registerReceiver(
            myBroadcastReceiver, myFilter);

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

1
2
localBroadcastManager.unregisterReceiver(
   myBroadcastReceiver);

Если вы считаете, что локальное вещание и намерения не дают вам необходимой гибкости, вам следует рассмотреть возможность использования EventBus, сторонней библиотеки, которая позволяет асинхронно отправлять данные из одного компонента вашего приложения в другой. На мой взгляд, API EventBus очень интуитивно понятен и лаконичен.

Чтобы включить EventBus в свой проект, добавьте следующую зависимость compile в файл build.gradle модуля приложения:

1
compile ‘org.greenrobot:eventbus:3.0.0’

Вместо просто объектов Intent , EventBus позволяет отправлять и получать объекты любого класса Java. Например, экземпляры следующего класса могут быть легко использованы с EventBus:

1
2
3
4
5
6
7
8
9
public class MyMessage {
    String title;
    String text;
 
    public MyMessage(String title, String text) {
        this.title = title;
        this.text = text;
    }
}

EventBus реализует шаблон издатель-подписчик и создает центральную шину, которая доступна для всех компонентов вашего приложения. Поэтому компонент, который должен отправлять объекты, должен просто публиковать объекты на шине. Компонент, который заинтересован в получении этих объектов, должен зарегистрироваться как подписчик шины.

Самый быстрый способ получить экземпляр центральной шины — использовать метод getDefault() класса EventBus .

1
EventBus bus = EventBus.getDefault();

Чтобы опубликовать объект, все, что вам нужно сделать, это вызвать метод post() шины. Например, вот как вы должны опубликовать экземпляр класса MyMessage :

1
bus.post(new MyMessage(«Hello», «My first EventBus message»));

Компонент приложения, такой как действие или служба, может зарегистрироваться как подписчик, вызвав метод register() шины. В следующем примере кода регистрируется служба MyService :

1
bus.register(MyService.this);

Компонент, который действует как подписчик, должен также иметь метод подписчика , который является любым методом с аннотацией @Subscribe . Методы подписчика ведут себя подобно обработчикам событий и автоматически вызываются каждый раз, когда на шине появляются новые объекты.

В следующем фрагменте кода показано, как создать очень простой метод подписчика, который автоматически вызывается каждый раз, когда на MyMessage становится доступен новый экземпляр класса MyMessage :

1
2
3
4
@Subscribe
public void messageAvailable(MyMessage message) {
    Log.d(«MYAPP», message.title + » [» + message.text + «]»);
}

Из этого руководства вы узнали, как отправлять информацию из одного компонента приложения в другой с помощью как глобальной, так и локальной трансляции. Вы также узнали, как использовать очень популярную библиотеку EventBus для общения внутри приложения. Поскольку трансляции имеют очень низкие накладные расходы, вы можете отправлять их так часто, как это необходимо. Фактически, система Android генерирует новую трансляцию android.intent.action.TIME_TICK каждую минуту.

Чтобы узнать больше о широковещательных приемниках и трансляциях Android, вы можете обратиться к документации классов BroadcastReceiver и Intent .