I am programming a Music Player and the Music plays in a background Service
. When the user kills the Activity
which hosts 3 Fragments
, and then restarts the Activity
again, I send a Broadcast
from the Service
that contains information about the current playing song, and the list of songs that the user added to his session.
The problem is, every time I want to set the last information into the Fragments
nothing happens because their creation takes too long, and the Broadcast
doesn't get handled like they should.
How can I let the Service
or the Broadcast
wait until the Fragments are created so they are handled appropriately?
Here are the relevant code snippets:
//When the activity binds to the service, refresh the fragments
private ServiceConnection conn = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
myService = binder.getService();
myService.setBound(true);
if(myService.startedOnce) {
myService.refreshFragments();
}
myService.startedOnce = true;
}
}
//This is the broadcast receiver of the Fragment
//it receives the broadcast too soon, and I can't set the
//Views so they are always empty.
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MusicService.REFRESH_ALL)) {
Song current = intent.getParcelableExtra("song");
setCurrentSong(current);
}
}
These files contain only the onCreateView() method to inflate the UI of the fragment and returns the root of the fragment layout. If the fragment does not have any UI, it will return null.
Traditionally, a Fragment instance could only be instantiated using its default empty constructor. This is because the system would need to reinitialize it under certain circumstances like configuration changes and the app's process recreation.
The Fragment class has two callback methods, onAttach() and onDetach() , that you can override to perform work when either of these events occur.
A Fragment represents a reusable portion of your app's User Interface. Retained Fragment consists of the configuration change that causes the underlying Activity to be destroyed. The term "retained" refers to the fragment that will not be destroyed on configuration changes.
The easiest thing to do would simply be hold on to the information until the Fragment is ready to display it. Use the Fragment's setArguments()
method to attach the information into the Fragment.
@Override
public void onReceive() {
String action = intent.getAction();
if(action.equals(MusicService.REFRESH_ALL)) {
// Creating a new Bundle so the Fragment can control its own object
Bundle args = new Bundle(intent.getExtras());
Fragment fr = getUsingFragment();
fr.setArguments(fr);
}
}
Then, in the Fragment's onCreateView()
simply pull the arguments from getArguments()
and build the view with the values.
@Override
public void onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle args = getArguments();
if(args != null) {
// code to set values if arguments are set
} else {
// code to set values if arguments are not set
}
}
Another way to do it would be to use setter methods in which the Fragment itself puts values into a Bundle for setArguments()
. That way, you can update the views whenever the View has been created on top of setting the arguments for the possible event when the Fragment's View is destroyed and must be recreated.
Note: You can only call setArguments()
before the Fragment has been attached to the Activity. You can however update the Bundle that you pass in by setArguments
by retrieving a reference to it from getArguments()
, then simply putting in the values. So instead of calling setArguments()
from your receiver, do something like this:
public void setCurrentSong(Song extra) {
Bundle args = getArguments();
args.putParcable(KEY_MAP, extra);
if(/* Views are created */) {
// update and invalidate the views
}
}
As I was using a Service for Media Playback, I wanted to bring up last listened songs from the service so I could directly play it. This was old logic, but I actually built my code around it. Until thusfar I bumped into it.
FragmentActivity
is createdService
gets started and bound toFragments
get created asynchronouslyService
starts, it sends out a Broadcast
with latest informationService
and the Fragment
creations are asynchronous, the broadcast would be sent from the service, but because the BroadcastReceivers
in the Fragments
weren't even initialized yet, they would not receive the Intent
.I somehow had to use a callback that made sure that
So I used the ServiceConnection
and to be precise, the onServiceConnected()
method. There I got the preferences in which the last song was saved, and then send out the Broadcast
and the Fragments
received it and the Views
were appropiately set. This also worked for orientation changes.
//This is the code in the FragmentActivity
private ServiceConnection conn = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
myService = binder.getService();
myService.setBound(true);
if (myService.startedOnce) {
myService.refreshFragments();
} else {
sendLastSavedSong();
}
myService.startedOnce = true;
}
public void onServiceDisconnected(ComponentName className) {
myService.setBound(false);
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With