How to pass data from BroadcastReceiver to Activity without in onCreate()
Asked Answered
G

4

5

I have a serious issue about passing data from BroadcastReceiver to an Activity. Let see my issue carefully. I have a class PhoneStateReceiver extends BroadcastReceiver that used to received an incoming phone.

public class PhoneStateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            System.out.println("Receiver start");
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
            if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)){

            }            
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
}

The incoming phone will be sent to an Activity, called ReceiverActivity. The ReceiverActivity receives the incoming phone and sends it to a server via a socket connection. The socket connection is initialized in the onCreate function. I googled and found server way to pass the data from BroadcastReceiver to an Activity. The common way is that send data via putExtra function and call startActivity. However, the way will call the onCreate again and then connect the socket, draw the UI again. Thus, it is not helpful in my case.

In my goal, If the phone receives an incoming call, it will send the incoming call to the ReceiverActivity. The ReceiverActivity receives the message and calls the send function. Which is the best way to do it? Thank you

The common way to pass data from a BroadcastReceiver to a ReceiverActivity that I used as follows

In PhoneStateReceiver class :

Intent intent_phonenum = new Intent(context, ReceiverActivity.class);
intent_phonenum.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent_phonenum.putExtra("phone_num", incomingNumber);
context.startActivity(intent_phonenum);

In ReceiverActivity class :

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        connect_socket();
        Intent intent = getIntent();
        phone_num = intent.getStringExtra("phone_num");
        send(phone_num);
}
Gayomart answered 15/8, 2016 at 11:31 Comment(0)
C
4

There is a very simple design pattern you can use here to ease communication between your classes and also decouple your code: publisher/subscriber. My favorite library for this is EventBus:

First, add to your build.gradle file:

compile 'org.greenrobot:eventbus:3.0.0'

Then, create a simple POJO - Plain Old Java Object like this:

public class OnReceiverEvent{
   private String phoneNumber;

   public OnReceiverEvent(String phone){
      this.phoneNumber = phone;
   }

   public String getPhoneNumber(){
      return phoneNumber;
   }
}

Next, by making your Receiver class a publisher, and your Activity a subscriber, you should be able to easily pass the information to your activity like this:

//inside your PhoneStateReceiver class when you want to pass info

EventBus.getDefault().post(new OnReceiverEvent(phoneNumber));

Next, inside your activity, simply do this:

//onStart
@Override
public void onStart(){
   super.onStart();
   EventBus.getDefault().register(this);
}

//onStop
@Override
public void onStop(){
   super.onStop();

   EventBus.getDefault().unregister(this);
}

Finally, handle the posted data i.e phoneNumber value:

@Subscribe
public void onPhoneNumberReceived(OnReceiverEvent event){
   //get the phone number value here and do something with it

   String phoneNumber = event.getPhoneNumber();

   //display or something?
}

UPDATE

If you have another Event that you want this activity to subscribe to, simply create a method like you did in the first one using the @Subscribe annotation.

@Subscribe
public void onSomeOtherEvent(EventClassName event){
   //get the variables here as usual;
}

This is the easiest way to pass the data from your receiver to your activity without having to worry about starting the activity over and over!

I hope this helps and good luck!

Conversation answered 15/8, 2016 at 11:50 Comment(14)
Your answer gives a brief tutorial on pub/sub using EventBus.Latarsha
I think it is a great answer. I tested and it worked well. I just ask one more thing about onStop function. Because my app can be run in the background or as a service. At that purpose, the PhoneStateReceiver still receives the incoming phone. Is it possible to receives the incoming phone in the Activity when the phone is in the background or running in service? I want to ask it because when I press Home button, the data does not send to the ActivityGayomart
Well, if you want to pass the information from your receiver to your service (for when your app is closed and running in the background), you can simply do the same thing in your background service that you do in the activity. The good thing about EventBus, you can use it with services, activities, fragments, etcConversation
Actually I am using event based solution in my projects, so good answer and briefly. But if you are using instant run sometimes it is not working, consider it when testing.Madder
To get around Instant Run, you just unplug your device and it will run your entire project (although might take a few minutes depending on your machine speed) and once you have the whole code built, you should be fine!Conversation
@Conversation thats good point. But when Instant Run is enabled the first build takes long time. Maybe when testing Eventbus he can disable Instant Run.Madder
@Eenvincible: If I want to pass both phone and SMS, I must create two classes: one for the phone number and other class for SMS( phone+body). I created a class for SMS as OnSMSReceiverEvent. It allows passing SMS phone and body: public OnSMSReceiverEvent(String SMSphone, String SMSbody). The class is used in SMS Broadcast Receiver as EventBus.getDefault().post(new OnSMSReceiverEvent(smsphone,smsbody)); In the activity, I called it as @Subscribe public void onSMSContentReceived(OnSMSReceiverEvent event), but it has error as D/EventBus: No subscribers registered for the event class.Gayomart
Nooo, you just have a second variable next to phonenumber in the same event class; another class is totally unnecessary! Note: If you are sending a separate broadcast receiver for SMS, then sure you can. If you are doing all these from same place, just one class is enough I hope this helpConversation
@Eenvincible: First class PhoneStateReceiver extends BroadcastReceiver is for listening to a incoming call and it returns a phone number, while the second class SMSReceiver extends BroadcastReceiver is used to listen the SMS's phone and its body. Hence, I must use two separate classes.Gayomart
That totally makes sense now; yes you need two classes!Conversation
Thanks. So, I used two class to receive data such as public OnSMSReceiverEvent(S‌​tring SMSphone, String SMSbody) and public OnReceiverEvent(S‌​tring phone). In activity I added In the activity, I called it as @Subscribe public void onSMSContentReceived‌​(OnSMSReceiverEvent event), but it has error as D/EventBus: No subscribers registered for the event class.Gayomart
Because you have not subscribed in the activity; you need to do it onStart and unregister onStop if you are in your activityConversation
I registered/unregistered it EventBus.getDefault().register(this); EventBus.getDefault().unregister(this);. Because one Activity will receive the data from these two class. So, I just register it one time. Is it correct?Gayomart
You only register once and unregister once! I wish we could chat and fix this once?Conversation
V
10

