Android service binding - android

I have Problem understanding the Binding to a service
I've created a simple song player, with a service to play the song.
I've created a log to track the binding and unbinding.
the problem is when i exit the app Unbinding log appears but when i go back to the app there is no binding log message ,i'm able to control play and pause of the song though
public class PlayService extends Service {
MediaPlayer mPlayer;
public IBinder mBinder=new LocalBinder();
#Override
public void onCreate() {
super.onCreate();
Log.i("TAG","Create");
mPlayer=MediaPlayer.create(this,R.raw.askme);
}
//since we want the song to be played in the background then we need to start the service
#Override
public int onStartCommand(Intent intent,int flags, int startId) {
Log.i("TAG","Start");
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
stopSelf();
}
});
return Service.START_NOT_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.i("TAG","Bind");
return mBinder;
}
#Override
public boolean onUnbind(Intent intent) {
Log.i("TAG","Unbind");
return super.onUnbind(intent);
}
#Override
public void onRebind(Intent intent) {
Log.i("TAG","ReBind");
super.onRebind(intent);
}
#Override
public void onDestroy() {
Log.i("TAG","Destroy");
mPlayer.release();
super.onDestroy();
}
//here are the play and pause methods frm the user
public void playSong(){
mPlayer.start();
}
public void pauseSong(){
mPlayer.pause();
}
public boolean isPlaying(){
return mPlayer.isPlaying();
}
//since Binder already Extends IBinder so
//we can create LocalBinder Class which extends IBinder Interface
public class LocalBinder extends Binder{
//this method for returning an instance of our service in the MainActivity
public PlayService getService(){
return PlayService.this;
}
}
}
and main Activity as following
public class MainActivity extends AppCompatActivity {
public static final String TAG ="TAG" ;
public PlayService mPlayService;
private Button mDownloadButton;
private Button mSongButton;
private boolean mBound=false;
private ServiceConnection mServiceConnection=new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//the iBinder object is the returned value from onBind() method in our service
mBound=true;
//we need to get an instance of our service PlayerService so we can play or Pause the song
PlayService.LocalBinder binder = (PlayService.LocalBinder) iBinder;
//and here finally we are getting an instance of our service
mPlayService= binder.getService();
if (mPlayService.isPlaying()){
mSongButton.setText("Pause");
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mBound=false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDownloadButton=(Button) findViewById(R.id.download_button);
mSongButton=(Button) findViewById(R.id.song_button);
mDownloadButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(MainActivity.this,"downloading",Toast.LENGTH_LONG).show();
for (String song:Playlist.songs){
Intent intent=new Intent(MainActivity.this,StartDownloadService.class);
intent.putExtra(TAG,song);
startService(intent);
}
}
});
mSongButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//here we can play or Pause the song
//but first we need to know if we are already bound to the service
if (mBound){
if (mPlayService.isPlaying()){
mPlayService.pauseSong();
mSongButton.setText("Play");
}else {
//but here we need the service to be started and keep playing in the background
//even if we unbound from the service when we exit the App
Intent intent=new Intent(MainActivity.this,PlayService.class);
startService(intent);
mPlayService.playSong();
mSongButton.setText("Pause");
}
}
}
});
}
#Override
protected void onResume() {
Intent intent=new Intent(this,PlayService.class);
bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
super.onResume();
}
#Override
protected void onStop() {
super.onStop();
if (mBound){
unbindService(mServiceConnection);
mBound=false;
}
}
}
if someone can explain

You are starting your service twice in your code.
Once as a started service and then as a bind service. Remove the started service code and most probably it should work as intended.
Remove this from your code
Intent intent=new Intent(MainActivity.this,PlayService.class);
startService(intent);
mPlayService.playSong();
mSongButton.setText("Pause");
as you are binding service in on Resume() method.
Intent intent=new Intent(this,PlayService.class);
bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);

