Como Utilizar o LocalBroadcastManager Para Comunicação no Android

Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog. Você receberá um email de confirmação. Somente depois de confirma-lo é que poderei lhe enviar os conteúdos exclusivos.

Email inválido.
Blog /Android /Como Utilizar o LocalBroadcastManager Para Comunicação no Android

Como Utilizar o LocalBroadcastManager Para Comunicação no Android

Vinícius Thiengo07/02/2017
(1927) (248) (30)
Go-ahead
"Com tudo o que aconteceu com você, você pode sentir pena de si mesmo ou tratar o que aconteceu como um presente. Tudo é tanto uma oportunidade de crescer ou um obstáculo para parar de crescer. Você tem que escolher."
Wayne W. Dyer
Treinamento Oficial
Android: Prototipagem Profissional de Aplicativos
CursoAndroid: Prototipagem Profissional de Aplicativos
CategoriaAndroid
InstrutorVinícius Thiengo
NívelTodos os níveis
Vídeo aulas+ 144
PlataformaUdemy
Acessar Curso
Receitas Android
Capa do livro Receitas Para Desenvolvedores Android
TítuloReceitas Para Desenvolvedores Android
CategoriaDesenvolvimento Android
AutorVinícius Thiengo
Edição
Ano2017
Capítulos20
Páginas934
Acessar Livro
Código Limpo
Capa do livro Refatorando Para Programas Limpos
TítuloRefatorando Para Programas Limpos
CategoriaEngenharia de Software
AutorVinícius Thiengo
Edição
Ano2017
Capítulos46
Páginas598
Acessar Livro
Conteúdo Exclusivo
Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog.
Email inválido

Opa, blz?

Neste artigo vamos a utilização de uma API interna no Android para comunicação tanto entre componentes (Activity, Service, BroadcastReceiver, ...) quanto entre classes de domínio do problema.

A API é a LocalBroadcastManager, que é tão simples de utilizar, ou até mais, que a famosa library EventBus. Isso, pois as limitações, quando comparada ao BroadcastReceiver, removem o risco de vazamento de dados para outras aplicações no device.

Vamos aos tópicos abordados:

Comunicação interna e o LocalBroadcastManager

Caminhos para comunicação interna entre entidades do mesmo aplicativo Android não é algo novo e nem mesmo uma dor de cabeça hoje em dia, muito devido a libraries que nos permitem excluir a parte "trabalho pesado" na construção desses algoritmos.

Mesmo sabendo dos problemas no uso do EventBus, o custo / benefício na aplicação dessa library em nossos APPs Android tende a ser bem positivo.

O principal problema no uso do EventBus, em meu ponto de vista, é a referência, a mais, a uma library externa, diminuindo a capacidade de referência a métodos no aplicativo, as aproximadas 65k.

Enfatizei o EventBus aqui, pois é a mais simples e robusta maneira de comunicação interna que conheço, isso sem contar com o LocalBroadcastManager. Há outras maneiras também conhecidas, mas não tão simples e abrangíveis, são elas: ResultReceiver e IBinder.

Apesar de pouco divulgado, o LocalBroadcastManager é a solução que vejo sendo a melhor opção para comunicação interna no Android, mesmo quando comparado ao EventBus.

Lembrando que o LocalBroadcastManager não remove a importância do BroadcastReceiver, o primeiro é para comunicação somente interna e o segundo é para comunicação global no device.

A seguir algumas características importantes de LocalBroadcastManager:

  • É uma entidade nativa do Android SDK;
  • Oferece suporte a partir da API 9;
  • Tão simples de utilizar como qualquer outra library de comunicação interna;
  • Não há risco de mensagens saírem do contexto / processo da aplicação em execução, pois essa API não é global como o BroadcastReceiver em uso sem referência a instância de LocalBroadcastManager.

A seguir a implementação em um projeto Android real para facilitar o entendimento e consequentemente a aplicação do LocalBroadcastManager.

Projeto de exemplo, Android

