32

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

allprog
  • 16,540
  • 9
  • 56
  • 97
Salman Khan
  • 2,822
  • 5
  • 32
  • 53
  • Have a look at http://stackoverflow.com/questions/13377300/how-to-show-dialog-from-a-static-method – Braj Feb 01 '13 at 09:54

11 Answers11

33

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();
                }
            });
        }
    }
}
allprog
  • 16,540
  • 9
  • 56
  • 97
  • 2
    i have tried it, but first time it gives me an error that "No empty constructor is present", then i made a empty constructor but now it is giving me a null pointer exception where i am using handle.post method..... what should i do now ? – Salman Khan Feb 02 '13 at 06:28
  • 1
    Updated code in the post. If you get a NullPointerExc, the stack trace will be shown in LogCat. Try to analyze it to understand which variable or expression was `null` during execution. Hint for your issues: Since you haven't set the `handler` field in the constructor, it was `null` when you tried to use it. – allprog Feb 02 '13 at 08:54
  • Have you tried this solution? I copied the code from a working app, so it is error free. Can you explain why you removed the acceptance? – allprog Feb 11 '13 at 08:30
  • i didnt remove that i think somebody else is also using my account and due to this i am losing my reputation also – Salman Khan Feb 11 '13 at 09:07
  • Ugh, you should change your password as soon as possible. Thanks – allprog Feb 11 '13 at 10:15
  • 12
    This is overkill, since `BroadcastReceivers` already run on the Main Thread, which is the same as the "GUI thread". So you are basically using a Handler to talk to the same thread. It's absolutely fine to update GUI directly from a local broadcast receiver in the activity. More info here: http://stackoverflow.com/q/5674518/813951 – Mister Smith Sep 02 '13 at 15:51
  • 3
    @MisterSmith From the docs: `unless you explicitly asked for it to be scheduled on a different thread`. I'd say, this is a very robust approach and allows for very easy testing. – allprog Sep 02 '13 at 18:13
  • This will NOT work if for some reason you need to register your receiver in the manifest file, which is statically registering it and requires empty constructor. One scenario that you want to do static registering is when you want your activity receive messages even if it's not fully instantiated yet or if it's not the visible activity. – listboss Mar 03 '16 at 08:44
  • @listboss well, this method may not be the solution for all cases, but it works very well in most situations. Thank you for highlighting the difference with static registration. – allprog Mar 05 '16 at 13:56
  • I'm just confused. If we follow the above way how will we get the references to the UI elements that we want to update? – Syed Danish Ali Apr 05 '16 at 13:29
  • @SyedDanishAli I think you should think in terms of smaller blocks that update only a part of the UI. These blocks update parts of the UI that are related to the state changes you want to reflect. This approach will make it clean what data you use and when. (This approach is a small subset of the Reactive Programming toolset. If used properly, it can significantly reduce the complexity of creating multithreaded application logics.) – allprog Apr 06 '16 at 08:53
  • @allprog understood sir. Please help me with this. I have made a broadcast receiver for Connectivity change. I now want to implement the feature that whenever the connectivity changes, the active Activity should show some animations like showing a temporary bar at the bottom saying No internet connectivity. I should update certain parts of the UI as you just mentioned. But how to make every activity implement that reciever? – Syed Danish Ali Apr 06 '16 at 09:06
  • @SyedDanishAli If the same type of elements have to be updated everywhere, then pass them in the constructor and reuse the receiver class in every activity. If the elements differ from activity to activity, then you can override the UI updating mechanism the same way as it is shown in my example. – allprog Apr 06 '16 at 12:09
18

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

Rohit Singh
  • 16,950
  • 7
  • 90
  • 88
  • do you register the receiver with empty intent filter ? – Omar Boshra Dec 24 '19 at 04:37
  • @OmarBoshra Well, that goes in your Activity code. So you could just write there. But if you want to add another level of abstraction. You could just write an abstract method for it. See this https://stackoverflow.com/questions/34331284/best-example-of-abstract-class-in-android/55704253#55704253 – Rohit Singh Dec 24 '19 at 06:32
  • getting error as "Unable to start receiver MyBroadcastReceiver: java.lang.NullPointerException:" – Jaycee May 28 '20 at 09:49
  • Just qq, can I make MyBroadcastListener instance in companion object in broadcast reciever class and then assign value to it from fragment or activity i.e MyBroadcastReciever.MyBroadcastListener= this(fragment which implements the interface method) – Rahul Matte Jun 02 '22 at 18:55
  • This reminds me of the repository pattern, where `MyBroadcastListener` would be the repository that will access the data sources – Iglesias Leonardo Mar 14 '23 at 11:53
  • Sure. It's one way to look at it. But I feel it is more leaning towards Strategy Pattern. @IglesiasLeonardo – Rohit Singh Mar 16 '23 at 02:59
  • https://refactoring.guru/design-patterns/strategy – Rohit Singh Mar 16 '23 at 02:59
