How to update UI in a BroadcastReceiver
I suggest you use a Handler.
- Initialize a Handler in the Activity, example:
handler = new Handler()
- Provide the handler to the BroadcastReceiver in the constructor, in the same way as I did for NissanTabBroadcast above
- Use
post()
method of your Handler instance in theonReceive()
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 update the UI of Activity from BroadCastReceiver
A BroadcastReceiver
can be used in many ways but when it comes to something as specific as updating the UI components of an Activity
, there is little advantage to declaring / defining a BroadcastReceiver
in it's own Java class file.
Reasoning - the BroadcastReceiver
has to have some prior "knowledge" of the Activity
and what it is required to do in order to update the UI. In effect the BroadcastReceiver
is tied to the Activity
itself and it makes sense to declare / define it as an inner class.
Another important aspect is the Activity
needs to be in a "running" (i.e., visible) state in order to guarantee manipulation of UI components. In this case, registering the receiver in onResume()
and unregistering in onPause()
will help prevent problems.
Using a generic template I'd do something like the following...
class MyActivity extends Activity {
boolean mIsReceiverRegistered = false;
MyBroadcastReceiver mReceiver = null;
// onCreate(...) here
@Override
protected void onResume() {
// Other onResume() code here
if (!mIsReceiverRegistered) {
if (mReceiver == null)
mReceiver = new MyBroadcastReceiver();
registerReceiver(mReceiver, new IntentFilter("YourIntentAction"));
mIsReceiverRegistered = true;
}
}
@Override
protected void onPause() {
if (mIsReceiverRegistered) {
unregisterReceiver(mReceiver);
mReceiver = null;
mIsReceiverRegistered = false;
}
// Other onPause() code here
}
private void updateUI(Intent intent) {
// Do what you need to do
}
private class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
updateUI(intent);
}
}
}
EDIT: A couple of extra notes...
- The life-cycle of a
BroadcastReceiver
is between entering and leavingonReceive(...)
. Once it has returned fromonReceive(...)
the instance remains in a dormant state waiting for the next broadcast. - Directly related to point 1 - a
BroadcastReceiver
isn't designed for "heavy lifting". Basically theonReceive(...)
method should be kept as simple as possible. Any methods it calls should also be as light-weight as possible...get in, do your stuff, get out then wait for the next broadcast. If updating the UI is going to take some time (perhaps updating aListView
by re-querying a database for a large amount of data for example), consider calling code which performs asynchronously (anAsyncTask
for example).
How can I update the UI using a broadcast receiver?
The problem is that you can't subscribe to ActionTimeTick the way you do. From the documentation:
Broadcast Action: The current time has changed. Sent every minute. You
cannot receive this through components declared in manifests, only by
explicitly registering for it with Context.registerReceiver().
In Xamarin, the IntentFilter attribute is used by monodroid.exe when generating the intent-filter
elements in the AndroidManifest.xml file so that won't work.
What you need to do, is remove the IntentFilter
attribute and register the receiver programmatically in your MainActivity
(for example).
protected override void OnResume()
{
base.OnResume();
RegisterReceiver(yourReceiver, new IntentFilter(Intent. ActionTimeTick));
}
protected override void OnPause()
{
base.OnPause();
UnregisterReceiver(yourReceiver);
}
update ui from broadcast receiver
One way to update your Activity from a BroadcastReciever is to create a BroadcastReciever in MainActivity:
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Extract data included in the Intent
// String message = intent.getStringExtra("message");
//update the TextView
}
};
Reigester this BroadcastReciever in onResume:
this.registerReceiver(mMessageReceiver, new IntentFilter("some_unique_name"));
And unregiester onPause:
this.unregisterReceiver(mMessageReceiver);
In AlarmReceiver just use:
private void updateYourActivity(Context context) {
Intent intent = new Intent("some_unique_name");
// put whatever data you want to send, if any
// intent.putExtra("message", message);
// send broadcast
context.sendBroadcast(intent);
}
Kotlin: Call a function to update UI from BroadcastReceiver onReceive
Sharing the info to register BroadcastReceiver in Kotlin
Step 1. Create BroadcastReceiver in MainActivity.kt
private val mPlugInReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
Intent.ACTION_POWER_CONNECTED -> {
//update your main background color
updateBackgroundColor()
}
Intent.ACTION_POWER_DISCONNECTED -> {
//update your main background color
updateBackgroundColor()
}
}
}
}
Step 2. Create IntentFilter
private fun getIntentFilter(): IntentFilter {
val iFilter = IntentFilter()
iFilter.addAction(Intent.ACTION_POWER_CONNECTED)
iFilter.addAction(Intent.ACTION_POWER_DISCONNECTED)
return iFilter
}
Step 3. Register receiver at onStart()
override fun onStart() {
super.onStart()
registerReceiver(mPlugInReceiver, getIntentFilter())
}
Step 4. Unregister receiver at onStop()
override fun onStop() {
super.onStop()
unregisterReceiver(mPlugInReceiver)
}
If you have custom BroadcastReceiver, you can register using LocalBroadcastManager
and create your local IntentFilter
private val mLocalBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
AnyService.UPDATE_ANY -> {
}
}
}
}
private fun getLocalIntentFilter(): IntentFilter {
val iFilter = IntentFilter()
iFilter.addAction(AnyService.UPDATE_ANY)
return iFilter
}
Register local receiverLocalBroadcastManager.getInstance(applicationContext).registerReceiver(mLocalBroadcastReceiver, getLocalIntentFilter())
Unregister local receiver LocalBroadcastManager.getInstance(applicationContext).unregisterReceiver(mLocalBroadcastReceiver)
Update active Activity UI with BroadcastReceiver
The easiest way is to make your AlarmReceiver an inner class of your activity. This way it will have access to all fields and methods of your activity. If you don't use it anywhere else, it might as well be anonymous.
To make it update your activity only when it's active, register your receiver in onResume()
and unregister it in onPause()
. Notice the IntentFilter
specifying intent actions your BroadcastReceiver
will respond to.
Example:
private BroadcastReceiver updateReceiver;
//...
@Override
protected void onResume() {
super.onResume();
updateReceiver=new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//your list update code here
}
};
IntentFilter updateIntentFilter=new IntentFilter("update");
registerReceiver(updateReceiver, updateIntentFilter);
}
@Override
protected void onPause() {
super.onPause();
if (this.updateReceiver!=null)
unregisterReceiver(updateReceiver);
}
If you still want your AlarmReceiver to be a separate class, pass some kind of callback to it during initialization:
public interface AlarmReceiverCallback {
public void onAlarmReceived(Intent intent);
}
//in your AlarmReceiver class:
private AlarmReceiverCallback callback;
public AlarmReceiver(AlarmReceiverCallback callback) {
this.callback=callback;
}
@Override
public void onReceive(Context context, Intent intent) {
callback.onAlarmReceived(intent);
}
Initialization of your AlarmReceiver
will look the following way:
updateReceiver=new AlarmReceiver(new AlarmReceiverCallback() {
@Override
public void onAlarmReceived(Intent intent) {
//your list update code here
}
});
Android: How to update the UI from a static BroadcastReceiver?
The thing you need is not broadcast receiver along with AlarmManager or JobScheduler for api above 21 and greenrobot event bus.
AlarmManager Schedules the broadcast call once a day or at any time you want and every time if the broadcast is called you can trigger event from eventbus and receive that event in the place where you want it. The thing why to use event bus is we do not need to handle if the view is visible or not.iF the view is in re use state it triggers the event the view and one method is called by event bus and in that method you can do anything you want to do with view.
personally i don't prefer service because service execution is really expensive now a days.
Note: the package name where you put alarm manager and broadcast
receiver should be "alert" some samsung mobile are very optimized so
they will only let the package name with "alert to run fully". You
will also need on boot receiver to register receiver and schedule
alarmmanager in case if the phone is booted.
How to update the UI with BroadcastReceiver onReceive in Android?
Use this code:
public class MainActivity extends AppCompatActivity {
...
BroadcastReceiver br = new BroadcastReceiver(){
@Override
public void onReceive( Context context, Intent intent ){
//update UI here directly
View view = findViewById( R.id.example );
}
};
@Override
protected void onResume(){
super.onResume();
// Check state here
...
IntentFilter filter = new IntentFilter();
filter.addAction( ConnectivityManager.CONNECTIVITY_ACTION );
registerReceiver( br, filter);
}
@Override
protected void onPause(){
super.onPause();
unregisterReceiver( br );
}
...
}
Update fragment ui from broadcast receiver in MvvM architecture and Kotlin
Eventually I found a clean and modern solution like I wanted. The trick is to wrap the BroadcastReceiver in a LiveData. By using this pattern you can observe the connection LiveDada directly from your fragment and update the ui. Of Course it is also possible (like sergiy tikhonov mentioned) to have a companion object in the base activity with a LiveData that is getting updated from the BroadcastReceiver and you can observe it from the fragment, but I didn't like this approach.
So it works like this:
Create an enum with the states of the network.
enum class NetworkAvailability {
UNKNOWN,
CONNECTED,
DISCONNECTED
}
Create the class that extends the LiveData. I am using the singleton pattern because I don't want multiple instances. I create a BroadcastReceiver instance and I register/unregister it in onActive and onInactive methods of the LiveData.
class ConnectionStateLiveData(val context: Context) : LiveData<NetworkAvailability>() {
companion object {
private lateinit var instance: ConnectionStateLiveData
fun get(context: Context): ConnectionStateLiveData {
instance = if (::instance.isInitialized){
instance
}else{
ConnectionStateLiveData(context)
}
return instance
}
}
private val connectivityBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val netInfo = connectivityManager.activeNetworkInfo
value = if (netInfo != null && netInfo.isConnected) {
NetworkAvailability.CONNECTED
} else {
NetworkAvailability.DISCONNECTED
}
}
}
override fun onActive() {
super.onActive()
value = NetworkAvailability.UNKNOWN
val broadcastIntent = IntentFilter()
broadcastIntent.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
context.registerReceiver(connectivityBroadcastReceiver, broadcastIntent)
}
override fun onInactive() {
super.onInactive()
context.unregisterReceiver(connectivityBroadcastReceiver)
}
}
And finally I observe the LiveData in my fragment.
ConnectionStateLiveData.get(context).observe(viewLifecycleOwner, Observer {
if (it == NetworkAvailability.CONNECTED) {
binding.noInternetTextView.visibility = View.GONE
}
})
Related Topics
Intercept Back Button from Soft Keyboard
Googleservice Failed to Initialize
Failed to Resolve: Com.Google.Firebase:Firebase-Core:9.0.0
Passing Arraylist Through Intent
What Characters Allowed in File Names on Android
Actionbar Not Shown with Appcompat
Google Map Signed API Key Errors in Android
An Established Connection Was Aborted by the Software in Your Host MAChine
Bad Class File Magic or Version
Where Is the Layout Preview in Android Studio
Android Facebook Sdk 4 in Eclipse
Android Sample Bluetooth Code to Send a Simple String via Bluetooth
Android: Allow Portrait and Landscape for Tablets, But Force Portrait on Phone
Google Maps Android API V2 Authorization Failure