O projeto é, dessa vez, somente para apresentar como utilizar o LocalBroadcastManager, mesmo assim não lhe atrapalhará no entendimento para utiliza-lo em aplicações Android em produção, principalmente devido a facilidade de uso da API.

Caso queira adiantar a construção do projeto em seu ambiente de desenvolvimento, você pode acessar o aplicativo de exemplo no GitHub a seguir: https://github.com/viniciusthiengo/comunicacao-local-manager.

Seguindo com o conteúdo, abra o Android Studio e inicie um novo projeto com uma "Empty Activity" e com o seguinte nome: Comunicação LocalManager.

Ao final do exemplo vamos ter a seguinte estrutura no Android Studio: 

E o seguinte APP quando em execução:

Abaixo vamos seguir com a configuração completa do aplicativo e logo depois com as alterações aplicando a comunicação via LocalBroadcastManager.

Configurações Gradle

Primeiro a configuração do Gradle Project Level ou build.gradle (Project: ComunicacaoLocalManager):

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
}
}

allprojects {
repositories {
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

 

Logo depois do Gradle APP Level ou build.gradle (Module: app):

apply plugin: 'com.android.application'

android {
compileSdkVersion 25
buildToolsVersion "24.0.3"
defaultConfig {
applicationId "br.com.thiengo.comunicacaolocalmanager"
minSdkVersion 10
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.1'
testCompile 'junit:junit:4.12'
}

 

Ambos os arquivos estão com a configuração padrão de um novo projeto no Android Studio.

Configurações AndroidManifest

A seguir a configuração do AndroidManifest.xml, configuração na qual já adiantamos a colocação da tag <service>, pois no projeto de exemplo vamos também trabalhar com um serviço:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="br.com.thiengo.comunicacaolocalmanager">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<service
android:name=".services.ServiceTest"
android:enabled="true"
android:exported="false" />
</application>
</manifest>

Configurações de estilo

As configurações dos arquivos XML de estilo são praticamente as mesmas de um novo projeto Android, tivemos apenas as adições de: uma imagem de background e diferentes cores de definição de template.

Todas as imagens você pode estar baixando diretamente no GitHub do projeto.

Abaixo o XML /res/values/colors.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimaryDark">#D32F2F</color>
<color name="colorPrimary">#F44336</color>
<color name="colorAccent">#FFEB3B</color>
</resources>

 

Agora o arquivo de dimensões, /res/values/dimens.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

 

O arquivo de String do projeto, /res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Comunicação LocalManager</string>
<string name="header_main_activity">Comunicação com LocalBroadcastManager</string>
<string name="action_button">Iniciar Ciclo Mensagem</string>
</resources>

 

E enfim o arquivo de definição de template, /res/values/styles.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="Theme.AppCompat">
<item name="android:windowBackground">@drawable/background</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

Configurações da atividade principal

Como todas as configurações até aqui, a configuração de layout XML e classe Java API da MainActivity é bem simples. Vamos iniciar pelo layout.

Segue XML /res/layout/activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="br.com.thiengo.comunicacaolocalmanager.MainActivity">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:gravity="center"
android:text="@string/header_main_activity"
android:textColor="#fff"
android:textSize="28sp" />

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="160dp"
android:onClick="cicloMensagem"
android:text="@string/action_button" />

<TextView
android:id="@+id/tv_conteudo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#fff" />
</LinearLayout>

 

Abaixo o diagrama do layout anterior:

Com o layout apresentado já temos, quando em execução, a tela como a seguir:

Assim a configuração padrão da MainActivity depois da criação de um projeto iniciando com uma "Empty Activity":

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void cicloMensagem(View view){
/* TODO */
}
}

 

Simples, certo? Ainda será adicionado mais código a essa Activity. Agora vamos as outras classes e componentes e então seguir para o uso do LocalBroadcastManager.

Definição da classe de domínio

Segue configuração inicial da classe ClasseDominio:

public class ClasseDominio {
public ClasseDominio( Context context ){}
}

 

O nome ClasseDominio é devido ao exemplo ter somente como objetivo a apresentação do LocalBroadcastManager. Ainda voltaremos a essa classe para adicionar mais linhas de código.

Definição do serviço de teste

Agora o Service do projeto, ServiceTest:

public class ServiceTest extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

 

A implementação do onBind() é obrigatória, ele mesmo não será útil a nós no projeto de exemplo. Como em classes anteriores, também voltaremos a esta classe para colocar o código de LocalBroadcastManager.

Definição do fragmento de teste

E por fim o Fragment, esse será apenas anexado a MainActivity, não vamos definir um layout para ele:

public class FragmentThread extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

 

Fragments também podem ser utilizados assim, sem uma interface para o usuário. Na própria documentação deles é apresentada essa maneira de utiliza-los.

Ciclo de comunicação

Nosso objetivo com o projeto de exemplo é permitir que as entidades apresentadas anteriormente: Activity, Service, Fragment e classe de domínio. Essas entidades se comuniquem em um ciclo como o apresentado a seguir:

  • MainActivity enviará uma mensagem para o ServiceTest por meio do LocalBroadcast dele, o LocalBroadcastServiceTest;
  • ServiceTest enviará uma mensagem para o FragmentThread por meio do LocalBroadcast dele, o LocalBroadcastFragmentThread;
  • FragmentThread enviará uma mensagem para a ClasseDominio por meio do LocalBroadcast dela, o LocalBroadcastClasseDominio. Aqui vamos utilizar uma Thread de background;
  • ClasseDominio enviará uma mensagem para a MainActivity por meio do LocalBroadcast dela, o LocalBroadcastMainActivity. Na MainActivity a mensagem final será apresentada em um TextView.

Assim podemos prosseguir com a implementação.

Da Activity para o Service

Da MainActivity para o ServiceTest. Vamos iniciar criando um novo pacote no projeto, o broadcasts. Logo depois vamos criar um BroadcastReceiver para responder aos envios de dados ao ServiceTest. Vamos chamar esse broadcast de LocalBroadcastServiceTest:

public class LocalBroadcastServiceTest extends BroadcastReceiver {
private ServiceTest service;

public LocalBroadcastServiceTest(ServiceTest service ){
this.service = service;
}

@Override
public void onReceive(Context context, Intent intent) {
String mensagem = intent.getStringExtra( ServiceTest.MENSAGEM_KEY );
service.logMensagem( mensagem );
}
}

 

Veja que a implementação é a mesma de um BroadcastReceiver convencional. Somente adicionamos um construtor para a injeção da instância do Service, isso para poder enviar a mensagem, assim que recebida, ao ServiceTest.

Essa estratégia de código será a mesma para todas as outras classes apresentadas na configuração do projeto, classes que vão estar no ciclo de envio da mensagem broadcast.

Note também que todos os novos LocalBroadcast* deverão estar no pacote broadcasts.

Agora vamos ao novo código de ServiceTest:

public class ServiceTest extends Service {
public static final String FILTRO_KEY = "ServiceTest_KEY";
public static final String MENSAGEM_KEY = "ServiceTest_MENSAGEM_KEY";

private LocalBroadcastServiceTest broadcast;

@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onCreate() {
super.onCreate();

broadcast = new LocalBroadcastServiceTest(this);
IntentFilter intentFilter = new IntentFilter( FILTRO_KEY );
LocalBroadcastManager
.getInstance(this)
.registerReceiver( broadcast, intentFilter );
}

@Override
public void onDestroy() {
super.onDestroy();
LocalBroadcastManager
.getInstance(this)
.unregisterReceiver( broadcast );
}

public void logMensagem(String mensagem ){
/* TODO */
}
}

 

Note que para trabalharmos com um BroadcastReceiver como sendo um LocalBroadcast, temos de vincula-lo a instância estática de LocalBroadcastManager. O this é para, internamente, a implementação de LocalBroadcastManager obter o contexto da aplicação, getApplicationContext().

Temos também de definir uma ação via filtro, ação a qual o LocalBroadcast estará respondendo. Aqui, por ser um exemplo, definimos somente uma, de nome ServiceTest_KEY.

Para definir ainda mais ações de resposta para um único broadcast, você faria como abaixo:

...
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction( FILTRO_1_KEY );
intentFilter.addAction( FILTRO_2_KEY );
...

 

O registerReceiver() é para ter o LocalBroadcast como listener ativo das ações que ele tem como parte da configuração dele.

O onDestroy() está com um código simples de entender, apenas removendo o broadcast registrado. Algo que a princípio somente tem efeito na memória disponível ao aplicativo.

Pois segundo alguns testes, depois que o contexto que tem referência a instância do LocalBroadcast é removido da memória, o LocalBroadcast não mais responde, mesmo tendo ainda uma referência a ele na instância estática de LocalBroadcastManager.

Ou seja, mesmo se não utilizássemos o unregisterReceiver(), não teríamos problemas de broadcasts duplicados recebendo mensagens.

O método logMensagem() receberá o código real posteriormente com a definição dos outros LocalBroadcasts.

Agora vamos ao novo código da MainActivity:

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

/* SERVICE */
Intent intent = new Intent(this, ServiceTest.class);
startService( intent );
}

@Override
protected void onDestroy() {
super.onDestroy();
Intent intent = new Intent(this, ServiceTest.class);
stopService( intent );
}

public void cicloMensagem(View view){
Intent intent = new Intent(ServiceTest.FILTRO_KEY);
intent.putExtra( ServiceTest.MENSAGEM_KEY, "ServiceTest: mensagem ok.<br>" );
LocalBroadcastManager
.getInstance(this)
.sendBroadcast(intent);
}
}

 