You should start and bind your music service like this:
#Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart invoked !");
// Start/Bind to play back service
Intent serviceIntent = new Intent(this, PlayService.class);
if (isMyServiceRunning(PlayService.class)
&& mBound== false) {
this.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
} else {
this.startService(serviceIntent);
this.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
Log.i(TAG, "Media Player service is created new -------------------");
}
}
#Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop invoked !");
if(mBound) {
Log.i(TAG, "unbinding service !");
unbindService(mServiceConnection);
mBound= false;
}
}
To Check service is running or not:
/**
* Utility function to check if a service is running.
*/
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Hope this will help you !

I found the Reason
the system calls your service's onBind() method to retrieve the IBinder only when the first client binds. The system then delivers the same IBinder to any additional clients that bind, without calling onBind() again.
that's why i'm not getting the Log message from onBind() and onUnbind() method, but I've used a log message in onServiceConnected() and i got it when i ran the App again.

Related

Resume music using Service when we Resume the application

I'm developing an application, which plays music in the background by using service.
Music stops when we hit back app will be paused and but, music is not resuming when I get back to the application.
public class backService extends Service implements ComponentCallbacks2 {
private MediaPlayer mp;
SharedPreferences sharedpreferences;
public Boolean musicSwitch;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if (mp != null){
mp.stop();
mp.release();
}
}
#Override
public void onCreate() {
super.onCreate();
sharedpreferences = getSharedPreferences(mypreference,
Context.MODE_PRIVATE);
musicSwitch = sharedpreferences.getBoolean("music", true);
if(musicSwitch){
mp = MediaPlayer.create(this, R.raw.all);
mp.setLooping(true);
mp.start();
}
}
#Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
if(mp != null){
mp.pause();
}
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I want the application to resume music when we get back to the application, I have tried using onResume method, but there is no onResume method in services.
TIA
1) You need to create foreground service to prevent it from killing by OS
How can we prevent a Service from being killed by OS?
2) You can bind service (bindService(serviceIntent)) and use Binder interface
https://developer.android.com/guide/components/bound-services.html#Binder
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
Activity:
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(mConnection);
mBound = false;
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
}
}
3) Then, in your activity onResume you can call method from your Binder to control music

Stop service and close app

