How to update the UI of Activity from BroadCastReceiver - android

I am learning Android concepts Activity and BroadCastReceiver. I want to update the content of Activity from the BroadtCastReceiver both are in different java class.
It is something like
MyActivity.java and MyBroadtCastReceiver.java
Is this possible to do this in Android ?

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 leaving onReceive(...). Once it has returned from onReceive(...) 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 the onReceive(...) 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 a ListView by re-querying a database for a large amount of data for example), consider calling code which performs asynchronously (an AsyncTask for example).

Yes its possible. This is what i do.
Class i send the broadcast from (BackgroundActivity.java):
public static final String BROADCAST_BUFFER_SEND_CODE = "com.example.SEND_CODE";
onCreate(){
bufferIntentSendCode = new Intent(BROADCAST_BUFFER_SEND_CODE);
}
private void sendBufferingBroadcastSendCode() {
bufferIntentSendCode.putExtra("buffering", "1");
sendBroadcast(bufferIntentSendCode);
}
The class it will receive the broadcast(SendCode.java):
onResume(){
registerReceiver(broadcastBufferReceiver, new IntentFilter(BackgroundActivity.BROADCAST_BUFFER_SEND_CODE));
}
// set up broadcast receiver
private BroadcastReceiver broadcastBufferReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent bufferIntent) {
SendCode.this.LoadMessages(alarmNumber);
}
};
I unregister it in onPause
this.unregisterReceiver(broadcastBufferReceiver);

Register a new BroadcastReceiver object in your activity with same intent-filters as your MyBroadtCastReceiver. Since BroadcastReceiver and MyBroadtCastReceiver has same intent-filters both of their onReceive() will be invoked. Whatever update that you want to do in Activity can be done in onReceive of your BroadcastReceiver.

You can do like this:
public class MyActivity extends Activity{
// used to listen for intents which are sent after a task was
// successfully processed
private BroadcastReceiver mUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
new UpdateUiTask().execute();
}
};
#Override
public void onResume() {
registerReceiver(mUpdateReceiver, new IntentFilter(
YOUR_INTENT_ACTION));
super.onResume();
}
#Override
public void onPause() {
unregisterReceiver(mUpdateReceiver);
super.onPause();
}
// used to update the UI
private class UpdateUiTask extends AsyncTask<Void, Void, String> {
#Override
protected void onPreExecute() {
}
#Override
protected String doInBackground(Void... voids) {
Context context = getApplicationContext();
String result = "test";
// Put the data obtained after background task.
return result;
}
#Override
protected void onPostExecute(String result) {
// TODO: UI update
}
}
}

Squonk-s answer only works, if the Activity is active currently.
If you dont want to declare / define your BroadcastReceiver (BR) in an other Activity, or if you want to make some changes even if the app is not foreground, than your solution would look something like this.
First, you declare the BR, and save, or override the data needed to show in Acitvity.
public class MyBR extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// override the data. Eg: save to SharedPref
}
}
Then in Activity, you show the data
TextView tv = findViewById(R.id.tv);
tv.setText(/*get the data Eg: from SharedPref*/);
And you should use a Timer to refresh the tv as well:
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView tv = findViewById(R.id.tv);
tv.setText(/*get the data Eg: from SharedPref*/);
}
});
}
}, REFRESH_RATE, REFRESH_RATE);
REFRESH_RATE could be something like 1 second, but you decide.

