39

Here I am creating an online application that depends only on Internet.

So whenever there is a network error it must notify user. For that, I have created a BroadcastReciver that receives call when network connection gets lost(Internet).

All this works perfectly. Now what I need is that I have to call a method of Activity from this Broadcast Receiver, where I have created an Alert Dialogue.

I have read many answers on stack-overflow.com that I can declare that method static and call by using only Activity name,

e.g MyActivityName.myMethod()

But I can't declare my method static, because I am using Alert Dialogue there and it shows me error on line,

AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

that Cannot use this in a static context.

So, how can I call a method of Activity(must not static and without starting that activity) from a Broadcast Receiver ?

And can I get Activity(or fragment) name from Broadcast Receiver which is currently running?

PEHLAJ
  • 9,980
  • 9
  • 41
  • 53
Jay Vyas
  • 2,674
  • 5
  • 27
  • 58

6 Answers6

88

try this code :

your broadcastreceiver class for internet lost class :

public class InternetLostReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
    context.sendBroadcast(new Intent("INTERNET_LOST"));
}
}

in your activity add this for calling broadcast:

public class TestActivity  extends Activity{

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

    registerReceiver(broadcastReceiver, new IntentFilter("INTERNET_LOST"));
}

BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // internet lost alert dialog method call from here...
    }
};

@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(broadcastReceiver);
}
}
Bhanu Sharma
  • 5,135
  • 2
  • 24
  • 49
Vijju
  • 3,458
  • 1
  • 22
  • 20
  • Thanks for your code jay,it works perfectly. I have put a code for creating Alert Dialogue in onReceive(). Only one bug is there. Whenever internet gets disconnects , four Alert Dialogues created instantly instead of one. – Jay Vyas Mar 07 '14 at 05:50
  • 1
    please put if condition for opening AlertDialog if opened than don't open another. Please, if its work than accept my answer. – Vijju Mar 07 '14 at 07:11
  • AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); alertDialog.setMessage("Network not found."); alertDialog.setPositiveButton("Check Setting", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }); alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }); alertDialog.show(); – Jay Vyas Mar 07 '14 at 07:16
  • 1
    A side note: the internal `BroadcastReceiver` must not have the same intent filter as the external `BroadcastReceiver`. If they do, you'll get an endless loop. – velis Aug 04 '15 at 12:10
  • Thanks you saved my time :) – Shahzain ali Jul 28 '16 at 22:47
  • It works perfectly, but can you please explain to me the logic behind this, i am not getting it – Nick Asher Sep 21 '16 at 17:47
10

INTERFACE: Keep BroadCastReceiver and Activity code separate!

You can make a CallBackListener interface. The interface will work as a bridge between BroadcastReceiver and Activity.

1) Create a CallbackListener

interface ConnectionLostCallback{

      public void connectionLost();

} 

2) Provide ConnectionLostCallback in your BroadcastReceiver

public class MyBroadcastReceiver extends BroadcastReceiver{

     private ConnectionLostCallback listener;

     public MyBroadcastReceiver(ConnectionLostCallback listener ){

           this.listener = listener     //<-- Initialze it

     }

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

           listener.connectionLost();

     }
}

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

YourActvity extends AppcompatActivity implements ConnectionLostCallback{

    // Your Activity related code //
      //    new MyBroadcastReceiver(this);  <-- create instance

    private void showAlertMessage(){
       AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
    } 


    @Override 
    public void connectionLost(){

         showAlertMessage();          //<--- Call the method to shoe alert dialog

    }


}

Relevant link:

If you want to know how to make a BroadcastReceiver independent of any activity ie How can you reuse the same BroadCastReceiver with different Activities? Then READ THIS

Rohit Singh
  • 16,950
  • 7
  • 90
  • 88
  • 3
    what about if you cannot call the constructor? For example, when using AlarmManager, the broadcastreceiver is called from the alarmmanager methods – mantc_sdr Jun 02 '20 at 12:12
  • Same situation if you wnt to track wifi state changes - thats at the manifest level and you catch system level broadcasts. In that case, set up a listener – Martin Feb 06 '21 at 18:23
  • am also using alarmmanger, to fix this issue am using localbroadcastmanager instead of interface as callback @mantc_sdr – Ramesh kumar Apr 29 '22 at 12:17
  • here is my code in broadcast receiver:Intent refreshIntent = new Intent("CALLBACK"); refreshIntent.putExtra("STATUS", byReminderType); LocalBroadcastManager.getInstance(context.getApplicationContext()).sendBroadcast(refreshIntent) – Ramesh kumar Apr 29 '22 at 12:33