My app use a background music service.
I have a button to quit my app but I can't find anything to close my app and my service.
I bind my service to my activity.
I tried:
unbindService(serviceConnection);
myService().stopSelf();
stopService(new Intent(this, MediaPlayer.class));
and absolutely nothing works !!! The service continues.
How can I do to destroy my service and how can I do to close my app ??
Tx
EDIT:
I use this in the onCreate method
Intent intent = new Intent(this, serviceClass);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
And in the MediaPlayer class
public class LocalBinder extends Binder {
public MediaPlayer getService() {
return MediaPlayer.this;
}
}
public IBinder onBind(Intent intent) {
Log.i(TAG, "service bound");
init();
return mBinder;
}
And that...
But I dont know if I really need to start the service. Bind the service already starts it
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
Now I did this
#Override
public void onDestroy() {
player.stop();
super.onDestroy();
}
The onDestroy method works only if i unbind the service !
This doesnt work at all:
getService().stopSelf();
this.stopService(new Intent(this, MediaPlayer.class));
So, how can I stop the service and how can I close the app ?
This is what I do in my app. onDestroy() method from the activity will be called when you close your app.
private ServiceConnection musicServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.LocalBinder binder = (MusicService.LocalBinder) service;
musicService = binder.getService();
musicService.setCallbacks(MainActivity.this);
musicServiceBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "MusicService service disconnected (unbinded)");
musicServiceBound = false;
}
};
#Override
protected void onStart() {
super.onStart();
Intent intent1 = new Intent(this, MusicService.class);
bindService(intent1, musicServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onDestroy() {
super.onDestroy()
if(musicServiceBound){
musicService.stopSelf();
unbindService(musicServiceConnection);
}
}
You wrote myService(), where you are creating another service using ().
For closing your app programmatically you can refer to this question.

Accessing a variable of a Service

I have an Android Activity called Main that calls a Service called MainService as follows:
Intent intent = new Intent(this, MainService.class);
if(MainService.getInstance() == null){
Log.d(TAG, "Calling MainService");
startService(intent);
}
MainService maintains a variable during its lifetime that I wish to access in Main later on. How do I do this?
Thanks.
You can bind the service and can have the service instance forever. Below sample code will help you:-
Service Class
public class MusicService extends Service {
MyBinder binder=new MyBinder();
MusicService services;
static Context context;
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return binder;
}
#Override
public void onCreate() {
super.onCreate();
context=getApplicationContext();
MediaPlayer mPlayer = MediaPlayer.create(getApplicationContext(), R.raw.yaar);
mPlayer.start();
}
public class MyBinder extends Binder
{
public MusicService getServiceSystem()
{
return MusicService.this;
}
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
Activity
public class MainActivity extends AppCompatActivity {
MusicService services;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ServiceConnection connection=new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MyBinder binderr=(MusicService.MyBinder)service;
services=binderr.getServiceSystem();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent intent= new Intent(this, MusicService.class);
startService(intent);
}
}
You can then use service anywhere you need in activity. Hope it helps.
Yes, you can access variables inside service, but for that you have to bind to this service first. After that, use accessors for getting or setting variables or call any other method of the service.
See https://developer.android.com/guide/components/bound-services.html

My background music service stops when I load a new activity. Am I connecting or binding wrong?

I'm trying to add background music to my game and I thought I could persist it across activities by starting the service and then just connecting and binding to it in different activities in order to control it. But my music stops playing when I try to establish a connection from my second activity. I'm very new to working with services, so I apologize for any simple mistakes.
From my MusicService class (extends Service)
private final IBinder mBinder = new ServiceBinder();
MediaPlayer mPlayer;
private int length = 0;
public static boolean STOP_ON_DESTROY = true;
// Binder subclass. Allows access from clients to the server
public class ServiceBinder extends Binder {
MusicService getService(){
return MusicService.this;
}
}
public MusicService() {}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onCreate(){
super.onCreate();
}
#Override
public int onStartCommand (Intent intent, int flags, int startId)
{
return START_STICKY;
}
public boolean isPlaying(){
if(mPlayer != null){
return mPlayer.isPlaying();
}
return false;
}
public void stopMusic()
{
if(mPlayer != null){
if(mPlayer.isPlaying()) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
System.out.println("stopMusic service fn");
}
}
This is the code I call in both my Main and secondary activities in order to interact with the service. The music stops during the connectToMusicService function in the secondary activity. The Main activity works great.
onCreate(){....
startMusicService();
MusicService.STOP_ON_DESTROY = true;
}
private void startMusicService() {
Intent musicIntent = new Intent();
musicIntent.setClass(getApplicationContext(), MusicService.class);
startService(musicIntent);
}
#Override
public void onStart(){
super.onStart();
// establish connection for binding to the service
connectToMusicService();
// bind to the service
bindToMusicService();
}
private void bindToMusicService() {
Intent intent = new Intent(this, MusicService.class);
bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);
MusicService.STOP_ON_DESTROY = true;
}
private void connectToMusicService() {
myServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.ServiceBinder binder = (MusicService.ServiceBinder) service;
mService = binder.getService();
if(!mService.isPlaying())
mService.startMusic();
isServiceBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("Service disconnected from main");
isServiceBound = false;
}
};
}
The only thing I do during my Main activity's onStop is
#Override
public void onStop(){
super.onStop();
if(mService != null) {
if (MusicService.STOP_ON_DESTROY) {
mService.stopMusic();
}
}
}
UPDATE: I got it working. My issue wasn't with service binding at all. It was with static STOP_ON_DESTROY variable I was using to manage whether the music should stop when leaving an activity. I cleaned that up and all is good now. Thanks!
First of all, do you need to bind at all? Or could starting the service be enough? Started services run until you stop them (except if killed by the system when resources are scarce). I am not sure there's any point binding from each of your activities.
Btw if your service should run and play music also when your activities are closed, consider making it a foreground service.

Calling activity class method from Service class

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
}
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();
}
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
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);
}
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
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
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()
}
}
I would prefer to use some very easy and cleaner solution provided by
EventBus

Categories

Resources