No onCreate() o código de inicialização do Service. No onDestroy() o código de remoção dele. E por fim, no método vinculado ao evento de clique do Button do MainActivity, o cicloMensagem(), o código que realmente envia a mensagem broadcast.

O código da Intent em cicloMensagem() é totalmente independente do código do Service, digo, o Intent é enviado via sendBroadcast() do LocalBroadcastManager e qualquer LocalBroadcast registrado para a ação definida no Intent irá obte-lo.

Note que o método sendBroadcast() é assíncrono e sempre invoca o onReceiver() do LocalBroadcast na Thread principal.

Para execução síncrona, na mesma Thread de envio de dados, utilize o sendBroadcastSync(). Note que nesse caso, por ser síncrono, o código abaixo de sendBroadcastSync() aguardará o término da execução neste método para poder iniciar a própria execução.

Com isso já temos o seguinte trecho do diagrama apresentado no tópico de início do uso do LocalBroadcastManager:

A mensagem enviada, na verdade será concatenada a outras mensagens nas outras entidades que estarão no ciclo de comunicação. Por fim ela será apresentada como código HTML, por isso o <br> no final da primeira mensagem, a de ServiceTest.

Do Service para o Fragment

Aqui vamos iniciar criando um novo LocalBroadcast, o que será responsável por responder a ação da intenção que será disparada pelo código de ServiceTest. Segue código de LocalBroadcastFragment:

public class LocalBroadcastFragment extends BroadcastReceiver {
private FragmentThread fragment;

public LocalBroadcastFragment(FragmentThread fragment ){
this.fragment = fragment;
}

@Override
public void onReceive(Context context, Intent intent) {
String mensagem = intent.getStringExtra( FragmentThread.MENSAGEM_KEY );
fragment.logMensagem( mensagem );
}
}

 

Igualmente ao primeiro LocalBroadcast, também temos de definir, além de alguns outros códigos, algumas constantes em FragmentThread.

Por que esse nome: FragmentThread?

Porque o envio broadcast de FragmentThread será dentro de uma Thread de background, então para lembrar esse tipo de algoritmo ainda no nome da classe.

Segue novo código de FragmentThread:

public class FragmentThread extends Fragment {
public static final String KEY = "FragmentThread";
public static final String FILTRO_KEY = "FragmentThread_KEY";
public static final String MENSAGEM_KEY = "FragmentThread_MENSAGEM_KEY";

private LocalBroadcastFragment broadcast;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

broadcast = new LocalBroadcastFragment(this);
IntentFilter intentFilter = new IntentFilter( FILTRO_KEY );
LocalBroadcastManager
.getInstance( getActivity() )
.registerReceiver( broadcast, intentFilter );
}