4

Add a boolean variable in you activity from where you are open alertdialog

boolean isDialogOpened = false;

// in broadcast recever check 
if(isDialogOpened) {
    alertDialog();
}

And replace your code for alertdialog with this one

public void alertDialog() {
    AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

    alertDialog.setMessage("Network not found.");
    alertDialog.setPositiveButton("Check Setting",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                }
            });
    alertDialog.setNegativeButton("Cancel",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                }
            });

    alertDialog.setOnDismissListener(new OnDismissListener() {
        @Override
        public void onDismiss(DialogInterface dialog) {
            isDialogOpened = false;
        }
    });

    alertDialog.setOnCancelListener(new OnCancelListener() {
        @Override
        public void onCancel(DialogInterface dialog) {
            isDialogOpened = false;
        }
    });

    alertDialog.show();
}
Vijju
  • 3,458
  • 1
  • 22
  • 20
  • sorry but your code not working, Telling me "NoSuchMethodException" on alertDialog.setonDismissListenet – Jay Vyas Mar 07 '14 at 07:35
1

Pass your Activity's context to BroadcastReceiver's contructor.

public class ResponseReceiver extends BroadcastReceiver{

    MainActivity ma; //a reference to activity's context

    public ResponseReceiver(MainActivity maContext){
        ma=maContext;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        ma.brCallback("your string"); //calling activity method
    }

}

and in your MainActivity

public class MainActivity extends AppCompatActivity {
    ...
    public void onStart(){
        ...        
    ResponseReceiver responseReceiver = new ResponseReceiver(this); //passing context
    LocalBroadcastManager.getInstance(this).registerReceiver(responseReceiver,null);
        ...
    }

    public void brCallback(String param){
        Log.d("BroadcastReceiver",param);
    }
}

hope it helps

junhaotee
  • 483
  • 4
  • 10
  • Have you tried this? I don't think that works, because `brCallback` is not a valid message for `AppCompatActivity`. And sure, you could cast your activity down, but then you end up coupled. – mayid Sep 10 '20 at 01:44
1

Use lambdas. A Consumer would do.

protected void onCreate(Bundle savedInstanceState) {
    ...

    receiver = new LocationBroadcastReceiver((whatever) -> doSomething(whatever));
    registerReceiver(receiver, new IntentFilter("YOUR_MESSAGE"));
}

Where doSomething will be a method in your Activity.

...

class YourBroadcastReceiver extends BroadcastReceiver {

    private Consumer<Whatever> callback;

    public LocationBroadcastReceiver(Consumer<Whatever> callback) {
        this.callback = callback;
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void onReceive(Context context, Intent intent) {
            
        this.callback.accept(new Whatever());
    }
}

Is the alternative to all the other:

declare that method static and call by using only Activity name.

Apart from what you explained, that's a way of coupling.

pass your Activity's context to BroadcastReceiver's contructor.

That wouldn't work because you want to call a method that's not part of AppCompatActivity. And yeah, you could downcast, but then you end up coupled to your activity.

using another Broadcast or a Local Broadcasts instead

Well, you can only pass a bunch of primitives that way. What if you want to pass an object? Also, declaring a new BroadcastReceiver get quite verbose and maybe hard to follow.

mayid
  • 1,644
  • 15
  • 23
0

Same as Vijju' s answer but using Local Broadcasts instead

public class SampleReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent intentToBroadcast =  new Intent("YOUR_ACTION_HERE");
        LocalBroadcastManager.getInstance(context).sendBroadcast(intentToBroadcast);
    }
}

In your activity add this

public class SampleActivity extends Activity {

    @Override
    protected void onResume() {
        super.onResume();
        LocalBroadcastManager.getInstance(this).registerReceiver(mSampleReceiver, new IntentFilter(YOUR_ACTION_HERE));
    }

    @Override
    protected void onPause() {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mSampleReceiver);
        super.onPause();
    }

    private SampleReceiver mSampleReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // your code here
        }
    };
}

Note Move the register/unregister calls to onCreate/onDestroy is you want to be notified even when your activity is in the background.

Adeel Ahmad
  • 939
  • 1
  • 10
  • 20
  • When I want to declare a variable like you did: "private SampleReceiver mSampleReceiver = new BroadcastReceiver()", give me error that Require type: SampleReceiver but provided is BroadcastReceiver – H Hooshyar Mar 07 '21 at 15:28
  • try replacing new BroadcastReceiver() with new SampleReceiver() – Adeel Ahmad Mar 08 '21 at 20:21