I am working on background service where i am updating one Arraylist in the service, from the Arraylist i am getting the index and playing the songs.
So what's the issue now is if i'll Intent to another activity and inside that activity i am updating the Arraylist to the service but when i am finishing the activity and coming back to MainActivity it's not updating the Arraylist again.
I am using realm arraylist for storing the data.
#Override
protected void onStart() {
super.onStart();
songConnection();
if (playIntent == null) {
playIntent = new Intent(this, MusicService.class);
bindService(playIntent, musicServiceConn, Context.BIND_AUTO_CREATE);
startService(playIntent);
}
}
#Override
protected void onResume() {
super.onResume();
songConnection();
}
public void songConnection() {
musicServiceConn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
musicSrv = binder.getService();
musicSrv.setList(musicRealmResults);
musicBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};
}
Here the musicRealmResults is the Realm Arraylist.
In Albums.java
musicRealmResults = realm.where(Music.class).equalTo("albums", albums).findAllSorted("albums", Sort.ASCENDING);
In MainActivity.java
musicRealmResults = realm.where(Music.class).findAll();
So in both activity the Arraylist size will be different and as per the size it should update to the Service.
So please tell me why it's not updating in onResume when the activity finish.
Please kindly go through my post and suggest me some solution.
change your code like this:
#Override
protected void onStart() {
songConnection();// call this method before calling super
super.onResume();
}
public void songConnection() {
musicServiceConn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
musicSrv = binder.getService();
musicSrv.setList(musicRealmResults);
musicBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};
if (playIntent == null) {
playIntent = new Intent(this, MusicService.class);
bindService(playIntent, musicServiceConn, Context.BIND_AUTO_CREATE);
startService(playIntent);
}
}
now your your service when will called correctly But make sure you stop the Service at some point of time before it'
s started again. ThankYou I hope this was helpful.
Related
I am currently working on a Pedometer application. At first, I started with one activity, the PedometerActivity. This activity started the service that is supposed to run in the background, and binds to it. The code is long so I'll just give sections of what I think will help in my question.
//Bind service
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
//binder to communicate with the service
PedometerService.PedometerBinder mBinder = (PedometerService.PedometerBinder)service;
mPedometerService = mBinder.getService();
mPedometerService.registerCallback(mCallback);
}
public void onServiceDisconnected(ComponentName className) {
mPedometerService = null;
}
};
private void startPedometerService() {
if (!isPedometerService) {
Log.v(TAG, "Start service");
isPedometerService = true;
//start service
Intent intent = new Intent(getApplicationContext(),
PedometerService.class);
startService(intent);
}
}
//Bind to the service
private void bindPedometerService() {
Log.i(TAG, "Bind service");
Intent intent = new Intent(PedometerActivity.this, PedometerService.class);
bindService(intent, mServiceConnection,
Context.BIND_AUTO_CREATE + Context.BIND_DEBUG_UNBIND);
}
//close connection with service
private void unbindPedometerService() {
Log.i(TAG, "Unbind service");
unbindService(mServiceConnection);
}
//Stop the service that had been started
private void stopPedometerService() {
Log.i(TAG, "Stop service");
if (mPedometerService != null) {
//stop service
Intent intent = new Intent(PedometerActivity.this, PedometerService.class);
stopService(intent);
isPedometerService = false;
}
}
#Override
public void onResume() {
Log.i(TAG, "onResume");
super.onResume();
startPedometerService();
bindPedometerService();
}
#Override
public void onStop() {
super.onStop();
Log.i(TAG, "onStop");
stopPedometerService();
}
public void onDestroy() {
super.onDestroy();
unbindPedometerService();
stopPedometerService();
}
In the service class that extends Service
/*Local service binding*/
public class PedometerBinder extends Binder {
public PedometerService getService() {
return PedometerService.this;
}
}
/*A client is binding to the service with bindService()
* Returns the IBinder object received in
* ServiceConnection.onServiceConnected(ComponentName,IBinder)*/
#Override
public IBinder onBind(Intent intent) {
return new PedometerBinder();
}
I then modified my application to have tablayout with 3 tabs hence 3 fragments. I pasted the code from PedometerActivity into PedometerFragment and modified it
//Bind service
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
//binder to communicate with the service
PedometerService.PedometerBinder mBinder = (PedometerService.PedometerBinder)service;
mPedometerService = mBinder.getService();
mPedometerService.registerCallback(mCallback);
}
public void onServiceDisconnected(ComponentName className) {
mPedometerService = null;
}
};
private void startPedometerService() {
if (!isPedometerService) {
Log.v(TAG, "Start service");
isPedometerService = true;
//start service
Intent intent = new Intent(getActivity().getApplicationContext(),
PedometerService.class);
getActivity().startService(intent);
}
}
//Bind to the service
private void bindPedometerService() {
Log.i(TAG, "Bind service");
Intent intent = new Intent(getActivity(), PedometerService.class);
getActivity().bindService(intent, mServiceConnection,
Context.BIND_AUTO_CREATE + Context.BIND_DEBUG_UNBIND);
}
//close connection with service
private void unbindPedometerService() {
Log.i(TAG, "Unbind service");
getActivity().unbindService(mServiceConnection);
}
//Stop the service that had been started
private void stopPedometerService() {
Log.i(TAG, "Stop service");
if (mPedometerService != null) {
//stop service
Intent intent = new Intent(getActivity(), PedometerService.class);
getActivity().stopService(intent);
isPedometerService = false;
}
}
#Override
public void onResume() {
Log.i(TAG, "onResume");
super.onResume();
startPedometerService();
bindPedometerService();
}
#Override
public void onStop() {
super.onStop();
Log.i(TAG, "onStop");
stopPedometerService();
}
public void onDestroy() {
super.onDestroy();
unbindPedometerService();
stopPedometerService();
}
The problem is am having trouble keeping the service running when I switch between tabs. I am using FragmentStatePagerAdapter and therefore when I navigate to the last tab, the first fragment (PedometerFragment) is unloaded. I have been able to save other variables in onSaveInstanceState but this does not seem to help since everything is restarted all over again.
You're using startService, so even if your bound components get destroyed, the service shouldn't be stopped. However, you're explicitly calling stopPedometerService() in onStop(), which is called when your fragment is no longer started.
Try simply removing stopPedometerService() from onStop() andonDestroy() in your fragments.
See: https://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html
This version of the pager is more useful when there are a large number
of pages, working more like a list view. When pages are not visible to
the user, their entire fragment may be destroyed, only keeping the
saved state of that fragment.
You need to bind service in the Activity class. Then you can use it from any attached Fragment via interface or public methods.
I'm stuck in a problem with a service binding that is giving me nuts.
I got an activity that is binding a service, and is frequent that the user go in and out of that activity.
The problem comes when the user goes out first time of the activity this one unBinds the service and when is going in again, do not binds again.
The activity calls the binding service this way:
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, CService.class);
intent.putExtra("id_local", (String) getIntent().getExtras().get("id_local"));
intent.putExtra("id_send", (String) getIntent().getExtras().get("id_send"));
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
registerReceiver(uiUpdated, new IntentFilter("SERVER_MESAGE"));
mBound = true;
}
Where the mConnection is defined that way:
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
CService.LocalBinder binder =(CService.LocalBinder) service;
mService = binder.getService();
Log.d("Service", "onServiceConnected");
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.d("Service", "onServiceDisconnected");
mBound = false;
}
};
And in the onStop I unbindService:
#Override
protected void onStop() {
if (mBound) {
Log.d("ActivityStop", "Stoping activity");
unregisterReceiver(uiUpdated);
unbindService(mConnection);
mBound = false;
}
super.onStop();
}
The onBind in the service is that one:
#Override
public IBinder onBind(Intent intent) {
final String id_local = intent.getStringExtra("id_local");
final String id_send = intent.getStringExtra("id_send");
if (!misatgesList.isEmpty()) {
misatgesList.clear();
}
mBackGroundTimer.schedule(new TimerTask() {
#Override
public void run() {
String serverResult = restRecive(id_local, id_send,
misatgesList.size());
if (serverResult != null) {
misatgesList.addAll(procesMisatges(serverResult,
id_local));
Intent i = new Intent("SERVER_MESAGE");
i.putExtra("recive", serverResult);
sendBroadcast(i);
}
}
}, 0, 1000);
return mBinder;
}
And the onUnBind is that one:
#Override
public boolean onUnbind(Intent intent) {
mBackGroundTimer.cancel();
misatgesList.clear();
Log.d("ServiceOnUnBind", "ServiceOnUnBind");
//stopSelf();
return super.onUnbind(intent);
}
So my question would be, how I can bind again the service when the activity goes in again? Or what should I do to keep the binding alive until the user goes in the activity?
I found the solution!
What I meant was how to call again the onBind. That is done using the onRebind, that alows you to call again the onBind.
So, I created the onRebind:
#Override
public void onRebind(Intent intent) {
super.onRebind(intent);
}
Also, for the onRebind to work, you have to turn the return in the onUnbind to true.
#Override
public boolean onUnbind(Intent intent) {
mBackGroundTimer.cancel();
misatgesList.clear();
Log.d("ServiceOnUnBind", "ServiceOnUnBind");
//return super.onUnbind(intent);
return true;
}
For more explanation, check there: Bound Services
Use getApplicationContext() API when binding to your service from Activity as below:
getApplicationContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
getApplicationContext returns the global application context - the difference from other contexts is that for example, an activity context may be destroyed (or otherwise made unavailable) by Android when your activity ends. The Application context remains available all the while your Application object exists (which is not tied to a specific Activity)
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.
I have a glass app using LiveCards. As such, I don't explicitly have running activities, but just a background service that interacts with the LiveCard for information. At some point, I'd like to bring up a voice input. The problem is that the code samples tell you to use startActivityForResult, which isn't something I can do from within a service. So - is there a different way to bring this up, or can I not do this in my current configuration?
I was having this problem too, not with speech input but I needed to run an activity to get the information to display in a "low-frequency rendering" live card without the user bringing up a menu first. I think you could use an activity to get your text input then send it back to the service.
Most of the information on how to bind the service came from http://developer.android.com/guide/components/bound-services.html
MainService
This is the service that is started by "ok glass, ..." The layout just has a single TextView with the id text.
public class MainService extends Service {
private static final String LIVE_CARD_TAG = "my_card";
private final IBinder mBinder = new LocalBinder();
LiveCard mLiveCard;
TimelineManager mTimelineManager;
RemoteViews mViews;
#Override
public void onCreate() {
super.onCreate();
mTimelineManager = TimelineManager.from(this);
}
public class LocalBinder extends Binder {
MainService getService() {
return MainService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public int onStartCommand(Intent intent, int flags, int startId) {
mLiveCard = mTimelineManager.createLiveCard(LIVE_CARD_TAG);
mViews = new RemoteViews(this.getPackageName(),R.layout.activity_main);
mLiveCard.setViews(mViews);
Intent mIntent = new Intent(this, MenuActivity.class);
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mLiveCard.setAction(PendingIntent.getActivity(this, 0, mIntent, 0));
mLiveCard.publish(LiveCard.PublishMode.REVEAL);
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
// run the test activity after the initial text has displayed
Intent testIntent = new Intent(getBaseContext(), TestActivity.class);
testIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(testIntent);
}
}, 3000);
return START_STICKY;
}
public void updateText(String textString) {
mViews.setTextViewText(R.id.text,textString);
mLiveCard.setViews(mViews);
}
#Override
public void onDestroy() {
if (mLiveCard != null && mLiveCard.isPublished()) {
Log.d("debug", "Unpublishing LiveCard");
mLiveCard.unpublish();
mLiveCard = null;
}
super.onDestroy();
}
}
TestActivity
This is run after a delay from the MainService and updates the text on the live card automatically with no user input.
public class TestActivity extends Activity {
MainService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, MainService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
#Override
public void onResume() {
super.onResume();
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
// this crashes if run right away, so give it a little time
mService.updateText("Updated from TestActivity");
finish();
}
}, 500);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
MenuActivity
This is the activity that is set as the pending intent for the live card. It brings up a menu to exit or update the text when the touchpad is tapped.
public class MenuActivity extends Activity {
MainService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, MainService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
#Override
public void onResume() {
super.onResume();
openOptionsMenu();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.stop:
stopService(new Intent(this, MainService.class));
return true;
case R.id.update:
mService.updateText("Updated from MenuActivity");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public void onOptionsMenuClosed(Menu menu) {
finish();
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
Alright, so I've made a Service so I can keep my music player working in all of my activities. At least, that was the plan.
I am able to bind to my service in some of my activities, but in other activities the binding doesn't seem to do anything.
Set up
My service gets started on my MainScreen:
startService(new Intent(this, MusicService.class));
I'm trying to bind to it in a few other activities. The list of activities looks like this:
MainScreen -> FillerActivity -> bind to service -> Works
MainScreen -> AlbumList -> SongList -> SingleSong -> bind to service -> Does not work
What might be the reason for the binding working in the FillerActivity class, but not working in the SingleSong class?
My code is as following:
Filler
public class Filler extends Activity {
private MusicService s;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.filler);
doBindService();
Button b = (Button) findViewById(R.id.terug);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
Intent intent = new Intent();
setResult(RESULT_OK, intent);
finish();
}
});
}
public void startSong(View view){
if(s != null){
s.startSong(s.songPlaying);
System.out.println("Song started");
}
}
public void stopSong(View view){
if(s != null){
s.stopSong();
System.out.println("Song paused");
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
s = ((MusicService.MyBinder) binder).getService();
Toast.makeText(Filler.this, "Connected",
Toast.LENGTH_SHORT).show();
System.out.println("s is gevuld");
}
public void onServiceDisconnected(ComponentName className) {
s = null;
}
};
void doBindService() {
bindService(new Intent(this, MusicService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
}
SingleSong
public class SingleSongInfo extends Activity {
private MediaDetail mDetails;
private TextView textView;
boolean playing = false;
private String songURL;
private MusicService s;
Button b;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.album_song_info);
Bundle bun = getIntent().getExtras();
doBindService();
if (s != null) {
System.out.println("S filled");
} else {
System.out.println("S empty");
}
b = (Button) findViewById(R.id.playButtonSongPage);
mDetails = (MediaDetail) bun.getSerializable("details");
if (mDetails != null) {
System.out.println("Details gevuld");
songURL = mDetails.getSong_url();
setUpTextViews();
setUpImage();
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
}
});
}
}
public void buttonPressed(View view) {
if (s != null) {
if (!s.isPlaying()) {
s.startSong(songURL);
b.setBackgroundResource(R.drawable.button_pause);
} else {
s.stopSong();
b.setBackgroundResource(R.drawable.button_play);
}
} else {
System.out.println("S is leeg");
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
s = ((MusicService.MyBinder) binder).getService();
Toast.makeText(SingleSongInfo.this, "Connected", Toast.LENGTH_SHORT)
.show();
}
public void onServiceDisconnected(ComponentName className) {
s = null;
}
};
void doBindService() {
bindService(new Intent(this, MusicService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
}
Any help is greatly appreciated.
An interesting discovery after doing some more testing.
When I call the SingleSong class directly from the MainScreen, the MediaService object s gets filled and is usable. It even prints a message that it has been filled.
However, when the SingleSong class gets opened through the SongList (which opens the SingleSong class in a tabview with some other classes), the object that not get filled. BUT, and this is where it get's strange, it does not print a message that the object has NOT been filled, even though it should. The code for this can be found below.
What might be causing the code from completely not being run in a tabView?
Code used:
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
s = ((MusicService.MyBinder) binder).getService();
Toast.makeText(SingleSongInfo.this, "Connected", Toast.LENGTH_SHORT)
.show();
if (s != null) {
System.out.println("S filled"); // works when calling class directly
} else {
System.out.println("S empty"); // Does not print when object is empty when class is called through SongList (tabview)
}
}
public void onServiceDisconnected(ComponentName className) {
s = null;
}
};
Managed to fix the problem eventually. It happened because I was using the SingleSong class inside a tabView. Due to this, I had to make a small, yet important adjustment.
private void doBindService() {
getApplicationContext().bindService( // getApplicationContext() fixed the problem here.
new Intent(getApplicationContext(), MusicService.class),
mConnection, Context.BIND_AUTO_CREATE);
}