@Override
public void onDestroy() {
super.onDestroy();
LocalBroadcastManager
.getInstance( getActivity() )
.unregisterReceiver( broadcast );
}

public void logMensagem(String mensagem ){
new Thread(){
@Override
public void run() {
super.run();
/* TODO */
}
}.start();
}
}

 

Os códigos de inicialização e remoção de LocalBroadcastFragment têm os mesmos princípios dos códigos de LocalBroadcastServiceTest.

A constante FILTRO_KEY é para definição de ação ao LocalBroadcast de FragmentThread, nada diferente do que já está sendo feito em ServiceTest. Lembrando que todos esses valores de constantes você pode definir nos locais onde quiser em seu projeto e com os valores que quiser.

A constante FILTRO_MENSAGEM_KEY é para leitura do dado que será enviado no Intent recebido em LocalBroadcastFragment.

A constante KEY é para identificação desse Fragment no momento de vincula-lo a MainActivity.

O método logMensagem() será definido quando formos apresentar o código de LocalBroadcast da classe de domínio.

Abaixo o código atualizado de ServiceTest logMensagem():

public class ServiceTest extends Service {
...
public void logMensagem(String mensagem ){
mensagem += "FragmentThread: mensagem ok.<br>";

Intent intent = new Intent(FragmentThread.FILTRO_KEY);
intent.putExtra(FragmentThread.MENSAGEM_KEY, mensagem);
LocalBroadcastManager
.getInstance(this)
.sendBroadcast( intent );
}
}

 

Anteriormente foi adicionado o código da concatenação da mensagem e então o código de envio broadcast, onde teremos somente o LocalBroadcast do FragmentThread respondendo a ação definida.

Ainda temos de anexar este Fragment na MainActivity, segue código dela atualizado:

public class MainActivity extends AppCompatActivity {
...

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

/* SERVICE */
Intent intent = new Intent(this, ServiceTest.class);
startService( intent );

/* FRAGMENT */
if( savedInstanceState == null ){
FragmentThread fragment = new FragmentThread();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add( fragment, FragmentThread.KEY );
ft.commit();
}
}
...
}

 

Com isso conseguimos atingir mais um ponto do diagrama de comunicação apresentado anteriormente: 

Do Fragment para a classe de domínio

Assim o código de LocalBroadcastClasseDominio, LocalBroadcast responsável por obter os dados enviados via Intent com a ação definida em ClasseDominio:

public class LocalBroadcastClasseDominio extends BroadcastReceiver {
private ClasseDominio dominio;

public LocalBroadcastClasseDominio(ClasseDominio dominio ){
this.dominio = dominio;
}

@Override
public void onReceive(Context context, Intent intent) {
String mensagem = intent.getStringExtra( ClasseDominio.MENSAGEM_KEY );
dominio.logMensagem( mensagem );
}
}

 

Como em classes anteriores, vamos ao novo código de ClasseDominio:

public class ClasseDominio {
public static final String FILTRO_KEY = "ClasseDominio_KEY";
public static final String MENSAGEM_KEY = "ClasseDominio_MENSAGEM_KEY";

private Context context;
private LocalBroadcastClasseDominio broadcast;

public ClasseDominio( Context context ){
this.context = context;

broadcast = new LocalBroadcastClasseDominio(this);
IntentFilter intentFilter = new IntentFilter( FILTRO_KEY );
LocalBroadcastManager
.getInstance(context)
.registerReceiver( broadcast, intentFilter );
}

public void logMensagem( String mensagem ){
/* TODO */
}

public void destroy(){
LocalBroadcastManager
.getInstance(context)
.unregisterReceiver( broadcast );
}
}

 

