Streaming music in android studio - android

I want to stream an mp3 file that has a length of about 1 hour. I want to play the file in background. I saw many tutorial videos. Some of them used service and some used asyntask. I dont know what can i choose between these too. Which is the better one?

You'll definitely want to use a service, and not an AsyncTask. The main reason for this being that, if you want the music to run even when the app has been suspended/put into the background, like when the user moves to another app, only a service will do this. AsyncTasks will not run in the background in that way.
To include some background information about background services, they use events from app contexts such as activities and foreground services to notify them of when to do work. This work is handled via the service's onStartCommand() function. More can be read about services in the Android docs https://developer.android.com/guide/components/services
That being said, a service will allow running in the background, but it can still be preempted if the OS needs to complete another task. Therefore, for the music to play reliably, and restart shortly after the OS has preempted the service for any reason, you will need to specify START_STICKY as the return value from the service's onStartCommand() function. But, like with everything Android, prefer the compatibility version, START_STICKY_COMPATIBILITY, to the not compatible version. START_STICKY/START_STICKY_COMPATIBILITY is appropriate to return in the case of the PLAY command. I.e. if the event the service is receiving is PLAY.
Returning START_STICKY or START_STICKY_COMPATIBILITY from the onStartCommand() in every case will wind you up with a service that never dies, thus consuming processing power and battery life from the phone running it. This could cause processor consumption and a drain on battery. That is why it is important to return START_NOT_STICKY from the onStartCommand if the user is attempting to pause. I.e. if the event the service is receiving is PAUSE.
Here is a stripped down version of what you might want your onStartCommand of your service to look like:
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(ACTION_PLAY)) {
...
return START_STICKY_COMPATIBILITY;
} else { // i.e. action is ACTION_PAUSE
...
return START_NOT_STICKY;
}
}
Edit: To caveot this and the remainder of this answer - In an attempt to simplify the post, I excluded considerations for waiting for the mediaplayer to prepare. As a note, the service will likely also need to handle waiting for the mediaplayer to prepare with either a separate event, or from within the handling of the PLAY event. The could possibly also be handled from within the activity before starting the service but this may be more or less complicated. Explaining the rest of the issues/considerations in this answer is much easier without talking about this aspect of the problem, although it will have to be considered to make a functional music player app.
Provisions for when the device is locked are also required so that some hardware peripherals don't shut off. Consider adding the following in response to the PLAY event in the service's onStartCommand to account for this:
// Setup Wake Mode Wake Lock so the music can play while device screen is locked
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.FULL_WAKE_LOCK);
// Setup wifi lock so wifi can stay on while device is locked and trying to save power
wifiLock = ((WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
Another issue is that your user will ideally be able to kill the service if it is running. If they kill the app, it will not kill the service, as intended, and the music will keep playing. So the user should be able to control the service via a notification with controls to pause and play the music. This can be done using a foreground service. If you want to add the foreground service layer, you can add a call to startForeground() in the onStartCommand() of the service in response to the broadcast event for PLAY. Here is the stripped down onStartCommand() with the foreground logic added:
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(ACTION_PLAY)) {
...
Notification notification = setupNotification(); // where setupNotification is your own function
startForeground(1, notification);
return START_STICKY_COMPATIBILITY;
} else { // i.e. action is ACTION_PAUSE
...
stopForeground(false);
// NotificationManagerCompat needed here for swipeable notification b/c service will be killed
Notification notification = setupNotification();
NotificationManagerCompat nmc = NotificationManagerCompat.from(this);
nmc.notify(1, notification);
return START_NOT_STICKY;
}
}
The startForeground() functions takes an id and a Notification object as its params. The notification can be created with the NotificationCompat.Builder with code that looks something like this (noting that some variables here will need to be subbed out for your respective application):
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher_round_large);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText("Music is now playing") // change to paused on paused
.setTicker("Music Playing")
.setSmallIcon(R.drawable.status_bar_icon_xhdpi_48px)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingTapIntent)
.setDeleteIntent(pendingSwipeIntent)
.addAction(iconId, buttonText, pendingButtonIntent)
.setWhen(System.currentTimeMillis());
Note the pending intents in the code above. These are created with the PendingIntent class.
I.e. create a pending intent for a play button on the notification like so (where "this" is the background service, assuming you are creating this intent from within the background service)
Intent playIntent = new Intent(this, MyService.class);
playIntent.setAction(ACTION_PLAY);
PendingIntent pendingPlayIntent = PendingIntent.getService(this, 0, playIntent, 0);
Likewise, create a pending intent for when the user taps on the notification so that it opens the app with the following code (again, where "this" is the background service, assuming you are creating this intent from within the background service):
Intent notificationIntent = new Intent(this, StreamActivity.class);
notificationIntent.setAction("ACTION_MAIN");
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Furthermore, create the pending intent for swipe action on the notification to kill the background service like this (again, where "this" is the background service, assuming you are creating this intent from within the background service):
Intent swipeIntent = new Intent(this, MyService.class);
swipeIntent.setAction(ACTION_END_SERVICE);
PendingIntent pendingSwipeIntent = PendingIntent.getService(this, 0, swipeIntent, 0);
Hopefully this covers enough to get you going, but I would recommend starting this process without the foreground activity layer of complexity. Then add it once you see the music play from within the app is working.