try like this it may help you.
Define this method in activity's oncreate method in which you want to update ui,
BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//your code to update ui
}
};
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("giveyourappname"));
Define this action at place from where you want to update ui,
try{
ActivityManager am = (ActivityManager) this .getSystemService(ACTIVITY_SERVICE);
List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
Log.d("Activity", "Current Activity ::" + taskInfo.get(0).topActivity.getClassName());
Log.d("Package", "Package Name : "+ componentInfo.getPackageName());
if(componentInfo.getPackageName().equals("your application package name")){
Intent intent = new Intent("giveyourappname");
//add data you wnat to pass in intent
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}catch(Throwable e){
e.printStackTrace();
}

Related

How can I update an not MainActivity Activity via a Thread?

I want to update an Activity which is not the MainActivity.
So I start a second Activity via a onClick method in MainActivity.
Now the Activty "SecondActivity" is at front.
When I started a Thread in the "MainActivity" how can I reference to the "SecondActivity" to update their TextViews and so on?
PseudoCode
public class activity_MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ThreadSome threadSome= new ThreadSome();
threadSome.start()
}
onClick(View View){
Intent intent = new Intent(this, activity_Second.class);
startActivity(intent);
}
}
Inside Thread
public class ThreadSome extends Thread {
#Override
public void run() {
//This is what I don't know, so I just write what I want to do.
// I know the following Code is wrong and not working.
activity_Second.someTextView.setText("Hi");
}
}
Is a WeakReference the best way to do this, or better work with static TextView objects? How would you solve this problem?
Based on your description, I think you want to do something where there will be some ui change in activity stack based on some event performed in the forground activity. There is a good way to use onActivityResult() via startActivityForResult() but if this is not fullfilling your requirement directly then you can try something like below:
/**
UpdateActivity is the activity where some ui update or action will be taken based on event in EventActivity.
**/
public class UpdateActivity extends Activity {
private BroadcastReceiver mReceiver;
public static final String ACTION_UPDATE = "com.my.internal.activity.action";
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_update);
......
//Prepared Intent for broadcast receiver
IntentFilter intentFilter = new IntentFilter(ACTION_UPDATE);
//registering our receiver
this.registerReceiver(mReceiver, intentFilter);
.....
}
//This is the receiver section where you need to do the ui update
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//extract our message from intent
String some_msg = intent.getStringExtra("msg_1"); //parameter received if passed in intent when broadcast called.
//log our message value
Log.i("Message", some_msg);
updateActivityUi();
}
};
private void updateActivityUi() {
// you need to write the code for the update which you want to do after an event done in other activity.
}
#Override
protected void onDestroy() {
super.onDestroy();
//unregister our receiver
this.unregisterReceiver(this.mReceiver);
}
}
public class EventActivity extends Activity {
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event);
......
//Sending BroadcastReceiver against the action ACTION_UPDATE and it will be received by UpdateActivity.
if(condition_for_event) {
Intent i = new Intent(UpdateActivity.ACTION_UPDATE).putExtra("msg_1", "Hey! an event performed here.");
this.sendBroadcast(i);
}
.....
}
....
}
Let me know if it solved your issue.

Sending Broadcast Receiver from IntentService to Fragment (Activity)

I have a problem when I send an Broadcast Receiver from IntentService to Fragment (Activity).
I have an application with two types of process:
The first process have MainActivity and MainFragment. At this fragment, I implemented like that:
private void registerStickerReload() {
mStickerReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(DataFetcherService.ACTION_RELOAD_STICKER)) {
// Some code here
}
}
};
IntentFilter intentFilter = new IntentFilter(
DataFetcherService.ACTION_RELOAD_STICKER);
mLocalBroadcastManager.registerReceiver(mStickerReceiver, intentFilter);
}
and:
private void unregisterStickerReload() {
if (mLocalBroadcastManager != null) {
mLocalBroadcastManager.unregisterReceiver(mStickerReceiver);
}
}
I register receiver from onStart() and unregister from onStop().
From the second process, I run DataFetcherService class like IntentService.
My code here:
private class BasicLoader extends AsyncTask<Request, Void, Response> {
public BasicLoader() {
}
#Override
protected Response doInBackground(Request... params) {
Request request = params[0];
Response response = request.execute();
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Intent intent = new Intent(ACTION_RELOAD_STICKER);
LocalBroadcastManager.getInstance(getApplicationContext())
.sendBroadcast(intent);
}
});
return response;
}
#Override
protected void onPostExecute(Response result) {
super.onPostExecute(result);
}
}
I debugged and the LocalBroadcastManager sent the Broadcast but it don't have any action to onReceive(Context, Intent).
Here's some cases I tried:
Remove Handler.
Remove Looper.getMainLooper().
Remove unregisterStickerReload().
But It's not working.
As you can see LocalBroadcastManager class overview:
Helper to register for and send broadcasts of Intents to local objects
within your process.
Because DataFetcherService service is running in separate process from datafetcher and activity MainFragment is running in different process.that's why onReceive method is not called when sending Broadcast from doInBackground method.
To receive and send Broadcast between different process or applications use BroadcastReceiver

do something inside mainActivity whenever a push notification is received

