The question:
I have seen many posts in SO regarding this but could not get the exact and most easy way to call an activity method from service class. Is broadcast receiver only the option? No easy way out ? I just need to call the following method in Activity class after the media player is prepared in Service class .
Activity class:
public void updateProgress() {
// set Progress bar values
songProgressBar.setProgress(0);
songProgressBar.setMax(100);
// Updating progress bar
updateProgressBar();
}
Service class:
@Override
public IBinder onBind(Intent intent) {
Log.d(this.getClass().getName(), "BIND");
return musicBind;
}
@Override
public boolean onUnbind(Intent intent) {
return false;
}
@Override
public void onPrepared(MediaPlayer mp) {
try {
mp.start();
} catch (IllegalStateException e) {
e.printStackTrace();
}
// updateProgress();// Need to call the Activity method here
}
The Solutions:
Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.
Method 1
Define an interface your Service will use to communicate events:
public interface ServiceCallbacks {
void doSomething();
}
Write your Service class. Your Activity will bind to this service, so follow the sample shown here. In addition, we will add a method to set the ServiceCallbacks
.
public class MyService extends Service {
// Binder given to clients
private final IBinder binder = new LocalBinder();
// Registered callbacks
private ServiceCallbacks serviceCallbacks;
// Class used for the client Binder.
public class LocalBinder extends Binder {
MyService getService() {
// Return this instance of MyService so clients can call public methods
return MyService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public void setCallbacks(ServiceCallbacks callbacks) {
serviceCallbacks = callbacks;
}
}
Write your Activity class following the same guide, but also make it implement your ServiceCallbacks interface. When you bind/unbind from the Service, you will register/unregister it by calling setCallbacks
on the Service.
public class MyActivity extends Activity implements ServiceCallbacks {
private MyService myService;
private boolean bound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(...);
}
@Override
protected void onStart() {
super.onStart();
// bind to Service
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from service
if (bound) {
myService.setCallbacks(null); // unregister
unbindService(serviceConnection);
bound = false;
}
}
/** Callbacks for service binding, passed to bindService() */
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// cast the IBinder and get MyService instance
LocalBinder binder = (LocalBinder) service;
myService = binder.getService();
bound = true;
myService.setCallbacks(MyActivity.this); // register
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
bound = false;
}
};
/* Defined by ServiceCallbacks interface */
@Override
public void doSomething() {
...
}
}
Now when your service wants to communicate back to the activity, just call one of the interface methods from earlier. Inside your service:
if (serviceCallbacks != null) {
serviceCallbacks.doSomething();
}
Method 2
Use Broadcast receiver with service for updating your view from the service class.
For example:
-
In my activity class
public class ServiceDemoActivity extends Activity { Intent intent; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final TextView notification = (TextView) findViewById(R.id.notification); if (CheckIfServiceIsRunning()) { } else { startService(new Intent(this, MyService.class)); } } private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateDate(intent); } }; private void updateDate(Intent intent) { String time = intent.getStringExtra("time"); Toast.makeText(getApplicationContext(), "Yea!!! Service called", Toast.LENGTH_SHORT).show(); TextView date = (TextView) findViewById(R.id.date); date.setText(time); } @Override public void onResume() { super.onResume(); registerReceiver(broadcastReceiver, new IntentFilter( MyService.BROADCAST_ACTION)); } }
And in my service class I am calling my update ui after a few interval of time which updates my UI.
public class MyService extends Service { public static final String BROADCAST_ACTION = "com.mukesh.service"; private final Handler handler = new Handler(); @Override public void onCreate() { intent = new Intent(BROADCAST_ACTION); } @Override public void onDestroy() { stopService(intent); } @Override public void onStart(Intent intent, int startid) { int i = 0; while (i <= 2) { if (i > 1) { i++; this.onDestroy(); } else { counter = i; i++; handler.removeCallbacks(sendUpdatesToUI); handler.postDelayed(sendUpdatesToUI, 1 * 1000); // 1 sec } } } private Runnable sendUpdatesToUI = new Runnable() { public void run() { DisplayLoggingInfo(); handler.postDelayed(this, 7 * 1000); // 7 sec } }; private void DisplayLoggingInfo() { intent.putExtra("time", new Date().toLocaleString()); intent.putExtra("counter", String.valueOf(counter)); sendBroadcast(intent); stopService(intent); } }
For complete code check this link
Method 3
I created a general class called Delegate (it’s not a special name, you can name it John) and passed MainActivity class into it as a static field. Then I can access it from the service since its global now. I am not sure if it is cost-effective but it solved the problem for me simple.
My service:
package com.some.package;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class FirebaseInstanceIDService extends FirebaseInstanceIdService {
@Override
public void onTokenRefresh() {
String token = FirebaseInstanceId.getInstance().getToken();
Delegate.theMainActivity.onDeviceTokenChange(token);
}
}
Delegate class:
package com.some.package;
public class Delegate {
static MainActivity theMainActivity;
}
What I did in MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Delegate.theMainActivity = this;
//rest of the code...
}
public void onDeviceTokenChange(String token){
Log.e("updated token:", token);
}
Method 4
You can’t call your sevices method direcly from your activity or vise versa. There are 3 ways to communicate with a service; using broadcasters and receivers, using Messenger
or binding to the service. For further information look at http://developer.android.com/guide/components/bound-services.html
Method 5
You can call from your service
getContentResolver().notifyChange(uri, null);
and in your activity you set up a
getContentResolver().registerContentObserver(uri, false, new ContentObserver(getHandler())
{
public void onChange(boolean selfChange)
{
updateProgress()
}
};
the onChange method will ba called on the UI thread
Method 6
You can call a method of activity from service by implementing your own listener like this
https://stackoverflow.com/a/18585247/5361964
You might consider running your activity method in runOnUiThread
like this:
// method will be called from service
override fun callback(activity: Activity, result: String) {
runOnUiThread{
Toast.makeText(activity, result, Toast.LENGTH_LONG).show()
}
}
Method 7
I would prefer to use some very easy and cleaner solution provided by
EventBus
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0