If you want the music to play even after user exits the app, you need to use a foreground service for this. All music players use this approach.
If you want the music to end when user exists a perticular activity, only in that case would consider anything other than the service.
Async Task should not be used for this purpose as it is ideal for small background tasks. Playing music is not in this category.

Related

How do I keep media player service running until it's killed?

I have a media player service that gets killed everytime the user clears recent apps. I want the service to continue playing on the background. I have tried
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
return START_STICKY;
}
and
mediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
but it's not working. How do I solve this?
Google did some updates:
Some of those updates included security and it reached the services. This means that we can no longer perform lengthy operations in the background without notifying the user.
Foreground
A foreground service performs some operation that is noticeable to the
user. For example, an audio app would use a foreground service to play
an audio track. Foreground services must display a Notification.
Foreground services continue running even when the user isn't
interacting with the app.
Background
A background service performs an operation that isn't directly noticed
by the user. For example, if an app used a service to compact its
storage, that would usually be a background service. Note: If your app
targets API level 26 or higher, the system imposes restrictions on
running background services when the app itself isn't in the
foreground. In most cases like this, your app should use a scheduled
job instead.
Make sure to call startForeground as soon as possible
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
//do heavy work on a background thread
//stopSelf();
return START_STICKY;
}
This is how you start the foreground service:
public void startService() {
Intent serviceIntent = new Intent(this, ForegroundService.class);
serviceIntent.putExtra("inputExtra", "Foreground Service Example in Android");
ContextCompat.startForegroundService(this, serviceIntent);
}
As specified in Mr.Patel answer,
Many of the manufactures wont allow running of background service when frontend Activity is not running or removed from the recent list.
There is a way of achiving your requirement.
You can run your service in the background by setting a non cancellable notification from your application. Till you close your notification forcefully programatically using a close button, your service will be running in the background.
Hope this will solve your problem.

Play an audio file every X seconds Y times even when changing apps

