Thursday, 26 April 2012

LocalBroadcastManager: Intra application message passing

Using Broadcast receivers is good practice if you want to send and receive data between different applications. However its not good at all if you are using them for communication internal to application.


There are many reasons for NOT using broadcast:
  1.  A broadcast is sent system-wide, so this is not performance efficient.
  2.  When we use broadcast receiver and sender, any other application can send and receives broadcast messages to and from our application. This can be a serious security thread for our application.


Read more about security issues and configuration of broadcast at developer.android
Instead of using normal broadcast we should use Local broadcast for sending and receiving in-app messages between activities and services. Local broadcast is included in android 3.0 and is provided as support package v4 for early release development. For setting up support library read developer.android


ResultReceiver can be used to return data back to activity. Here is a simple IntentService: Providing data back to Activity #android .However by ResultRceiver, we can only return data to that specific activity which started the service.


To send data/intent application-wide, LocalBroadcasts should be used. LocalBroadcastManager is used to send and receive local broadcast. Lets start with a simple example. We have a simple IntentService say MyIntentService:

package sohail.aziz.mylocalbroadcast;

import android.app.IntentService;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

public class MyIntentService extends IntentService{

//ACTION should include application package convention, just to show that this can
//be any string
    public static final String ACTION="mycustomactionstring";
    
    public MyIntentService() {
        super("MyIntentService");
        // TODO Auto-generated constructor stub
       Log.d("sohail","service started");
    }

    @Override
    protected void onHandleIntent(Intent arg0) {
        // TODO Auto-generated method stub
        
        Log.d("sohail","onHandleIntent called");
        Intent in=new Intent(ACTION);  //you can put anything in it with putExtra
        Log.d("sohail","sending broadcast");
        LocalBroadcastManager.getInstance(this).sendBroadcast(in);
        
        
    }

}


A simple IntentService which sends local broadcast application-wide. Lets receive this in activity.
package sohail.aziz.mylocalbroadcast;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MylocalbroadcastExampleActivity extends Activity implements OnClickListener{
    /** Called when the activity is first created. */
       
    TextView tv;
    Button bt;
    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        
        LocalBroadcastManager.getInstance(this).unregisterReceiver(onNotice);
    }
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        
        IntentFilter iff= new IntentFilter(MyIntentService.ACTION);
        LocalBroadcastManager.getInstance(this).registerReceiver(onNotice, iff);
    }
    
    private BroadcastReceiver onNotice= new BroadcastReceiver() {
        
        @Override
        public void onReceive(Context context, Intent intent) {
            // intent can contain anydata
            Log.d("sohail","onReceive called");
            tv.setText("Broadcast received !");
        
        }
    };
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        tv=(TextView)findViewById(R.id.tvResults);
        
        bt= (Button)findViewById(R.id.btStart);
        bt.setOnClickListener(this);
        
    }
    @Override
    public void onClick(View v) {
        
        if(v.getId()==R.id.btStart){
            
            Log.d("sohail","starting service");
            Intent i= new Intent(this, MyIntentService.class);
            startService(i);
        }
    }
}

Here we are registering broadcast receiver onNotice as LocalBroadcast Receiver. Intent-filter is defined with ACTION defined in MyIntentService.


You just need to define service in AndroidManifest.xml:
 <service
 android:name=".MyIntentService" 
 ></service>

and thats it !
 
Browse and download source MyLocalbroadcastManager.

7 comments:

  1. hi,

    I didn't get the following: is LocalBroadcastManager available for Android 2.3.3?

    thanks in advance for your answer

    ReplyDelete
    Replies
    1. No, its not. But you can use it in 2.3.3 with support library as explained above.

      Delete
  2. Thank you for this. After looking at no less than about 30 examples and none of them working, including the pieces on the android developer site, or not having any flaws/bugs/deprecations, yours works and is a complete example... whoohooo!

    ReplyDelete
  3. Are the broadcasts sticky?
    If not the callback could be lost during an orientation change.

    ReplyDelete
  4. FYI, If you use a static receiver LocalBroadCastManager won't work.

    ReplyDelete
  5. Is it possible to send broadcasts from Service class (not an IntentService)? How do I get a hold of LocalBroadcastManager then? Thank you.

    ReplyDelete
  6. Hello
    Im trying to use your example in my application service, however

    LocalBroadcastManager.getInstance(this).sendBroadcast(msg);

    always produces the error

    getinstance(android.content.context) in LocalBroadcastManager cannot be applied to (NAME OF MY SERVICE)

    Do you have any suggestions as to why ?

    Thank you

    ReplyDelete