ClasseDominio não é um componente Android, logo não conseguimos acessar o contexto da aplicação com a simples instância dele. Note também que diferente de FragmentThread, não temos acesso a um getActivity(), logo utilizamos um contexto injetado via construtor para utilizarmos o LocalBroadcastManager ainda em ClasseDominio.

Criamos também um método destroy() para permitir a remoção, da memória, do LocalBroadcast criado. Lembrando que ClasseDominio é o penúltimo ponto do ciclo de comunicação, ela ainda enviará uma mensagem broadcast a MainActivity.

Abaixo a atualização do código de FragmentThread logMensagem():

public class FragmentThread extends Fragment {
...

public void logMensagem( String mensagem ){
final String m = mensagem + "ClasseDominio: mensagem ok.<br>";

new Thread(){
@Override
public void run() {
super.run();

Intent intent = new Intent(ClasseDominio.FILTRO_KEY);
intent.putExtra( ClasseDominio.MENSAGEM_KEY, m );
LocalBroadcastManager
.getInstance( getActivity() )
.sendBroadcast( intent );
}
}.start();
}
}

 

Note que mesmo enviando a mensagem broadcast de dentro de uma Thread de background, a mensagem será recebida na Thread UI, isso devido ao uso do método sendBroadcast(). Caso quisesse manter o contexto, utilizaríamos o método sendBroadcastSync().

Abaixo a atualização da MainActivity, classe que conterá a instância de ClasseDominio:

public class MainActivity extends AppCompatActivity {
...
private ClasseDominio dominio;

@Override
protected void onCreate(Bundle savedInstanceState) {
...

/* DOMAIN */
dominio = new ClasseDominio(this);
}

@Override
protected void onDestroy() {
...

dominio.destroy();
}
...
}

 

A instância dessa classe poderia seguramente estar no FragmentThread ou ServiceTest. Abaixo o diagrama de mais um passo dado para o projeto final:

Da classe de domínio para a Activity

Para finalizar, o código de LocalBroadcastMainActivity que permitirá o envio de mensagem broadcast de ClasseDominioMainActivity:

public class LocalBroadcastMainActivity extends BroadcastReceiver {
private MainActivity activity;

public LocalBroadcastMainActivity(MainActivity activity ){
this.activity = activity;
}


@Override
public void onReceive(Context context, Intent intent) {
String mensagem = intent.getStringExtra( MainActivity.MENSAGEM_KEY );
activity.logMensagem( mensagem );
}
}

 

Em seguida o código atualizado de MainActivity:

public class MainActivity extends AppCompatActivity {
public static final String FILTRO_KEY = "MainActivity_KEY";
public static final String MENSAGEM_KEY = "MainActivity_MENSAGEM_KEY";

private LocalBroadcastMainActivity broadcast;
private ClasseDominio dominio;

@Override
protected void onCreate(Bundle savedInstanceState) {
...

/* INTENT FILTER */
broadcast = new LocalBroadcastMainActivity(this);
IntentFilter intentFilter = new IntentFilter( FILTRO_KEY );
LocalBroadcastManager
.getInstance(this)
.registerReceiver( broadcast, intentFilter );
}

@Override
protected void onDestroy() {
...

LocalBroadcastManager
.getInstance(this)
.unregisterReceiver( broadcast );
}

public void logMensagem(String mensagem ){
TextView tvConteudo = (TextView) findViewById(R.id.tv_conteudo);
tvConteudo.setText( Html.fromHtml(mensagem) );
}
...
}

 

Para envio da mensagem, código atualizado de ClasseDominio:

public class ClasseDominio {
...

public void logMensagem( String mensagem ){
mensagem += "MainActivity: mensagem ok.<br>";

Intent intent = new Intent(MainActivity.FILTRO_KEY);
intent.putExtra(MainActivity.MENSAGEM_KEY, mensagem);
LocalBroadcastManager
.getInstance(context)
.sendBroadcast( intent );
}
}

 