My app is supposed to receive time in seconds that determines how audio is played.
Example: Play audio file x(length about 2 seconds) every 2 minutes all in all 16 times.
It would work great if android didn't enjoy closing foreign apps and services so frequently, once you move to another app.
My structure is like this:
MainActivity starts a new MainActivityService via intent.
gintent = new Intent(MainActivity.this,MainActivityService.class);
gintent.putExtra(...);
gintent.putExtra(...);
...
startService(gintent);
Then this service runs the mentioned time being given to it by putExtra.
The point is, android has all kinds of mechanisms to kill services and apps. So I have been trying to keep it up by using onSaveInstanceState in MainActivity
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean(...);
savedInstanceState.putInt(...);
...
}
onRestoreInstanceState, isMyServiceRunning(MainActivityService.class) and so forth, but I lost track with all the onResume and whatnot. And in the end, the service was still killed.
I don't want to debug this back and forth. I want to know the gold standard how to do this.
Android treats nonforegrounded services as ripe for killing. You'll need to make your Service flag itself as a foreground service. This will lower the chance the OS will kill that process.
A foreground service is a service that the user is actively aware of and is not a candidate for the system to kill when low on memory.... To request that your service run in the foreground, call startForeground().
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification =
new Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
.setContentTitle(getText(R.string.notification_title))
.setContentText(getText(R.string.notification_message))
.setSmallIcon(R.drawable.icon)
.setContentIntent(pendingIntent)
.setTicker(getText(R.string.ticker_text))
.build();
startForeground(ONGOING_NOTIFICATION_ID, notification);
See: Running a service in the foreground

Foreground service gets killed on performing internet related operations