Simple without any third party libs.

Make sure BroadcastReceiver must be registered and also unregistered on OnPause().

You have to do two thing

Register a receiver in you activity like below.

public class MainActivity extends Activity {

    Context context;
    BroadcastReceiver updateUIReciver;

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

        IntentFilter filter = new IntentFilter();
        filter.addAction("service.to.activity.transfer");
        updateUIReciver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                //UI update here
                if (intent != null)
                    Toast.makeText(context, intent.getStringExtra("number").toString(), Toast.LENGTH_LONG).show();
            }
        };
        registerReceiver(updateUIReciver, filter);
    }
}

Now in you service

public class PhoneStateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            System.out.println("Receiver start");
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
            if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
                Intent local = new Intent();
                local.setAction("service.to.activity.transfer");
                local.putExtra("number", incomingNumber);
                context.sendBroadcast(local);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Veta answered 15/8, 2016 at 11:55 Comment(0)
C
4

There is a very simple design pattern you can use here to ease communication between your classes and also decouple your code: publisher/subscriber. My favorite library for this is EventBus:

First, add to your build.gradle file:

compile 'org.greenrobot:eventbus:3.0.0'

Then, create a simple POJO - Plain Old Java Object like this:

public class OnReceiverEvent{
   private String phoneNumber;

   public OnReceiverEvent(String phone){
      this.phoneNumber = phone;
   }

   public String getPhoneNumber(){
      return phoneNumber;
   }
}

Next, by making your Receiver class a publisher, and your Activity a subscriber, you should be able to easily pass the information to your activity like this:

//inside your PhoneStateReceiver class when you want to pass info

EventBus.getDefault().post(new OnReceiverEvent(phoneNumber));

Next, inside your activity, simply do this:

//onStart
@Override
public void onStart(){
   super.onStart();
   EventBus.getDefault().register(this);
}

//onStop
@Override
public void onStop(){
   super.onStop();

   EventBus.getDefault().unregister(this);
}

Finally, handle the posted data i.e phoneNumber value:

@Subscribe
public void onPhoneNumberReceived(OnReceiverEvent event){
   //get the phone number value here and do something with it

   String phoneNumber = event.getPhoneNumber();

   //display or something?
}

UPDATE

If you have another Event that you want this activity to subscribe to, simply create a method like you did in the first one using the @Subscribe annotation.

@Subscribe
public void onSomeOtherEvent(EventClassName event){
   //get the variables here as usual;
}

This is the easiest way to pass the data from your receiver to your activity without having to worry about starting the activity over and over!

I hope this helps and good luck!