In my app, whenever I receive a push notification, I will perform a check if my mainActivity is visible to the user to do something...
I have a static boolean value that is set true inside onResume of mainActivity, and false inside it's onPause.
What should I do inside the onMessage
#Override
protected void onMessage(Context context, Intent intent)
{
if(mainActivity == visible)
//do something inside mainactivity.. change text inside edittext
else
//do something else
}
any insights ?
I'm not a fan of keeping static references to activities. I think they're a can of worms ready to explode on you. So you'll suggest an alternative to #TeRRo answer:
on your global BroadcastReceiver onMessage you'll send a LocalBroadcast that your activity will be listening to. Like this:
private static final String ACTION_PUSH_RECEIVED = "com.myapp.mypackage.action.pushReceived";
public static final IntentFilter BROADCAST_INTENT_FILTER = new IntentFilter(ACTION_PUSH_RECEIVED);
#Override
protected void onMessage(Context context, Intent intent) {
Intent i = new Intent(ACTION_PUSH_RECEIVED);
i.putExtra( ... add any extra data you want... )
LocalBroadcastManager.getInstance(context).sendBroadcast(i);
}
and now we make the activity listen to it:
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(context)
.registerReceiver(mBroadcastReceiver, BroadcastReceiverClass.BROADCAST_INTENT_FILTER);
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(context)
.unregisterReceiver(mBroadcastReceiver);
super.onPause();
}
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent){
// read any data you might need from intent and do your action here
}
}
To avoid this, you should manage activities references. Add the name of the application in the manifest file:
<application
android:name=".MyApp"
....
</application>
Your application class :
public class MyApp extends Application {
public void onCreate() {
super.onCreate();
}
private Activity mCurrentActivity = null;
public Activity getCurrentActivity(){
return mCurrentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity){
this.mCurrentActivity = mCurrentActivity;
}
}
Create a new Activity :
public class MyBaseActivity extends Activity {
protected MyApp mMyApp;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMyApp = (MyApp)this.getApplicationContext();
}
protected void onResume() {
super.onResume();
mMyApp.setCurrentActivity(this);
}
protected void onPause() {
clearReferences();
super.onPause();
}
protected void onDestroy() {
clearReferences();
super.onDestroy();
}
private void clearReferences(){
Activity currActivity = mMyApp.getCurrentActivity();
if (currActivity != null && currActivity.equals(this))
mMyApp.setCurrentActivity(null);
}
}
So, now instead of extending Activity class for your activities, just extend MyBaseActivity. Now, you can get your current activity from application or Activity context like that :
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
Or why don't you use the Local broadcasts when you receive the push notification, and receive it in your activity, and do respective changes or actions.
And if they are UI intensive tasks, bind your activity to a service, and receive the push notification and perform the action in this service and use the result in the activity.

Communication between Activity and Service