UPDATE: Previously I couldn't find a well defined pattern as to when my foreground service was being killed. After more debugging with the devices (doesn't happen on all) on which this was happening I found.
1.) A lot of times when I open chrome to load a website the foreground service gets killed. Sometimes even when I am using whatsapp this happens.
2.) There are no exceptions and the stacktrace doesn't show anything useful.
Original Question below:
There are many such questions on StackOverflow but the answers so far that I have read mostly say that it is upto Android and we don't have 100% guarantee that a foreground service will not be killed. Some answers suggest START_STICKY but that is not much helpful in my case.
In my case I have a music player app which has a foreground service. This service gets killed on certain devices, mostly some versions of Xiomi (Android version was 5.1.1). Now I understand that android might be short on memory and so my foreground service is being killed, but then why do other music player apps never go through such termination. What is it that they are doing right that I am not?
I made my service foreground service by using startForeground. Also I return START_STICKY in onStartCommand although that doesn't help because the service is restarted after a period of 4-5 sec if killed. To bind my service with my activity I use
bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT );
So what exactly can I improve/change in my app to prevent this from happening, if other apps are working right there must be something that is wrong in my case. Can someone please help. Thanks in advance !!
Edit:
This is how I call startForeground()
public void sendNotification() {
Intent notIntent = new Intent(this, MainActivity.class);
notIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendInt = PendingIntent.getActivity(this, 0,
notIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Bitmap bitmap = null;
if (!notificationShowing || !forwarded) {
Log.i(TAG, "present");
String title = CommonUtils.getSongFromID(songIndex, this);
bigView.setTextViewText(R.id.title, title);
bigView.setImageViewBitmap(R.id.img, bitmap);
smallView.setTextViewText(R.id.title1, title);
smallView.setImageViewBitmap(R.id.img1, bitmap);
if (pauseButton == 1) {
bigView.setImageViewResource(R.id.pause, R.drawable.pause_noti);
smallView.setImageViewResource(R.id.pause1, R.drawable.pause_noti);
} else {
bigView.setImageViewResource(R.id.pause, R.drawable.play_noti);
smallView.setImageViewResource(R.id.pause1, R.drawable.play_noti);
}
musicNotification = builder.setContentIntent(pendInt)
.setSmallIcon(R.drawable.logo1)
.setTicker(songTitle)
.setOngoing(true)
.setContentTitle("Playing")
.setStyle(new Notification.BigTextStyle().bigText("Song App"))
.setContentText(songTitle)
.setPriority(Notification.PRIORITY_MAX)
.build();
musicNotification.contentView = smallView;
musicNotification.bigContentView = bigView;
musicNotification.contentIntent = pendInt;
Intent switchIntent = new Intent("pause");
switchIntent.putExtra("button", "pause");
PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(this, 100, switchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
bigView.setOnClickPendingIntent(R.id.pause, pendingSwitchIntent);
smallView.setOnClickPendingIntent(R.id.pause1, pendingSwitchIntent);
Intent switchIntent1 = new Intent("forward");
switchIntent1.putExtra("button", "forward");
PendingIntent pendingSwitchIntent2 = PendingIntent.getBroadcast(this, 100, switchIntent1, PendingIntent.FLAG_UPDATE_CURRENT);
bigView.setOnClickPendingIntent(R.id.forward, pendingSwitchIntent2);
smallView.setOnClickPendingIntent(R.id.forward1, pendingSwitchIntent2);
Intent switchIntent2 = new Intent("previous");
switchIntent2.putExtra("button", "previous");
PendingIntent pendingSwitchIntent3 = PendingIntent.getBroadcast(this, 100, switchIntent2, PendingIntent.FLAG_UPDATE_CURRENT);
bigView.setOnClickPendingIntent(R.id.previous, pendingSwitchIntent3);
smallView.setOnClickPendingIntent(R.id.previous1, pendingSwitchIntent3);
Intent switchIntent3 = new Intent("end");
switchIntent3.putExtra("button", "end");
PendingIntent pendingSwitchIntent4 = PendingIntent.getBroadcast(this, 100, switchIntent3, PendingIntent.FLAG_UPDATE_CURRENT);
bigView.setOnClickPendingIntent(R.id.end, pendingSwitchIntent4);
smallView.setOnClickPendingIntent(R.id.end1, pendingSwitchIntent4);
startForeground(NOTIFY_ID, musicNotification);
notificationShowing = true;
}
forwarded = false;
}
This happened in Xiomi phone due to below reason.
Solution for MIUI 7.0 => Security => Autostart => select Apps that you want to run in background => Reboot
After reboot your device should able to run your application services in background like other android devices do.
MIUI 4.0 settings
MIUI AutoStart Detailed Description
And if you looking for other phone then check here is service structure.It automatically restart but when you restart phone call BootReceiver.
public class AppService extends Service {
private class LocalBinder extends Binder {
public AppService getServerInstance() {
return AppService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// If we get killed, after returning from here, restart
return Service.START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
}
}
Thanks hope this will help you.
This service gets killed on certain devices, mostly some versions of
Xiomi (Android version was 5.1.1)
Not sure about this, but as per my understanding this might be because of
Bug in os customization from the vendor.
Bugs in Android with respect to prioritizing foreground services, that are triggered by various combinations of service binding flags(i.e. BIND_AUTO_CREATE, BIND_IMPORTANT etc).Read this answer by Robin Davies.
I don't know whether you use startService() or not. But if you don't then as per this documentation:
You can create a service that is both started and bound. That is, the
service can be started by calling startService(), which allows the
service to run indefinitely, and also allow a client to bind to the
service by calling bindService().(This is called Binding to a Started Service)
If you do allow your service to be started and bound, then when the
service has been started, the system does not destroy the service when
all clients unbind. Instead, you must explicitly stop the service, by
calling stopSelf() or stopService().
Although you should usually implement either onBind() or
onStartCommand(), it's sometimes necessary to implement both. For
example, a music player might find it useful to allow its service to
run indefinitely and also provide binding. This way, an activity can
start the service to play some music and the music continues to play
even if the user leaves the application. Then, when the user returns
to the application, the activity can bind to the service to regain
control of playback.
Be sure to read the section about Managing the Lifecycle of a Bound Service, for more information about the service lifecycle when adding binding to a started service.
onStartCommand will be called in case of started service so START_STICKY will work in case of startService() only.
Update on process logs
Proc # 5: prcp F/S/IF trm: 0 22407:com.wave.music.player/u0a2
(fg-service)
In your process log your player service running in foreground with adj setting prcp (visible foreground service) which means it's virtually indestructible. Still your service destroyed by OS than there might be very low memory available to run newly launch app. As per this documentation,
There will only ever be a few foreground processes in the system, and these
will only be killed as a last resort if memory is so low that not even
these processes can continue to run. Generally, at this point, the
device has reached a memory paging state, so this action is required
in order to keep the user interface responsive.
So I think you are doing nothing wrong. I just want to suggest you to read this official Android developer documentation and try to run your service in separate process(Documentation suggests this approach for music player app). Be careful to implement this as it can easily increase—rather than decrease—your RAM footprint if done incorrectly.

Service stops when application is closed

I need a service to run in background and calculate every minute distance between two locations. I used Thread in order to execute a method every minute, then I understood that when the application is closed, the service stops too since application and service use the same thread.
How can i create a simple method that is invoked every 1 minute, in background even when the application is closed?
You can run the Service in a separate process by modifying the manifest:
<service
android:name="com.example.myapplication.MyBackgroundService"
android:exported="false"
android:process=":myBackgroundServiceProcess" >
</service>
But that might not really bring any benefit. And most of the time it may even be a bad idea.
Of course the main thing is that if the Service gets shut down it's then re-started.
The Service's onStartCommand() can return the START_STICKY flag:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Other code goes here...
return START_STICKY;
}
This (and the other) options are explained in the documentation. Basically START_STICKY means "Hey Android! If you really really have to shut down my precious Service because of running low on memory, then please please try to start it again."
Whereas START_NOT_STICKY would mean "Nahh...don't bother. I'll call startService() again myself if I really need my Service running."
This (start sticky) is probably fine most of time. Your Service will just start from scratch again. You could try if that's suitable for your use case.
Then there are "foreground services" which are less likely to get shut down by Android as they are treated more like visible apps. And in fact they show up in the notification drawer with an icon and (if you make it so) a status text. So they are visible to the user like e.g. SportsTracker, Beddit and such apps.
This involves modifying your Service's onStartCommand():
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Tapping the notification will open the specified Activity.
Intent activityIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// This always shows up in the notifications area when this Service is running.
// TODO: String localization
Notification not = new Notification.Builder(this).
setContentTitle(getText(R.string.app_name)).
setContentInfo("Doing stuff in the background...").setSmallIcon(R.mipmap.ic_launcher).
setContentIntent(pendingIntent).build();
startForeground(1, not);
// Other code goes here...
return super.onStartCommand(intent, flags, startId);
}
The Service is started as usual and you can get out of the foreground mode with:
myBackgroundService.stopForeground(true);
The boolean parameters defines if the notification should also be dismissed or not.
You have to use thread for this and set a flag on starting the service. And check that flag for stoping the service.
In addition to the previous solutions provided by our friends...
MAKE SURE YOU CHECK THE "Allow background activity" BUTTON IN YOUR APP'S SETTINGS!
See the picture: In the Battery section

android long running service without notification

I see this example with programs like Advance Task killer, Watch Dog, eBay, battery widgets, ect. There is a background service running that monitors device activity, a broadcastreceiver, but there is an option to disable the notification icon displayed. Currently my application works flawlessly by calling:
Context context = getApplicationContext();
Intent intent = new Intent(context, SomeBackGroundService.class);
context.startService(intent)
and then in my service I am calling:
startForeground(NOTIFICATION_ID, notification);
Now how is it that these other applications can have a long running broadcastreceiver running without a notification icon? What do I need to do in order to disable/set ect as to hide the icon and add this feature to my application?
Thanks in advance
I'm not expert on this, but I disagree with the accepted answer.
Your startForeground is key to ensuring that your service continues running.
Without that, it might keep running, but it has a much lower priority so it is much more likely to be killed by the system.
Your service is entirely independent of whether or not a notification exists for it. If you removed the line, startForeground, the service would continue to run. The only time a service stops is if you call context.stopService() or stopSelf(). Throw some logs into your service, and you'll see that it continues to run.
Override the onStartCommand to determine behavior of your service after garbage collection:
#Override
public int onStartCommand(Intent intent, int flags, int startId() {
super.onStartCommand(intent, flags, startId);
doWork();
return START_STICKY;
}
START_STICKY recreates your service after garbage collection. START_REDELIVER_INTENT will recreate your service after garbage collection using the original intent, use this only if you need extra intent data.
Create a notification that contains an icon made with a png and is only 1 pixel in height and width and make that pixel transparent.

Categories

Resources