Conversation answered 15/8, 2016 at 11:50 Comment(14)
Your answer gives a brief tutorial on pub/sub using EventBus.Latarsha
I think it is a great answer. I tested and it worked well. I just ask one more thing about onStop function. Because my app can be run in the background or as a service. At that purpose, the PhoneStateReceiver still receives the incoming phone. Is it possible to receives the incoming phone in the Activity when the phone is in the background or running in service? I want to ask it because when I press Home button, the data does not send to the ActivityGayomart
Well, if you want to pass the information from your receiver to your service (for when your app is closed and running in the background), you can simply do the same thing in your background service that you do in the activity. The good thing about EventBus, you can use it with services, activities, fragments, etcConversation
Actually I am using event based solution in my projects, so good answer and briefly. But if you are using instant run sometimes it is not working, consider it when testing.Madder
To get around Instant Run, you just unplug your device and it will run your entire project (although might take a few minutes depending on your machine speed) and once you have the whole code built, you should be fine!Conversation
@Conversation thats good point. But when Instant Run is enabled the first build takes long time. Maybe when testing Eventbus he can disable Instant Run.Madder
@Eenvincible: If I want to pass both phone and SMS, I must create two classes: one for the phone number and other class for SMS( phone+body). I created a class for SMS as OnSMSReceiverEvent. It allows passing SMS phone and body: public OnSMSReceiverEvent(String SMSphone, String SMSbody). The class is used in SMS Broadcast Receiver as EventBus.getDefault().post(new OnSMSReceiverEvent(smsphone,smsbody)); In the activity, I called it as @Subscribe public void onSMSContentReceived(OnSMSReceiverEvent event), but it has error as D/EventBus: No subscribers registered for the event class.Gayomart
Nooo, you just have a second variable next to phonenumber in the same event class; another class is totally unnecessary! Note: If you are sending a separate broadcast receiver for SMS, then sure you can. If you are doing all these from same place, just one class is enough I hope this helpConversation
@Eenvincible: First class PhoneStateReceiver extends BroadcastReceiver is for listening to a incoming call and it returns a phone number, while the second class SMSReceiver extends BroadcastReceiver is used to listen the SMS's phone and its body. Hence, I must use two separate classes.Gayomart
That totally makes sense now; yes you need two classes!Conversation
Thanks. So, I used two class to receive data such as public OnSMSReceiverEvent(S‌​tring SMSphone, String SMSbody) and public OnReceiverEvent(S‌​tring phone). In activity I added In the activity, I called it as @Subscribe public void onSMSContentReceived‌​(OnSMSReceiverEvent event), but it has error as D/EventBus: No subscribers registered for the event class.Gayomart
Because you have not subscribed in the activity; you need to do it onStart and unregister onStop if you are in your activityConversation
I registered/unregistered it EventBus.getDefault().register(this); EventBus.getDefault().unregister(this);. Because one Activity will receive the data from these two class. So, I just register it one time. Is it correct?Gayomart
You only register once and unregister once! I wish we could chat and fix this once?Conversation
H
1

So I understand you don't want to recreate your Activity everytime.

In your Intent changing this flag will help you :

intent_phonenum.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

In Intent class if you read method summary of FLAG_ACTIVITY_CLEAR_TOP:

  • If set, and the activity being launched is already running in the
  • current task, then instead of launching a new instance of that activity,
  • all of the other activities on top of it will be closed and this Intent
  • will be delivered to the (now on top) old activity as a new Intent. (you can read more ...)

In this case: If your app is running and you have an Activity instance then Intent will not recreate your Activity. But assume that your app is in closed state and when BroadcastReceiver triggered, the Intent will create new Activity because you don't have instance of that Activity.

@Edit :

You can specify special Intent like that in your BroadcastReceiver:

public void onReceive(Context context, Intent intent) {
  Bundle extras = intent.getExtras();
  Intent i = new Intent("mycustombroadcast");
  i.putExtra("phone_num", incomingNumber); 
  context.sendBroadcast(i);
}

Then in your Activity inside onCreate() register receiver like that :

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

        Bundle bundle = intent.getExtras();

        int incoming_number= bundle.getInt("phone_num");

        Log.e("incoming number", "" + incoming_number);
    }
};
//then register receiver like that :
registerReceiver(broadcastReceiver, new IntentFilter("mycustombroadcast"));

You can unregister Receiver in onDestroy() : unregisterReceiver(broadcastReceiver);

Also another way is overriding onNewIntent() in your Activity:

@Override
protected void onNewIntent(Intent intent) {
 //your intent is here, you can do sth.
 super.onNewIntent(intent);
} 
Houlberg answered 15/8, 2016 at 11:36 Comment(8)
So, How can I know the event that a data sends to ReceiverActivity. Because I need to forward the data to server via socketGayomart
@user8430 You wanna get incoming number in your ReceiverActivity ?Madder
Yes. But it must not in onCreate() function because the function creates a socket and UI, thus I do not want to call the received data in the functionGayomart
How do you think the function onActivityResult()? Can we receive data in the function?Gayomart
@user8430 I've edited my answer including couple ways. Can you try and come with result ?Madder
Thank you. I am looking at the solution of EventBus. I will test your solutionGayomart
@user8430 Actually I am using EventBus in my projects, if you like it go on. Have a good day.Madder
Thank you. Your solution is good too. Many solutions are posted. I will choose the best one. ThanksGayomart
M
1

This is what worked for me.

BroadcastReceiver

class SMSReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == Telephony.Sms.Intents.SMS_RECEIVED_ACTION) {
            val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
            for (message in messages) {
                val ourIntent = Intent(intent.action).apply { putExtra("message", message.messageBody) }
                LocalBroadcastManager.getInstance(context).sendBroadcast(ourIntent)
            }
        }
    }
}

Activity

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    private val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val message = intent.getStringExtra("message").toString()
            println("__print.MainActivity.onReceive::message::$message")
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val filter = IntentFilter().apply { addAction(Telephony.Sms.Intents.SMS_RECEIVED_ACTION) }
        LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter)
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(receiver)
    }
}

SOURCE

Mala answered 3/9, 2024 at 9:22 Comment(1)
It really saved me.Acridine

© 2022 - 2025 — McMap. All rights reserved.