How to update UI in a BroadcastReceiver

I created an application in which I registered a broadcast receiver within my main class(Main Activity) and whenever I receive something in my BroadcastReceiver I want to update UI for e.g. I want to show an alert box or set some text view of my MainActivity. I receive all the values in my receiver but unable to set them, can somebody help me so that I can update my UI in the BroadcastReceiver.

My BroadcastReceiver class is inner class of MainActivity like this :-

public class MainActivity extends Activity {

   ..........

public static class NissanTabBroadcast extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            SharedPreferences shrd = context.getSharedPreferences("NissanGallery", context.MODE_WORLD_READABLE);
            type = shrd.getString("type", "null");
            badges = shrd.getString("badge_count", "null");

            //badge_tips_text.setText(badges);
            /*Editor edit =  shrd.edit();
            edit.remove("type");*/

            Toast.makeText(context, "" + type + "\n" + badge_tips_text.getText().toString(), Toast.LENGTH_LONG).show();
        }
    }
}

Any help will be appreciable

Thanks


I suggest you use a Handler.

  1. Initialize a Handler in the Activity, example: handler = new Handler()
  2. Provide the handler to the BroadcastReceiver in the constructor, in the same way as I did for NissanTabBroadcast above
  3. Use post() method of your Handler instance in the onReceive() method to submit the Runnable that updates the UI

This is the cleanest solution I can imagine.

public class MainActivity extends Activity {

    private MyReceiver receiver;

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

        receiver = new MyReceiver(new Handler()); // Create the receiver
        registerReceiver(receiver, new IntentFilter("some.action")); // Register receiver

        sendBroadcast(new Intent("some.action")); // Send an example Intent
    }

    public static class MyReceiver extends BroadcastReceiver {

        private final Handler handler; // Handler used to execute code on the UI thread

        public MyReceiver(Handler handler) {
            this.handler = handler;
        }

        @Override
        public void onReceive(final Context context, Intent intent) {
            // Post the UI updating code to our Handler
            handler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(context, "Toast from broadcast receiver", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
}

How to re-use BroadcastReceiver in any Activity?

enter image description here
Consider situations where,

  • You want to use the same BroadcastReceiver in different activities
  • You have written BroadcastReceiver in a separate file and want to access Activity methods, UI elements, etc.

In these cases using an interface is a great idea. I will explain the advantage and a use case in more detail. But first, let's see how it's done.

1) Create an interface

public interface MyBroadcastListener{

    public void doSomething(String value);

}

2) Initialize the Listener in your BroadcastReceiver

public class MyReceiver extends BroadcastReceiver {

    private MyBroadcastListener listener;

    public MyReceiver(MyBroadcastListener listener){
        this.listener = listener;
    }

    @Override
    public void onReceive(Context context, Intent intent) {

        listener.doSomething("Some Result");

    }
}

3) Implement the interface in your Activity and override the method

 public MainActivity extends AppCompatActivity implements MyBroadcastListener{

      private BroadcastReceiver receiver;

      @Override
      public void onCreate(Bundle sis){

           // Related code 

           receiver = new MyReceiver(this);   // This is how you initialise receiver

          // Register receiver etc
      }


      public void updateUI(String msg) {
           TextView textView = (TextView) findViewById(R.id.textView);
           textView .setText(msg);
      }

      @Override
      public void doSomething(String result){
           updateUI(result);        // Calling method from Interface
      }

 }

Advantage and Use case?

Using an interface approach makes BroadcastReceiver independent of any Activity. Let's say in future you want to use this BroadCastReceiver with another activity which takes the result from BroadcastReceiver and start a DetailActivity.

Notice that this is a completely different task but you can use same BroadcastReceiver without even any code change inside the BroadcastReceiver.

How to do that?
Simple! Implement the interface in the Activity and Override the method. That's it!

public ListActivity extends AppCompatActivity implements MyBroadcastListener{

      // Your Activity code goes here

     public void startDetailActivity(String title) {
         Intent i = new Intent(ListActivity,this, DetailActivity.class);
         i.putExtra("Title", title);
         startActivity(i);
     }

      @Override
      public void doSomething(String result){
           startDetailActivity(String title);    // Calling method from Interface
      }

}

enter image description here


My case was to update the textfield in one of the fragments hosted by MainActivity.The simplest solution I found was this:--

In my MainActivity class retrieved the running instance of MainActivtiy.This is my MAinActivity

private static MainActivity mainActivityRunningInstance;
    public static MainActivity  getInstace(){
        return mainActivityRunningInstance;
    }
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mainActivityRunningInstance =this;
----------
}

Now in Broadcast reciever's onRecieve method, get that instance and call the update method

 @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().matches(Intents.PROVIDER_CHANGED_ACTION)) {
                String textValue="the text field value";
// the null check is for, if the activity is not running or in paused state, in my case the update was required onlyif the main activity is active or paused
            if(MainActivity.getInstace()!=null)
                MainActivity.getInstace().updateUI(textValue);
}

Now for the part where we need to run the update in UIThread,The updateUI method of MainActivity will call the fragments update method.

  public void updateUI(final String str) {
        MainActivity.this.runOnUiThread(new Runnable() {
            public void run() {
     //use findFragmentById for fragments defined in XML ((SimpleFragment)getSupportFragmentManager().findFragmentByTag(fragmentTag)).updateUI(str);
            }
        });
    }

and the final step is updating the fragment's text field

public void updateUI(String str){
        tv_content.setText(str);
    }

and Bingo, its done. I referred this link to solve my issue. Hope it help others.


Use runOnUiThread:

 MainActivity.this.runOnUiThread(new Runnable() {

        @Override
        public void run() {
            // show alert

        }
    });