Assim finalizamos o ciclo proposto no início da seção de configuração de LocalBroadcastManager. Podemos seguir ao teste.

Teste e resultado

Executando a aplicação e clicando em "INICIAR CICLO MENSAGEM" temos como resultado:

Alterando o código de FragmentThread, mais precisamente o código de logMensagem():

...
public void logMensagem( String mensagem ){
final String m = mensagem + "ClasseDominio: mensagem ok.<br>";

new Thread(){
@Override
public void run() {
super.run();
Intent intent = new Intent(ClasseDominio.FILTRO_KEY);
intent.putExtra( ClasseDominio.MENSAGEM_KEY, m );
LocalBroadcastManager
.getInstance( getActivity() )
.sendBroadcastSync( intent );
}
}.start();
}
...

 

E logo depois, voltando a executar a aplicação e clicando no Button, temos:

Como explicado anteriormente, o sendBroadcastSync() mantém o contexto de Thread. Em nosso caso a Exception foi gerada, pois na MainActivity, local de apresentação da mensagem final, há a atualização de um elemento da interface do usuário, alteração que somente pode ocorrer na Thread principal do aplicativo.

Assim finalizamos a apresentação de LocalBroadcastManager, a provável melhor maneira de comunicação interna em aplicativos Android.

Não se esqueça de logo abaixo se inscrever na lista de emails do Blog para receber o conteúdo em primeira mão.

Vídeo com implementação passo a passo do projeto com LocalBroadcastManager

Abaixo o vídeo com a implementação passo a passo do projeto:

Para acesso ao conteúdo completo do projeto, entre no GitHub dele em: https://github.com/viniciusthiengo/comunicacao-local-manager.

Conclusão

Apesar do pouco de código a mais do que algumas APIs externas, o LocalBroadcastManager ainda é uma excelente opção para comunicação não global, muito por ser nativo do Android e ainda sim simples de utilizar.

É importante lembrar que o LocalBroadcastManager não substitui o BroadcastManager, ambos têm objetivos distintos. O primeiro é para mensagens broadcasts internas ao processo do aplicativo, o segundo é para responder a mensagens broadcasts globais, ou seja, inclui a comunicação com outros aplicativos e com o sistema Android.

Apesar de no exemplo do artigo termos utilizado somente String no transporte via Intent, você poderia seguramente utilizar um objeto de seu domínio do problema e até mesmo mais de um ou coleções de objetos. Desde que as classes envolvidas implementem o Parcelable.

Note que são possíveis vários caminhos de comunicação, por exemplo: Service para Service, Fragment para Fragment, ... No exemplo optei por apresentar somente alguns, pois os vi como sendo o suficiente para mostrar a API proposta.

Então é isso, qualquer dúvida ou sugestão deixe-a abaixo nos comentários. Não se esqueça de se inscrever no Blog assinando a lista de emails ao lado (ou abaixo) e também de se inscrever no Canal no YouTube, assim você recebe sempre o conteúdo em primeira mão.

Vlw.

Fontes

Documentação Android LocalBroadcastManager

Definição da classe LocalBroadcastManager

Stackoverflow: how to use LocalBroadcastManager?

Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog.
Email inválido

Relacionado

Estudando Android - Lista de Conteúdos do BlogEstudando Android - Lista de Conteúdos do BlogAndroid
API de Endereços Para Pré-Cadastro em APPs Android - Parte 1API de Endereços Para Pré-Cadastro em APPs Android - Parte 1Android
Como Colocar Notificações Bolha em Seu Aplicativo AndroidComo Colocar Notificações Bolha em Seu Aplicativo AndroidAndroid
MVP AndroidMVP AndroidAndroid

Compartilhar

Comentários Facebook

Comentários Blog

Para código / script, coloque entre [code] e [/code] para receber marcação especifica.
Forneça seu nome válido.
Forneça seu email válido.
Forneça o comentário.
Enviando, aguarde...