I am trying to make my own MusicPlayer for android. Where i came to a problem is running some things in background. Main activity manages GUI and up to now all the songs are playing. I wanted to separate GUI and music playing classes. I want to put music managing part in Service and leave other things as they are now.
My problem is that i can't organize communication between Activity and Service as lot of communication is happening between them including moving objects in both directions. I tried many techniques that I searched here on Stack Overflow but every time I had problems. I need Service to be able to send objects to Activity and vice versa. When I add widget i also want it to be able to communicate with Service.
Any tips are appreciated, if you need source code place comment bellow but now in this transition it became chaotic.
Is there any more advanced tutorial on this than calling one method that returns random number from service? :P
EDIT: Possible solution is to use RoboGuice library and move objects with injection
I have implemented communication between Activity and Service using Bind and Callbacks interface.
For sending data to the service I used Binder which retruns the Service instace to the Activity, and then the Activity can access public methods in the Service.
To send data back to the Activity from the Service, I used Callbacks interface like you are using when you want to communicate between Fragment and Activity.
Here is some code samples for each:
The following example shows Activity and Service bidirectional relationship:
The Activity has 2 buttons:
The first button will start and stop the service.
The second button will start a timer which runs in the service.
The service will update the Activity through callback with the timer progress.
My Activity:
//Activity implements the Callbacks interface which defined in the Service
public class MainActivity extends ActionBarActivity implements MyService.Callbacks{
ToggleButton toggleButton;
ToggleButton tbStartTask;
TextView tvServiceState;
TextView tvServiceOutput;
Intent serviceIntent;
MyService myService;
int seconds;
int minutes;
int hours;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceIntent = new Intent(MainActivity.this, MyService.class);
setViewsWidgets();
}
private void setViewsWidgets() {
toggleButton = (ToggleButton)findViewById(R.id.toggleButton);
toggleButton.setOnClickListener(btListener);
tbStartTask = (ToggleButton)findViewById(R.id.tbStartServiceTask);
tbStartTask.setOnClickListener(btListener);
tvServiceState = (TextView)findViewById(R.id.tvServiceState);
tvServiceOutput = (TextView)findViewById(R.id.tvServiceOutput);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
Toast.makeText(MainActivity.this, "onServiceConnected called", Toast.LENGTH_SHORT).show();
// We've binded to LocalService, cast the IBinder and get LocalService instance
MyService.LocalBinder binder = (MyService.LocalBinder) service;
myService = binder.getServiceInstance(); //Get instance of your service!
myService.registerClient(MainActivity.this); //Activity register in the service as client for callabcks!
tvServiceState.setText("Connected to service...");
tbStartTask.setEnabled(true);
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Toast.makeText(MainActivity.this, "onServiceDisconnected called", Toast.LENGTH_SHORT).show();
tvServiceState.setText("Service disconnected");
tbStartTask.setEnabled(false);
}
};
View.OnClickListener btListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v == toggleButton){
if(toggleButton.isChecked()){
startService(serviceIntent); //Starting the service
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); //Binding to the service!
Toast.makeText(MainActivity.this, "Button checked", Toast.LENGTH_SHORT).show();
}else{
unbindService(mConnection);
stopService(serviceIntent);
Toast.makeText(MainActivity.this, "Button unchecked", Toast.LENGTH_SHORT).show();
tvServiceState.setText("Service disconnected");
tbStartTask.setEnabled(false);
}
}
if(v == tbStartTask){
if(tbStartTask.isChecked()){
myService.startCounter();
}else{
myService.stopCounter();
}
}
}
};
#Override
public void updateClient(long millis) {
seconds = (int) (millis / 1000) % 60 ;
minutes = (int) ((millis / (1000*60)) % 60);
hours = (int) ((millis / (1000*60*60)) % 24);
tvServiceOutput.setText((hours>0 ? String.format("%d:", hours) : "") + ((this.minutes<10 && this.hours > 0)? "0" + String.format("%d:", minutes) : String.format("%d:", minutes)) + (this.seconds<10 ? "0" + this.seconds: this.seconds));
}
}
And here is the service:
public class MyService extends Service {
NotificationManager notificationManager;
NotificationCompat.Builder mBuilder;
Callbacks activity;
private long startTime = 0;
private long millis = 0;
private final IBinder mBinder = new LocalBinder();
Handler handler = new Handler();
Runnable serviceRunnable = new Runnable() {
#Override
public void run() {
millis = System.currentTimeMillis() - startTime;
activity.updateClient(millis); //Update Activity (client) by the implementd callback
handler.postDelayed(this, 1000);
}
};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Do what you need in onStartCommand when service has been started
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
//returns the instance of the service
public class LocalBinder extends Binder{
public MyService getServiceInstance(){
return MyService.this;
}
}
//Here Activity register to the service as Callbacks client
public void registerClient(Activity activity){
this.activity = (Callbacks)activity;
}
public void startCounter(){
startTime = System.currentTimeMillis();
handler.postDelayed(serviceRunnable, 0);
Toast.makeText(getApplicationContext(), "Counter started", Toast.LENGTH_SHORT).show();
}
public void stopCounter(){
handler.removeCallbacks(serviceRunnable);
}
//callbacks interface for communication with service clients!
public interface Callbacks{
public void updateClient(long data);
}
}
Update: July 10 2016
IMO I think using BroadcastReceiver for custom events is better way
as the Messengers mentioned don't handle activity recreation on device
rotation as well as possible memory leaks.
You may create custom BroadCast Receiver for events in the activity, Then you may also use Messengers.
In your Activity
create a MessageHandler class as
public static class MessageHandler extends Handler {
#Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case HIDE:
progressBar.setVisibility(View.GONE);
break;
case SHOW:
progressBar.setVisibility(View.VISIBLE);
break;
}
}
}
Now you can have it's instance as
public static Handler messageHandler = new MessageHandler();
Start your Service with this Handler object as an extra data as
Intent startService = new Intent(context, SERVICE.class)
startService.putExtra("MESSENGER", new Messenger(messageHandler));
context.startService(startService);
In your Service you receive this object from the intent and initialize the Messenger variable in Service as
private Messenger messageHandler;
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
sendMessage(ProgressBarState.SHOW);
And then write a method sendMessage to send messages to activity.
public void sendMessage(ProgressBarState state) {
Message message = Message.obtain();
switch (state) {
case SHOW :
message.arg1 = Home.SHOW;
break;
case HIDE :
message.arg1 = Home.HIDE;
break;
}
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
The sample code above shows and hides a ProgressBar in Activity as messages are received from Service.
Intents are good solution for communication between Activitiy and Service.
A fast solution for receive intents in your service is subclassing IntentService class. It handles asynchronous requests expressed as Intents using a queue and worker thread.
For communication from service to Activity you can broadcast the intent but instead of using normal sendBroadcast() from Context, a more efficent way is to use LocalBroadcastManager from support library.
Example service.
public class MyIntentService extends IntentService {
private static final String ACTION_FOO = "com.myapp.action.FOO";
private static final String EXTRA_PARAM_A = "com.myapp.extra.PARAM_A";
public static final String BROADCAST_ACTION_BAZ = "com.myapp.broadcast_action.FOO";
public static final String EXTRA_PARAM_B = "com.myapp.extra.PARAM_B";
// called by activity to communicate to service
public static void startActionFoo(Context context, String param1) {
Intent intent = new Intent(context, MyIntentService.class);
intent.setAction(ACTION_FOO);
intent.putExtra(EXTRA_PARAM1, param1);
context.startService(intent);
}
public MyIntentService() {
super("MyIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_FOO.equals(action)) {
final String param1 = intent.getStringExtra(EXTRA_PARAM_A);
// do something
}
}
}
// called to send data to Activity
public static void broadcastActionBaz(String param) {
Intent intent = new Intent(BROADCAST_ACTION_BAZ);
intent.putExtra(EXTRA_PARAM_B, param);
LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
bm.sendBroadcast(intent);
}
}
Example Activity
public class MainActivity extends ActionBarActivity {
// handler for received data from service
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(MyIntentService.BROADCAST_ACTION_BAZ)) {
final String param = intent.getStringExtra(EXTRA_PARAM_B);
// do something
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new IntentFilter();
filter.addAction(MyIntentService.BROADCAST_ACTION_BAZ);
LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
bm.registerReceiver(mBroadcastReceiver, filter);
}
#Override
protected void onDestroy() {
LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
bm.unregisterReceiver(mBroadcastReceiver);
super.onDestroy();
}
// send data to MyService
protected void communicateToService(String parameter) {
MyIntentService.startActionFoo(this, parameter);
}
}
I think there is a problem with the correct answer. I have not enough reputation to comment on it.
Right in the answer:
Activity call bindService() to get pointer to Service is ok. Because service context is maintained when connection is maintained.
wrong in the answer:
service pointer to Activity class to call back is bad way. Activity instance maybe not null during Activity context is being Release => exception here.
solution for the wrong in the answer:
service send intent to Activity. and Activity receiver intent via BroadcastReceiver.
Note:
in this case, Service and Activity in the same Process, you should use LocalBroadcastManager to send intent. It make performance and security better
This is a simple example of communication between activity and service
Activity
MyReceiver myReceiver; //my global var receiver
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layourAwesomexD);
registerReceiver();
}
//When the activity resume, the receiver is going to register...
#Override
protected void onResume() {
super.onResume();
checkStatusService(); // verficarStatusServicio(); <- name change
registerReceiver();
}
//when the activity stop, the receiver is going to unregister...
#Override
protected void onStop() {
unregisterReceiver(myReceiver); //unregister my receiver...
super.onStop();
}
//function to register receiver :3
private void registerReceiver(){
//Register BroadcastReceiver
//to receive event from our service
myReceiver = new MyReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MyService.SENDMESAGGE);
registerReceiver(myReceiver, intentFilter);
}
// class of receiver, the magic is here...
private class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent arg1) {
//verify if the extra var exist
System.out.println(arg1.hasExtra("message")); // true or false
//another example...
System.out.println(arg1.getExtras().containsKey("message")); // true or false
//if var exist only print or do some stuff
if (arg1.hasExtra("message")) {
//do what you want to
System.out.println(arg1.getStringExtra("message"));
}
}
}
public void checkStatusService(){
if(MyService.serviceStatus!=null){
if(MyService.serviceStatus == true){
//do something
//textview.text("Service is running");
}else{
//do something
//textview.text("Service is not running");
}
}
}
Service
public class MyService extends Service {
final static String SENDMESAGGE = "passMessage";
public static Boolean serviceStatus = false;
#Override
public void onCreate() {
super.onCreate();
serviceStatus=true;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {return null;}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//you service etc...
passMessageToActivity("hello my friend this an example of send a string...");
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
passMessageToActivity("The service is finished, This is going to be more cooler than the heart of your ex...");
System.out.println("onDestroy");
serviceStatus=false;
}
private void passMessageToActivity(String message){
Intent intent = new Intent();
intent.setAction(SENDMESAGGE);
intent.putExtra("message",message);
sendBroadcast(intent);
}
}
if we don't unregister BroadcastReceiver we will have an error, you need to unregister when the activity go onPause, onStop, onDestroy...
if you don't register BroadcastReceiver when you back to activity, it will not listen anything from the service... the service will send information to BroadcastReceiver but it will not receive anything because it isn't registered.
When you create more than one service, the following services are going to begin in onStartCommand.
You can pass information to service with intent and you get it in onStartCommand
Difference about return in onStartCommand: Difference between START_STICKY and START_REDELIVER_INTENT? and check the official website of google: Services
The best way in this case is to communicate by doing broadcasting from your service for different actions and receiving it in your activity. You can create a custom broadcast and send some codes defining specific events like complete, change, prepare etc...
Most easy and efficient way will be using EventBus from GreenRobot.
Use simple 3 steps:
1 Define events
public static class MessageEvent { /* Additional fields if needed */ }
2 Prepare subscribers: Declare and annotate your subscribing method, optionally specify a thread mode:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
Register and unregister your subscriber. For example on Android, activities and fragments should usually register according to their life cycle:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
3 Post events:
EventBus.getDefault().post(new MessageEvent());
Very easy yet powerful way is to use EventBus you can add it to your gradle build and enjoy the easy publisher/subscriber pattern .

