Nota: Peço desculpas pelo atraso das postagens devido a assuntos pessoais, informo também que as postagens podem atrasar um pouco devido a algumas mudanças e melhorias relacionadas às postagens (e a manutenção que realizei no meu pc a algumas semanas), prezando pela qualidade ao invés da quantidade, então sem mais delongas e vamos aos estudos.
Hoje falaremos sobre a classe
BroadcastReceiver uma das classes mais importantes na arquitetura do Android,
utilizada para que aplicações possam reagir a determinados eventos gerados por
uma intent (mais informações sobre inten aqui:).
A classe BroadcastReceiver é sempre
executada em segundo plano durante um curto espaço de tempo sem utilizar a
interface gráfica, com o objetivo de receber uma mensagem e processá-la sem o
usuário perceber. Um passo importante na integração de aplicações uma vez que
elas podem trocar mensagens em segundo plano sem atrapalhar o usuário.
Introdução
A classe
android.content.BroadcastReceive é utilizada para responder a determinados
eventos iniciados por uma intente, por exemplo, executar uma determinada tarefa
em segundo plano ou determinada aplicação ao receber uma mensagem SMS, uma
ligação telefônica ou qualquer outra ação mapeada da aplicação.
Para configurar o BroadcastReceiver,
basta configurar a tag <receiver> no arquivo AndroidManifest.xml
combinando-a com a tag <intente-filter> para definir uma ação e
categoria.
Podemos ver a seguir a
configuração da classe Receiver1 que pode interceptar uma mensagem que tenha a
ação RECEIVER1:![]() | ||
Configurações Receiver1 |
Note que a configuração realizada no
arquivo AndroidManifest.xml é a mesma para uma <activity> e
<receiver>, onde ambas declaram o nome de uma classe e um
<intente-filter> com a ação e categoria para a qual a classe deve executar.
O código-fonte da classe
Receiver1 pode ser visto a seguir. Note que apenas o método
onReceiver(contexto, intent) precisa ser implementado. Assim quando a mensagem
com a ação RECEIVER1 for disparada a classe Receiver1 será executada em segundo
plano sem interferir na tela do usuário.
Configurando um BroadcastReceiver
Um BroadcastReceiver pode
ser configurado de duas formas:
- Confiurando o arquivo AndroidManifest.xml com a tag <receiver>. A tag <intente-filter> é usada pra configurar qual ação e categoria devem ser executados;
- Utilizando o método contente.registerReceiver(receiver, filtro) dentro do código para registrar dinamicamente o BroadcastReceiver. O primeiro parâmetro é uma instância de uma subclasse(classe-filha) de IntentReceiver, o segundo é uma instância da classe IntentFilter, que possui a configuração de ação e categoria;
Projeto de exemplo
Não há muito mais o que falar de um
BroadcastReveiver então partiremos para a prática. No final do post você
encontra o link para download do projeto completo AppBroadcastReceiver no
Eclipse.
O projeto possui um menu
com opções de forma a organizar todos os exemplos em um só aplicativo conforme
a figura abaixo:![]() | |
Menu da aplicação |
Arquivo AndroidManifest.xml completo:
<?xml version="1.0"
encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="br.app.blog.appbroadcastreceiver"
android:versionCode="1"
android:versionName="1.0"
>
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="23"
/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="br.app.blog.appbroadcastreveicer.receiv.Receiver1">
<intent-filter>
<action android:name="RECEIVER1"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
<receiver android:name="br.app.blog.appbroadcastreveicer.receiv.Receiver2">
<intent-filter>
<action android:name="RECEIVER2"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
<receiver android:name="br.app.blog.appbroadcastreveicer.receiv.Receiver3">
<intent-filter>
<action android:name="RECEIVER_ANR"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
<receiver android:name="br.app.blog.appbroadcastreveicer.receiv.Receiver4"></receiver>
<activity android:name=".ActivityTeste"></activity>
<receiver android:name="br.app.blog.appbroadcastreveicer.receiv.ReceiverBoot">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
</application>
Note que várias classes foram declaradas no
arquivo AndroidManifes.xml com a tag <receiver> exceto a classe Receiver2
que será registrada manualmente pela api no código da classe MainActivit.class
(Menu).
Nota:
Há outras configurações no AndroidManifest.xlm
como a permissão RECEIVE_BOOT_COMPLETED, que serão explicadas mais a frente.
A seguir podemos ver a classe
MainActivity que define uma lista com as opões de exemplo:
package
br.app.blog.appbroadcastreceiver;
import
android.annotation.SuppressLint;
import
android.content.Intent;
import
android.content.IntentFilter;
import android.os.Bundle;
import
android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import
android.widget.AdapterView;
import
android.widget.AdapterView.OnItemClickListener;
import
br.app.blog.appbroadcastreveicer.receiv.Receiver2;
import
br.app.blog.appbroadcastreveicer.receiv.Receiver4;
import
android.widget.ArrayAdapter;
import
android.widget.ListView;
@SuppressLint("NewApi")
public class MainActivity
extends AppCompatActivity implements OnItemClickListener {
private static final String CATEGORIA = "livro";
private ListView lista;
private String[] opcoes = { "Receiver 1",
"Receiver2 - API", "Receiver3 - ANR",
"Receiver4 - Iniciar
Activity", "Receiver5 - Integrar outra aplicação - APP_TESTE",
"Sair" };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inicializarComponentes();
registerReceiver(new Receiver2(), new
IntentFilter("RECEIVER2"));
}
@Override
public void onItemClick(AdapterView<?> parent, View
view, int position, long id) {
switch (position) {
case 0: sendBroadcast(new
Intent("RECEIVER1")); break;
case 1: sendBroadcast(new
Intent("RECEIVER2")); break;
case 2: sendBroadcast(new Intent("RECEIVER_ANR"));
break;
case 3: sendBroadcast(new Intent(this,
Receiver4.class)); break;
case 4: sendBroadcast(new
Intent("APP_TESTE")); break;
default: finish();
}
}
private void inicializarComponentes() {
lista = (ListView) findViewById(R.id.lista);
lista.setOnItemClickListener(this);
lista.setAdapter(new
ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, opcoes));
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(CATEGORIA, "onDestroy");
unregisterReceiver(new
Receiver2());
}
Note que além de definirmos um menu
com as opções do exemplo, o método registerReceiver(receiver, filter) é chamado
para registrar dinamicamente a classe Receiver2 com a ação RECEIVER2.
Como registramos um
Broadcast pela API Java, é necessário chamar o método
unregisterReceiver(receiver) informando uma instância da mesma classe utilizada
para registrar. Essse método pode ser chamado no onDestroy(). Nota:
Registrar o receiver dinamicamente deve ser usado quando é necessário
que ele seja utilizado somente quando determinada tela da aplicação estiver
executando, caso contrário, não. Se for registrado de forma estática no arquivo
AndroidManifest.xml ele pode executar a qualquer momento que receber uma
mensagem (intent), mesmo quando a aplicação não estiver executando.
Agora
mostraremos o código-fonte das classes Receiver1 e Receiver2 que apenas exibem
uma mensagem na tela:
Receiver2 :
Note que exibir um alerta na tela não é o que o
BroadcastReceiver deve fazer, pois ele deve ser utilizado justamente para o
contrário: executar em segundo plano e nunca atrapalhar o usuário, porem isso é
apenas um exemplo e o alerta permite que se veja claramente quando a classe
está sendo executada.
Ciclo de Vida
Um BroadcastReceiver é válido somente
durante a chamada do método onReceive(context, intent). Depois disso o sistema
operacional encerrará seu processo para liberar memória. O método
onReceive(context, intent) deve executar em um curto espaço de tempo e assim
que terminar o Android vai considerar que o BroadcastReceiver não está mais
ativo e o destruirá.
Este método deve consumir rapidamente
a mensagem (intent) recebida e retornar. Caso demore mais de 10 segundos para
executar, o Android exibirá um erro ANR (Application Not Responding), que nada
mais é do que um timeout:
![]() | ||
Erro ANR |
Nota:
Um Broadcast deve
executar em, no máximo, 10 segundos para evitar o erro ANR. Outra forma de
acontecer o erro ANR é quando a activity atual não consegue responder a um
evento de entrada (tecla pressionada) em 5 segundos.
O problema desses 10 segundos é que
são fáceis de serem extrapolados. Imagine que você precise executar um
sincronismo pela internet (download, por exemplo) em segundo plano. Dependendo
da conexão e da quantidade de dados a serem trafegados, isso pode demorar um
pouco.
Lembre-se também que para um processo
demorado com acesso a internet, ou não, é necessário uma Thread, porém não
devemos nos esquecer dos 10 segundos de limite e o mais importante, ‘Um
BroadcastReceiver só é válido somente durante a chamada do método onReceive(context,
intent), após sua chamada o sistema operacional encerrará seu processo para
liberar memória’.
A Thread é assíncrona e o Broadcast
quando for executado iniciará e encerrará sua execução independente da Thread
ter concluído a execução ou não, essa Thread não faz parte de nenhum ciclo de
vida que o android conheça e como resultado a sua Thread pode ser encerrada e o
sincronismo com a internet interrompido.
Para resolver o problema
foi criada a classe androd.app.Service (que será estudada em outro post) que
pode executar processos assíncronos em segundo plano por tempo indeterminado.
Nota:
Executar um BroadcastReceiver ao inicializar o SO
O Projeto AppBroadcastReceiver (link
no final do post) já tem configurada a classe ReceiverBoot, que é executada
assim que o sistema operacional terminar a inicialização (boot).
Isso é possível porque, quando o
sistema operacional termina a inicialização dispara um sendBroadcast(intent)
com a ação android.intent.action.BOOT_COMPLETED para informar a todas as
aplicações que o boot terminou.
A mensagem é enviada a todas as
aplicações instaladas. Nesse casso é possível interceptar a mensagem e iniciar
um BroadcastReceiver automaticamente logo após o sistema operacional terminar a
inicialização.
A seguir podemos analisar a
configuração da classe ReceiverBoot no arquivo AndroidManifest.xml:
É necessário declarar a permissão
RECEIVE_BOOT_COMPLETED para utilizar a ação BOOT_COMPLETED:
A seguir podemos ver a classe ReceiverBoot que
apenas imprime uma mensagem no logcat:
Para testar o exemplo feche o emulador, caso
esteja aberto, e execute-o novamente, a mensagem aparecerá no logcat após a
inicialização do sistema operacional:

Configurando uma aplicação para ser iniciada apenas por um BroadcastReceiver
Nos exemplos mostrados anteriormente
foi criada uma activity “top_level” para ser iniciada pelo usuário. Isso não é
obrigatório para o Android e é possível construir aplicações que não precisem
definir uma interface gráfica.
Para isso configuramos um
BroadcastReceiver que pode ser executado ao receber determinada mensagem
(intent). Este pode iniciar um serviço em segundo plano para realizar alguma
tarefa longa e avisar o usuário de algum acontecimento utilizando uma notificação.
Para mostrar como iniciar uma
aplicação a partir de um BroadcastReceiver e crie um projeto chamado
AppBroadcastAbrirAplicacao ou baixe o projeto completo no link ao final do post.
Ao criar um novo projeto ele deve ser
criado em um pacote diferente de algum já registrado caso contrário não será
possível instalar a aplicação.
Para iniciar a aplicação
será usado um BroadcastReceiver, configurado para executar com a ação APP_TESTE,
de acordo com o arquivo AndroidManifest.xml mostrado a seguir:
A seguir podemos ver a classe ReceiverAbrirApp,
que ao executar mostra uma simples mensagem no Logcat (ou Toast):
Agora que criamos a nova aplicação
precisamos instalar no emulador, para isso clique com o botão direito em cima
do projeto e selecione a opção RunAs>Android Application.
A aplicação criada não tem
nenhuma activity, de modo que, ao executar o projeto nenhuma tela aparece. Mas
podemos visualizar na janela console do Eclipse que a aplicação foi instalada
com sucesso.
Para testar a aplicação é só criar uma
intent com a ação APP_TESTE. O Android vai verificar qual BroadcastReceiver
está configurado para a ação informada, e nesse caso, executará a classe
ReceiverAbrirApp desse exemplo.
Abertura de uma tela apartir de um BroadcastReceiver
Um BroadcastReceiver é executado em
segundo plano se atrapalhar o usuário, esse é seu sentido. Mas caso seja
necessário abrir uma tela da aplicação a partir de um BroadcastReceiver, embora
não recomendado, pode ser realizado.
Para isso é possível utilizar o método
startActivity(intent) normalmente, porém é necessário configurar a flat
Intent.NEW_TASK_LAUNCH.
Um exemplo pode ser visto
na activity Receiver4 do projeto AppBroadcastReceiver:
Chamar uma activity a partir de um
BroadcastReceiver não é recomendado pelo Android, porque isso pode interromper
a tarefa que o usuário está fazendo.
Interação com o usuário por meio de notificações
Para finalizar devemos lembrar que um BroadcastReceiver não deve interagir com o usuário de forma direta, por exemplo, mostrando um alerta com a classe Toast ou abrindo uma tela sem perguntar ao usuário se ele deseja ou não. O usuário pode estar fazendo algo importante e provavelmente não desejaria ser interrompido. A maneira mais indicada de uma aplicação que está executado em segundo plano interagir com o usuário é através de uma notificação. Uma notificação é um tipo de alerta que fica na barra de status do Android e chama a atenção do usuário, mas esse assunto fica pra próxima!
AppBroadcastReceiver:http://www.4shared.com/rar/IAKQoqNSce/AppBroadcastReveiver.html
AppBroadcastAbrirAplicacao:http://www.4shared.com/rar/kCKkgqVQce/AppBroadcastAbrirAplicacao.html