9

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.

Community
  • 1
  • 1
Nicks
  • 16,030
  • 8
  • 58
  • 65
7

Use runOnUiThread:

 MainActivity.this.runOnUiThread(new Runnable() {

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

        }
    });
Nermeen
  • 15,883
  • 5
  • 59
  • 72
1

I am using completely different approach for that.Pass the instance of handler to broadcast receiver by putting it in intent.Than in broadcast receiver used that handler to send a message to you activity class which updates the UI in your onHandleMessage method of your custom handler class.

create handler class like

   public class MyHandler extends Handler{

onHandlerMessage(Message msg){//do whatever you want here after broadcast get fired}
}

now use Myhandler handler=new MyHandler(); create this handler object on global level scope in activity.

Now put this handler into you intent by putExtra and then send this intent via sendBraodcast(intent).

in the broadcast receiver class get this handler object by getExtras and use it like below in onReceive() method

 handler.sendEmptyMessage();
codercat
  • 22,873
  • 9
  • 61
  • 85
kaushal trivedi
  • 3,405
  • 3
  • 29
  • 47
1

I used Intent to let Broadcast Receiver know about Handler instance of main Activity thread and used Message to pass a message to Main activity

I have used such mechanism to check if Broadcast Receiver is already registered or not. Sometimes it is needed when you register your Broadcast Receiver dynamically and do not want to make it twice or you present to the user if Broadcast Receiver is running.

Main activity:

public class Example extends Activity {

private BroadCastReceiver_example br_exemple;

final Messenger mMessenger = new Messenger(new IncomingHandler());

private boolean running = false;

static class IncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        running = false;    
        switch (msg.what) {
        case BroadCastReceiver_example.ALIVE:
    running = true;
            ....
            break;
        default:

            super.handleMessage(msg);
        }

    }
    }

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

    IntentFilter filter = new IntentFilter();
        filter.addAction("pl.example.CHECK_RECEIVER");

        br_exemple = new BroadCastReceiver_example();
        getApplicationContext().registerReceiver(br_exemple , filter); //register the Receiver
    }

// call it whenever you want to check if Broadcast Receiver is running.

private void check_broadcastRunning() {    
        /**
        * checkBroadcastHandler - the handler will start runnable which will check if Broadcast Receiver is running
        */
        Handler checkBroadcastHandler = null;

        /**
        * checkBroadcastRunnable - the runnable which will check if Broadcast Receiver is running
        */
        Runnable checkBroadcastRunnable = null;

        Intent checkBroadCastState = new Intent();
        checkBroadCastState .setAction("pl.example.CHECK_RECEIVER");
        checkBroadCastState .putExtra("mainView", mMessenger);
        this.sendBroadcast(checkBroadCastState );
        Log.d(TAG,"check if broadcast is running");

        checkBroadcastHandler = new Handler();
        checkBroadcastRunnable = new Runnable(){    

            public void run(){
                if (running == true) {
                    Log.d(TAG,"broadcast is running");
                }
                else {
                    Log.d(TAG,"broadcast is not running");
                }
            }
        };
        checkBroadcastHandler.postDelayed(checkBroadcastRunnable,100);
        return;
    }

.............
}

Broadcast Receiver:

public class BroadCastReceiver_example extends BroadcastReceiver {


public static final int ALIVE = 1;
@Override
public void onReceive(Context context, Intent intent) {
    // TODO Auto-generated method stub
    Bundle extras = intent.getExtras();
    String action = intent.getAction();
    if (action.equals("pl.example.CHECK_RECEIVER")) {
        Log.d(TAG, "Received broadcast live checker");
        Messenger mainAppMessanger = (Messenger) extras.get("mainView");
        try {
            mainAppMessanger.send(Message.obtain(null, ALIVE));
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    .........

}

}
jarek
  • 41
  • 1
1

In my case I want to update my the textview in my activity with the incoming sms , what I did was added the required information using putextra and then started the activity from broadcast receiver :

Intent intentone = new Intent(context.getApplicationContext(), enroll.class);
intentone.putExtra("pinbody",message);
intentone.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intentone);    

And then on the receiving end i.e in the enroll activity I extracted that information using the below and updated my textview :

Bundle extras = getIntent().getExtras();
String message = extras.getString("pinbody");

Not sure if it helps but definitely shall lead you to what you want .

Cheers !

beresfordt
  • 5,088
  • 10
  • 35
  • 43
1

Using getters and setters can easily solve this problem.

just declare a class as follows.

public class DetailsGetters {
    private View view;
    public View getview() {
        return view;
    }