Android update activity UI from service

I have a service which is checking for new task all the time. If there is new task, I want to refresh the activity UI to show that info.
I did find https://github.com/commonsguy/cw-andtutorials/tree/master/18-LocalService/ this example. Is that a good approch ? Any other examples?
Thanks.
See below for my original answer - that pattern has worked well, but recently I've started using a different approach to Service/Activity communication:
Use a bound service which enables the Activity to get a direct
reference to the Service, thus allowing direct calls on it, rather
than using Intents.
Use RxJava to execute asynchronous operations.
If the Service needs to continue background operations even when no
Activity is running, also start the service from the Application
class so that it does not get stopped when unbound.
The advantages I have found in this approach compared to the startService()/LocalBroadcast technique are
No need for data objects to implement Parcelable - this is particularly important to me as I am now sharing code between Android and iOS (using RoboVM)
RxJava provides canned (and cross-platform) scheduling, and easy composition of sequential asynchronous operations.
This should be more efficient than using a LocalBroadcast, though the overhead of using RxJava may outweigh that.
Some example code. First the service:
public class AndroidBmService extends Service implements BmService {
private static final int PRESSURE_RATE = 500000; // microseconds between pressure updates
private SensorManager sensorManager;
private SensorEventListener pressureListener;
private ObservableEmitter<Float> pressureObserver;
private Observable<Float> pressureObservable;
public class LocalBinder extends Binder {
public AndroidBmService getService() {
return AndroidBmService.this;
}
}
private IBinder binder = new LocalBinder();
#Nullable
#Override
public IBinder onBind(Intent intent) {
logMsg("Service bound");
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
if(pressureSensor != null)
sensorManager.registerListener(pressureListener = new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent event) {
if(pressureObserver != null) {
float lastPressure = event.values[0];
float lastPressureAltitude = (float)((1 - Math.pow(lastPressure / 1013.25, 0.190284)) * 145366.45);
pressureObserver.onNext(lastPressureAltitude);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}, pressureSensor, PRESSURE_RATE);
}
#Override
public Observable<Float> observePressure() {
if(pressureObservable == null) {
pressureObservable = Observable.create(emitter -> pressureObserver = emitter);
pressureObservable = pressureObservable.share();
}
return pressureObservable;
}
#Override
public void onDestroy() {
if(pressureListener != null)
sensorManager.unregisterListener(pressureListener);
}
}
And an Activity that binds to the service and receives pressure altitude updates:
public class TestActivity extends AppCompatActivity {
private ContentTestBinding binding;
private ServiceConnection serviceConnection;
private AndroidBmService service;
private Disposable disposable;
#Override
protected void onDestroy() {
if(disposable != null)
disposable.dispose();
unbindService(serviceConnection);
super.onDestroy();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.content_test);
serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
logMsg("BlueMAX service bound");
service = ((AndroidBmService.LocalBinder)iBinder).getService();
disposable = service.observePressure()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(altitude ->
binding.altitude.setText(
String.format(Locale.US,
"Pressure Altitude %d feet",
altitude.intValue())));
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
logMsg("Service disconnected");
}
};
bindService(new Intent(
this, AndroidBmService.class),
serviceConnection, BIND_AUTO_CREATE);
}
}
The layout for this Activity is:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.controlj.mfgtest.TestActivity">
<TextView
tools:text="Pressure"
android:id="#+id/altitude"
android:gravity="center_horizontal"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
If the service needs to run in the background without a bound Activity it can be started from the Application class as well in OnCreate() using Context#startService().
My Original Answer (from 2013):
In your service: (using COPA as service in example below).
Use a LocalBroadCastManager. In your service's onCreate, set up the broadcaster:
broadcaster = LocalBroadcastManager.getInstance(this);
When you want to notify the UI of something:
static final public String COPA_RESULT = "com.controlj.copame.backend.COPAService.REQUEST_PROCESSED";
static final public String COPA_MESSAGE = "com.controlj.copame.backend.COPAService.COPA_MSG";
public void sendResult(String message) {
Intent intent = new Intent(COPA_RESULT);
if(message != null)
intent.putExtra(COPA_MESSAGE, message);
broadcaster.sendBroadcast(intent);
}
In your Activity:
Create a listener on onCreate:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.copa);
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String s = intent.getStringExtra(COPAService.COPA_MESSAGE);
// do something here.
}
};
}
and register it in onStart:
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((receiver),
new IntentFilter(COPAService.COPA_RESULT)
);
}
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
super.onStop();
}
for me the simplest solution was to send a broadcast, in the activity oncreate i registered and defined the broadcast like this (updateUIReciver is defined as a class instance) :
IntentFilter filter = new IntentFilter();
filter.addAction("com.hello.action");
updateUIReciver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//UI update here
}
};
registerReceiver(updateUIReciver,filter);
And from the service you send the intent like this:
Intent local = new Intent();
local.setAction("com.hello.action");
this.sendBroadcast(local);
don't forget to unregister the recover in the activity on destroy :
unregisterReceiver(updateUIReciver);
I would use a bound service to do that and communicate with it by implementing a listener in my activity. So if your app implements myServiceListener, you can register it as a listener in your service after you have bound with it, call listener.onUpdateUI from your bound service and update your UI in there!
I would recommend checking out Otto, an EventBus tailored specifically to Android. Your Activity/UI can listen to events posted on the Bus from the Service, and decouple itself from the backend.
Clyde's solution works, but it is a broadcast, which I am pretty sure will be less efficient than calling a method directly. I could be mistaken, but I think the broadcasts are meant more for inter-application communication.
I'm assuming you already know how to bind a service with an Activity.
I do something sort of like the code below to handle this kind of problem:
class MyService extends Service {
MyFragment mMyFragment = null;
MyFragment mMyOtherFragment = null;
private void networkLoop() {
...
//received new data for list.
if(myFragment != null)
myFragment.updateList();
}
...
//received new data for textView
if(myFragment !=null)
myFragment.updateText();
...
//received new data for textView
if(myOtherFragment !=null)
myOtherFragment.updateSomething();
...
}
}
class MyFragment extends Fragment {
public void onResume() {
super.onResume()
//Assuming your activity bound to your service
getActivity().mMyService.mMyFragment=this;
}
public void onPause() {
super.onPause()
//Assuming your activity bound to your service
getActivity().mMyService.mMyFragment=null;
}
public void updateList() {
runOnUiThread(new Runnable() {
public void run() {
//Update the list.
}
});
}
public void updateText() {
//as above
}
}
class MyOtherFragment extends Fragment {
public void onResume() {
super.onResume()
//Assuming your activity bound to your service
getActivity().mMyService.mMyOtherFragment=this;
}
public void onPause() {
super.onPause()
//Assuming your activity bound to your service
getActivity().mMyService.mMyOtherFragment=null;
}
public void updateSomething() {//etc... }
}
I left out bits for thread safety, which is essential. Make sure to use locks or something like that when checking and using or changing the fragment references on the service.
Callback from service to activity to update UI.
ResultReceiver receiver = new ResultReceiver(new Handler()) {
protected void onReceiveResult(int resultCode, Bundle resultData) {
//process results or update UI
}
}
Intent instructionServiceIntent = new Intent(context, InstructionService.class);
instructionServiceIntent.putExtra("receiver", receiver);
context.startService(instructionServiceIntent);
My solution might not be the cleanest but it should work with no problems.
The logic is simply to create a static variable to store your data on the Service and update your view each second on your Activity.
Let's say that you have a String on your Service that you want to send it to a TextView on your Activity. It should look like this
Your Service:
public class TestService extends Service {
public static String myString = "";
// Do some stuff with myString
Your Activty:
public class TestActivity extends Activity {
TextView tv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tv = new TextView(this);
setContentView(tv);
update();
Thread t = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
runOnUiThread(new Runnable() {
#Override
public void run() {
update();
}
});
}
} catch (InterruptedException ignored) {}
}
};
t.start();
startService(new Intent(this, TestService.class));
}
private void update() {
// update your interface here
tv.setText(TestService.myString);
}
}
You could use android Jetpack's LiveData
As per the documentation:
You can extend a LiveData object using the singleton pattern to wrap
system services so that they can be shared in your app. The LiveData
object connects to the system service once, and then any observer that
needs the resource can just watch the LiveData object. For more
information, see Extend
LiveData.
Below is what I did for communication between Service and Activity and also Service and Fragment.
In this example, I have:
a class SyncLogLiveData extending LiveData that contains a SpannableStringBuilder
a service SyncService
a fragment SyncFragment
The Fragment "observes" the LiveData (ie SyncLogLiveData) and performs an action when the LiveData changes.
The LiveData is updated by the Service.
I could also update the LiveData from the Fragment in the same way but don't show it here.
class SyncLogLiveData
public class SyncLogLiveData extends LiveData<SpannableStringBuilder> {
private static SyncLogLiveData sInstance;
private final static SpannableStringBuilder log = new SpannableStringBuilder("");
#MainThread
public static SyncLogLiveData get() {
if (sInstance == null) {
sInstance = new SyncLogLiveData();
}
return sInstance;
}
private SyncLogLiveData() {
}
public void appendLog(String text) {
log.append(text);
postValue(log);
}
public void appendLog(Spanned text) {
log.append(text);
postValue(log);
}
}
in class SyncService
This line of code will update the content of the LiveData
SyncLogLiveData.get().appendLog(message);
You could also make direct use of setValue(...) or postValue(...) methods of LiveData
SyncLogLiveData.get().setValue(message);
class SyncFragment
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
//...
// Create the observer which updates the UI.
final Observer<SpannableStringBuilder> ETAObserver = new Observer<SpannableStringBuilder>() {
#Override
public void onChanged(#Nullable final SpannableStringBuilder spannableLog) {
// Update the UI, in this case, a TextView.
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
textViewLog.setText(spannableLog);
}
});
}
};
// Observe the LiveData, passing in this activity/fragment as the LifecycleOwner and the observer.
SyncLogLiveData.get().observe(getViewLifecycleOwner(), ETAObserver);
//...
}
From within an activity it works the same way, but for .observe(...), you may use this instead
SyncLogLiveData.get().observe(this, ETAObserver);
You could also fetch the current value of the LiveData this way at anytime in your code.
SyncLogLiveData.get().getValue();
Hopefully this will help someone. There wasn't any mention of LiveData in this answer yet.

Categories

Resources