I’m working on a media player app that will play music in the background using a service, but I am confused about all the different ways to communicate with the service. I want the service to tell the activity when it is playing so the button can change to pause. I also want a seek bar so I will need information about what is currently playing in the service.
I have been looking at different examples for a while now and it seems like everyone of them is doing it a different way. Some are binding the activity to the service, some just use global broadcasts, and the android music player is using aidl.
As far as a media player goes, which method should I be using? Is one going to allow me to do stuff that won't work on the other methods? What did using the aidl allow them to do?
I've had roughly the same questions over the last several months. It's not a real easy question to answer because there are several variables that go into the decision. Likely, you'll end up using a combination of the options.
Q: Is the communication one way? Just from the service to the activity?
A: If so, then the LocalBroadcastManager is your friend. I've found this to be very useful and one of the easiest ways to communicate from service to activity.
Q: Do you expect other applications to receive your broadcasts?
A: If so, then you'll not be able to use the LocalBroadcastManager. You'll have to use global ones with Context.sendBroadcast() instead.
Q: Are you expecting to communicate back to the service from your activity?
A: This one is tricky. If you just need to tell the service something and don't expect anything in return then you can just use intents. But, if you need information back from the service then you'll definitely want to look into binding to that service.
I've found that when binding to your service I sometimes find myself playing a waiting game while the activity binds to the service. So, this can be a little difficult when time is important. You'll have to work to ensure that you've bound to the service well before you expect to communicate with it.
Given your example and assuming that your not expecting other applications to receive your broadcasts you could do something like the following within your media player service.
Intent broadcastIntent = new Intent(PlayerService.AUDIO_PLAYING);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
Then, within your player activity you need to register as a listener for this intent. Maybe within your onCreate() or onStart(). This can vary depending on your application.
IntentFilter broadcastsToListenFor = new IntentFilter();
broadcastsToListenFor.addAction(PlayerService.AUDIO_PLAYING);
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, broadcastsToListenFor);
Your broadcastReceiver would look something like this...
// Set up broadcast receiver
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public synchronized void onReceive(Context context, Intent intent) {
if(action.equals(PlayerService.AUDIO_PLAYING)) {
// Set your play button image to pause here
}
}
};
Finally, make sure that you unregister your activity from receiving these broadcasts in the onStop() or onPause() method. Again, depending on your specific scenario...
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
We've done something very similar to this recently and it's working pretty well. I really think it's worth it to test and play around with each of the options because you'll probably find a situation where each of them is the right choice.
Related
Intents are taking too long to be process in my app. Is there a better way I can tell different elements of my app that something has happened? For example I use:
Intent i = new Intent("com.ftx.player_died");
I listen for that intent on two different places in my app. I would not like to make the same call twice.
Inventory.playerDied(true);
NotificationBar.playerDied(true);
Doesn't scale nicely.
Is there something I can use that is faster than intents but that I don't make same call twice or three times?
Using LocalBroadcastManager orEventBus is not only faster the normal BroadcastManager, but also is private to your app (that means other application cant listen to your Intent action)
It depends on a lot of factors. It is needed to see of your project to give a piece of advice.
But in some cases it is possible to use usual interface that declared and shared in the static Application class.
This question already has answers here:
What is an Intent in Android?
(14 answers)
Closed 9 years ago.
I still don't understand what is so special about Intents. Why not just use a new thread or just call the function? I think I got the whole idea about intents wrong. A simple code showing why Intents are better or when are needed would be great!
Intents are get widely used in android to switch from one activity to other . it is good practice to use intents . Using intents we can pass/send values from one activity to another. So it can be used as value passing mechanism. Also its syntax is very simple.so why to think about threads ?
Intents are asynchronous messages which allow application components to request functionality from other Android components. Intents allow you to interact with components from the own and other applications. For example an activity can start an external activity for taking a picture.
Intents are objects of the android.content.Intent type. Your code can send them to the Android system defining the components you are targeting. For example via the startActivity() method you can define that the intent should be used to start an activity.
An intent can contain data via a Bundle. This data can be used by the receiving component.
To start an activity use the method startActivity(intent). This method is defined on the Context object which Activity extends.
The following code demonstrates how you can start another activity via an intent.
# Start the activity connect to the
# specified class
Intent i = new Intent(this, ActivityTwo.class);
startActivity(i);
Why not just use a new thread or just call the function?
No matter what Thread you use, there would still need to be a mechanism to direct the message which is what an Intent does. It is a way to send a message. Now, it needs to be called on the UI Thread or have an appropriate Context passed because it needs this to send the message. Call what function? You are. It calls a constructor of the Intent class.
A simple code showing why Intents are better or when are needed would be great!
I don't have a simple code to compare to because I'm not sure what you want to see. No one is saying that it is better than something else. As to why Intents are used opposed to something else? I don't know...you would have to ask the developers of the Android platform. That is what they decided to use.
When they are needed is when you want to pass a message from one Activity to another or from one application to another. From the docs
An intent is an abstract description of an operation to be performed
I said "message" but the docs say "description of an operation to be performed" (I guess can mean the same thing). You can use them to start an Activity, pass data between Activities, and more such as telling the OS what to do at boot time. Why is it better? Better than what? That is what the developers decided to use so I guess you would have to ask them but maybe they didn't think it was better rather different.
while I don't have an example, Intents encourage coupling components loosely.
It negates the necessity of you building your own Observer design patterns and enables Inter/Intra-Application communication.
I'm working with the sample media player given by the android sdk. MainActivity starts Service MusicService with startService(new Intent(MusicService.ACTION_PLAY)).
I need to find a view by ID inside the Service but I don't know how to do it:
findViewById(R.id.playbutton).setVisibility(View.GONE);
I've found some similar questions but none provide a simple solution (the most similar question's accepted answer is actually "no you can't" and I'm sure it's possible). How can I make this line work inside the Service? Do I have to pass the context from MainActivity to it, how do I do it?
Since the service handles media playback the interface should be updated directly before playing/pausing that's why I need to update the ui from it
No, you do not. You need to let the UI know, if it exists, about the state change. The UI will affect its own changes how it sees fit. There may not be any UI at all, depending upon what the user has done.
For letting any affected bits of UI know about the state change, you can:
send a regular broadcast Intent, or
use LocalBroadcastManager to send a "narrowcast" Intent (works a lot like a broadcast, but it is completely within your process), or
use Otto as an event bus
The RemoteControlClient was introduced in ICS. That's the way the lock screen seems to be integrating with various music players. See the screenshot below for an example of Spotify on the lock screen.
Could one from another app than the lock screen integrate with said players as well?
I find the documentation lacking a bit on the subject, but I think the results, if it's possible, could be interesting.
Edit:
Progress so far: none. What I've found out is that IRemoteControlDisplay likely has some part in it, but my Android/Java skills are a bit lacking to actually understand how to implement it and achieve the same functionality as on the lock screen.
While working on my app I've actually found how to implement your own RemoteControlDisplay.
Basically, you extend IRemoteControlDisplay$Stub, which sends messages to special handler, this handler updates metadata and thing. Then you register your own class extended from IRemoteControlDisplay$Stub by calling to AudioManager#registerRemoteControlDisplay().
And then you unregister it by calling AudioManager#unregisterRemoteControlDisplay().
It's fairly complex, but I've wrote an article on how to this.
I've published it on XDA, check it here:
http://forum.xda-developers.com/showthread.php?p=44513199
I believe you can do this. However, the method will use private API (the implication is that it may not work on some later version of Android OS).
I recommend to go and download Android source code (http://source.android.com/) and check directory /frameworks/base/media/java/android/media
It has couple of files which are points of your interest:
AudioManager.java
AudioService.java
IRemoteControlClient.aidl
IRemoteControlDisplay.aidl
Audio manager has public method, which isn't documented called registerRemoteControlDisplay. You should be able to access it through reflection.
Using this interface you can register an object which implements IRemoteControlDisplay (another undocumented interface) and you should be able to control player through this object.
Try this
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
Bitmap AlbumArt=BitmapFactory.decodeResource(getResources(), R.drawable.alislahthumbmain);
mIslahReceiverComponent=new ComponentName(this,AlIslahReceiver.class.getName());
audioManager.registerMediaButtonEventReceiver(mIslahReceiverComponent);
Intent mediaButtonIntent=new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(mIslahReceiverComponent);
PendingIntent mediaPendingIntent=PendingIntent.getBroadcast(getApplicationContext(),
0,mediaButtonIntent,0);
RemoteControlClient mRemoteControlClient=new RemoteControlClient(mediaPendingIntent);
mRemoteControlClient.editMetadata(true)
.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,AlIslahApplication.getStreamTitle())
.putBitmap(100,AlbumArt)
.apply();
mRemoteControlClient.setPlaybackState(
RemoteControlClient.PLAYSTATE_PLAYING);
mRemoteControlClient.setTransportControlFlags(
RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE|
RemoteControlClient.FLAG_KEY_MEDIA_STOP);
audioManager.registerRemoteControlClient(mRemoteControlClient);
You can't get the same requests to show the display that the lock screen does, but you can certainly trigger the same events that the buttons on this screen do with Broadcast Intents.
The action in question is ACTION_MEDIA_BUTTON and you should attach a KeyEvent with the appropriate keyCode to do what you want.
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(
KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
sendBroadcast(intent);
This will do the same thing as pressing the play/pause button on the lockscreen. You can do this with the other KeyEvent keycodes that make sense (KEYCODE_MEDIA_NEXT, etc...), although you won't know what the currently playing track has registered itself as supporting, while the lockscreen does.
For anyone stumbling on this question post KitKat release, you can now use the RemoteController, which connects to RemoteControlClients and allows you to control them.
You would have to implement the IRemoteControl*.aidl interface in your app and the apps (like Spotify) would have to register itself to your app, which is not the case yet.
They register themself to the AudioManager. So NO, you're not able to catch those RemoteClient registrations without either modifying the apps (Spotify, etc..) or modifying the Android system so your app could grab the RemoteClients and their data.
I have two classes, one is a standard activity, the other a service. I would like the service to update a TextView (via .setText()) of the Activity on certain events that happen within the service.
I've tried to achieve this by programming something like a setter-method inside the Activity-Class, but TextViews don't accept a static-reference, and when I try invoking an instance of the Activity-class instead (via MyActivityClassName aVariable = new MyActivityClassName();), I get a NullPointer Exception, even though the View in questions is visible at the time of the call.
Could anyone tell me what I'm doing wrong :-)? It is probably more of a basic Java question than an Android-one, but since it might have to do with the nature of Android-services, I've still added the android-tag.
Thanks for your help!
I would like the service to update a
TextView (via .setText) of the
Activity on certain events that happen
within the service.
I would strongly recommend greater logical decoupling. Have the Service tell the Activity to update the TextView.
You can do that via a broadcast Intent, or by having the Activity provide a listener object that the Service calls. In either case, be sure the Activity detaches from the service (unregisters the listener or broadcast Intent receiver), lest you cause a memory leak.
And, of course, this only works if the activity is actually running.