    public void setview(View view) {
        this.view = view;
    }
}

On you main activity or fragment activity ,create a instance of the class DetailsGetters.

DetailsGetters newdetailsinstance=new DetailsGetters();

Before Returing view callnewdetailsinstance.setview(view);

On broadcast receiver get the view as newdetailsinstance.getview(view);

The follwing can be used to get view from the fragment and update Ui using setText and so on..

For updating UI on main activity pass the Textview and change DeailsGetters class and create getters and setters for Textview. Hope this helps for someone.

Guy Avraham
  • 3,482
  • 3
  • 38
  • 50
Elaiya
  • 9
  • 3
0

I used Handler & its post() method. Instead of runOnUiThread(). No need to cast Context to Activity. Its an alternative for runOnUiThread()

Handler handler = new Handler();
Runnable runnable = new Runnable() {
    private long startTime = System.currentTimeMillis();
    public void run() {
        while (gameState == GameState.Playing) {  
            try {
                Thread.sleep(1000);
            }    
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            handler.post(new Runnable(){
                public void run() {
                   tvTime.setText("" + ((System.currentTimeMillis() - this.startTime) / 1000));
            }
        });
        }
    }
};
new Thread(runnable).start();
Pavan Pyati
  • 950
  • 2
  • 13
  • 18
0

You should make Broadcast receiver as inner class, in this way it will have access to all fields to update the UI.

See this Plaid app by nick butcher Plaid- Nick Butcher(Github)

Uzair
  • 1,529
  • 14
  • 17
0

Get Current Date Time & Second & update in UI Through Broadcast Receiver

    package com.keshav.broadcasttest;

    import java.util.Date;

    import android.app.Service;
    import android.content.Intent;
    import android.os.Handler;
    import android.os.IBinder;
    import android.util.Log;

    public class BroadcastService  extends Service {
       private static final String TAG = "BroadcastService";
       public static final String BROADCAST_ACTION = "com.keshav.broadcasttest.displayevent";
       private final Handler handler = new Handler();
       Intent intent;
       int counter = 0;

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

           intent = new Intent(BROADCAST_ACTION); 
       }

        @Override
        public void onStart(Intent intent, int startId) {
            handler.removeCallbacks(sendUpdatesToUI_Runnable);
            handler.postDelayed(sendUpdatesToUI_Runnable, 1000); // 1 second

        }

        private Runnable sendUpdatesToUI_Runnable = new Runnable() {
           public void run() {
              DisplayLoggingInfo();         
               handler.postDelayed(this, 10000); // 10 seconds
           }
        };    

        private void DisplayLoggingInfo() {
           Log.d(TAG, "entered DisplayLoggingInfo");

           intent.putExtra("time", new Date().toLocaleString());
           intent.putExtra("counter", String.valueOf(++counter));
           sendBroadcast(intent);
        }

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

       @Override
       public void onDestroy() {     
            handler.removeCallbacks(sendUpdatesToUI_Runnable);
          super.onDestroy();
       }     
    }
    ============================================

    package com.keshav.broadcasttest;

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.util.Log;
import android.widget.TextView;

public class BroadcastTest extends Activity {
    private static final String TAG = "BroadcastTest";
    private Intent intent;

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

        intent = new Intent(this, BroadcastService.class);
    }

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            updateUI(intent);       
        }
    };    

    @Override
    public void onResume() {
        super.onResume();       
        startService(intent);
        registerReceiver(broadcastReceiver, new IntentFilter(BroadcastService.BROADCAST_ACTION));
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterReceiver(broadcastReceiver);
        stopService(intent);        
    }   

    private void updateUI(Intent intent) {
        String counter = intent.getStringExtra("counter"); 
        String time = intent.getStringExtra("time");
        Log.d(TAG, counter);
        Log.d(TAG, time);

        TextView txtDateTime = (TextView) findViewById(R.id.txtDateTime);   
        TextView txtCounter = (TextView) findViewById(R.id.txtCounter);
        txtDateTime.setText(time);
        txtCounter.setText(counter);
    }
}
    =============================

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <TableLayout android:id="@+id/tableGPS"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="7px"
            android:stretchColumns="1">
            <TableRow
                android:layout_margin="1dip">
                <TextView android:layout_gravity="right"
                    android:text="Time:"
                    android:layout_marginRight="7px"
                    android:layout_width="130px" />
                <TextView android:id="@+id/txtDateTime"
                    android:gravity="left"
                    android:layout_span="2" />
            </TableRow>
            <TableRow
                android:layout_margin="1px">
                <TextView android:layout_gravity="right"
                    android:text="Counter:"
                    android:layout_marginRight="7px"
                    android:layout_width="160px" />
                <TextView android:id="@+id/txtCounter"
                    android:gravity="left" />
            </TableRow>
        </TableLayout>
    </LinearLayout>

    ===========================================

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

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

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

            <service android:name=".BroadcastService" />
        </application>

    </manifest>
Keshav Gera
  • 10,807
  • 1
  • 75
  • 53