Should I create a worker thread when extend a Service? - android

I have a service for playing music that extends Service class. It has local MediaPlayer instance and performs music playback without creating working thread. It looks like UI thread is not blocked, I can freely navigate through my app while listening to music. I am a bit confused because on documentation guide it is said that such operation blocks main thread. Could someone explain what`s going on? Should I create working thread inside my service?
A service runs in the same process as the application in which it is
declared and in the main thread of that application, by default. So,
if your service performs intensive or blocking operations while the
user interacts with an activity from the same application, the service
will slow down activity performance. To avoid impacting application
performance, you should start a new thread inside the service.
The snippet looks like this
public class MusicPlayerService extends Service implements MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
...
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
playNext();
}
#Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
mMediaPlayer.reset();
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
#Override
public void onCreate() {
super.onCreate();
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnCompletionListener(this);
startForeground(1, mNotification);
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onDestroy() {
mMediaPlayer.release();
}
public void onPlay() {
mMediaPlayer.start();
}
public void onPause() {
mMediaPlayer.pause();
}
}

https://developer.android.com/guide/components/services.html
As we can see in official document of Android:
Service This is the base class for all services. When you extend this
class, it's important that you create a new thread in which to do all
the service's work, because the service uses your application's main
thread, by default, which could slow the performance of any activity
your application is running.
IntentService This is a subclass of
Service that uses a worker thread to handle all start requests, one at
a time. This is the best option if you don't require that your service
handle multiple requests simultaneously. All you need to do is
implement onHandleIntent(), which receives the intent for each start
request so you can do the background work. The following sections
describe how you can implement your service using either one for these
classes.
If you are doing heavy work in service it's possible to block ui and you should create a thread or use intent service for non-blocking ui.

Related

Do I need to run MediaPlayer in a service in Android Studio?

The following code is from the project .
Maybe it's a long time operation when I use the MediaRecorder control, so the author run MediaRecorder in a service, you can see Code B.
Maybe it's a long time operation to play a audio too, so I think the author should run MediaPlayer in a service, but why doesn't Code A do that?
Code A
public final class MediaPlayerHolder implements PlayerAdapter {
public static final int PLAYBACK_POSITION_REFRESH_INTERVAL_MS = 1000;
private MediaPlayer mMediaPlayer;
#Override
public void play() {
if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
mMediaPlayer.start();
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener.onStateChanged(PlaybackInfoListener.State.PLAYING);
}
startUpdatingCallbackWithPosition();
}
}
...
}
Code B
public class RecordingService extends Service {
public class LocalBinder extends Binder {
public RecordingService getService() {
return RecordingService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return myBinder;
}
public void startRecording(int duration) {
setFileNameAndPath();
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setOutputFile(mFilePath);
mRecorder.setMaxDuration(duration); // set the max duration, after which the Service is stopped
mRecorder.setAudioChannels(1);
mRecorder.setAudioSamplingRate(44100);
mRecorder.setAudioEncodingBitRate(192000);
...
}
}
The purpose of using a Service is to have your code run on the background, you can do operations that don't require a user interface and even have your code run beyond the onDestoy() method of an activity. This is how music players allow you to listen to music even after you have closed the Application's Activity.
There are 3 types of services:
Foreground Service
Background Service
Bound Service
Why is Code B in a Service but Code A is not?
From the Bound services overview:
A bound service is the server in a client-server interface. It allows
components (such as activities) to bind to the service, send requests,
receive responses, and perform interprocess communication (IPC). A
bound service typically lives only while it serves another application
component and does not run in the background indefinitely.
In other words it allows communication with other applications or across separate processes. And that's the main reason the author would use the Service. It has nothing to do with performance.
About performance:
Code B does not account for performance.
From the Service overview:
Caution: A service runs in the main thread of its hosting process; the
service does not create its own thread and does not run in a separate
process unless you specify otherwise. You should run any blocking
operations on a separate thread within the service to avoid
Application Not Responding (ANR) errors.
So merely using a Service does not guarantee performance. In Code B we have the method startRecording(), which initializes MediaRecorder and sets some paramaters for the Recording. This doesn't not mean that this method will run as soon as the service starts. The Author has used a Bound Service, you can tell by the method:
#Override
public IBinder onBind(Intent intent) {
return myBinder;
}
Which means that any component that binds to it (such as an activity or another process can call its method startRecording() ). Please check the Bound Service link for more info. In the project this gets called from RecordViewModel.startRecording().
If you are worried about performance. Code B should start a new Thread inside startRecording(int duration). There are many ways to do it. Here is one:
public void startRecording(int duration) {
( new Thread( new Runnable() {
#Override
public void run() {
setFileNameAndPath();
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setOutputFile(mFilePath);
mRecorder.setMaxDuration(duration); // set the max duration, after which the Service is stopped
mRecorder.setAudioChannels(1);
mRecorder.setAudioSamplingRate(44100);
mRecorder.setAudioEncodingBitRate(192000);
...
}
}) ).start();
}
As for Code A the only thing happening there is the call to MediaPlayer.start(), which already starts a new thread internally.
From the MediaPlayer class source code:
public void start() throws IllegalStateException {
//FIXME use lambda to pass startImpl to superclass
final int delay = getStartDelayMs();
if (delay == 0) {
startImpl();
} else {
new Thread() {
public void run() {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
baseSetStartDelayMs(0);
try {
startImpl();
} catch (IllegalStateException e) {
// fail silently for a state exception when it is happening after
// a delayed start, as the player state could have changed between the
// call to start() and the execution of startImpl()
}
}
}.start();
}
}
If performance is of concern with Code A, then MediaPlayerHolder.loadMedia(String path) is where a separate Thread should be used.
So to answer your question. No, you do not need to run MediaPlayer in a Service. It depends on your requirements.
Regards

The difference between Bound Services and Threads

I'm studying Android services because I heard that this is the android recommendation to perform background tasks.
I want to interact with that service, so this is the reason that I've chosen Bound Services (btw, I don't want to run that service indefinitely).
The question is: Why should I complicate myself using the IBinder interface and call my methods via this callback see official docs
#Override
public void onServiceConnected(ComponentName className, IBinder service){}
when I can simply create my custom service? For example, if I want to play some background music in a single Activity I can create this custom (and simple) service:
public class MyOwnService {
MediaPlayer mp;
MainActivity ma;
public MyOwnService(MainActivity mainActivity) {
mp = MediaPlayer.create(mainActivity, R.raw.badinerie);
ma = mainActivity;
}
public void play(){
new Thread(new Runnable() {
#Override
public void run() {
mp = MediaPlayer.create(ma, R.raw.badinerie);
mp.start();
}
}).start();
}
public void pause(){
mp.pause();
}
public void stop(){
mp.stop();
mp.release();
}
}
and call my service in a simplest way like this
public class MainActivity extends AppCompatActivity {
MyOwnService myOwnService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myOwnService = new MyOwnService(this);
}
public void play(View view) {
myOwnService.play();
}
public void pause(View view) {
myOwnService.pause();
}
public void stop(View view) {
myOwnService.stop();
}
}
You can create threads and other objects as you need. But your solution has one big issue - Android OS is not aware about your "service". Remember, android can terminate your app process at any time. Suppose your activity goes into background, from the perspective of android os your app process is a good candidate for killing - it only has one not active activity.
Think of the service like a notification to os that your app is doing something important in the background.
If on the other hand your background work should only happen as long as your activity is active you do not need a service. Just to avoid confusion do not use name service in classes that are not really android services.
EDIT: also note that you can start a service manually and then later bind to it. If you have started bound service manually then it will continue running until you stop it.

How to make WakefulService (IntentService) wait until MediaPlayer finishes?

I'm trying to make an app, which plays series of sounds using MediaPlayer, at scheduled times. To properly handle the wake lock and schedule the playback I used CommonsWare's WakefulIntentService.
Unfortunately, the IntentService's worker thread quits right after I call MediaPlayer.play() and neither MediaPlayer registered listeners are called. Instead, the exception is logged:
W/MessageQueue(6727): Handler (android.media.MediaPlayer$EventHandler) {4160d820} sending message to a Handler on a dead thread
W/MessageQueue(6727): java.lang.RuntimeException: Handler (android.media.MediaPlayer$EventHandler) {4160d820} sending message to a Handler on a dead thread
W/MessageQueue(6727): at android.os.MessageQueue.enqueueMessage(MessageQueue.java:294)
W/MessageQueue(6727): at android.os.Handler.sendMessageAtTime(Handler.java:473)
W/MessageQueue(6727): at android.os.Handler.sendMessageDelayed(Handler.java:446)
W/MessageQueue(6727): at android.os.Handler.sendMessage(Handler.java:383)
W/MessageQueue(6727): at android.media.MediaPlayer.postEventFromNative(MediaPlayer.java:2063)
W/MessageQueue(6727): at dalvik.system.NativeStart.run(Native Method)
As far as I understand, it is caused by the worker thread being already dead when MediaPlayer completes. If I pause the thread by means of the debugger and let the player complete, everything works fine.
In my listeners I not only release MediaPlayer's resources, but also use OnCompletionListener to do consecutive MediaPlayer.play() calls until the sound queue is empty.
I tried putting a wait loop right after the initial play() call, checking for a custom completion flag, but it seems to freeze because MediaPlayer's callbacks are called on the same thread play() was called.
The question is, how can I make the worker thread not quit before I let it do so (i.e. the has been processed and the onCompletion() method has been called for the last time?
Here is the code of my service:
public class SoundService extends WakefulIntentService {
private static final TAG = "SoundService";
private final Queue<SoundDescriptor> soundQueue = new ConcurrentLinkedQueue<SoundDescriptor>();
private final OnCompletionListener onComediaPlayerletionListener = new OnComediaPlayerletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.reset();
try {
if (!playNextFromQueue(mediaPlayer)) {
Log.v(TAG, "Reached end of queue. Cleaning up.");
release();
}
} catch (Exception e) {
Log.e(TAG, "Exception!", e)
release();
}
}
};
private final OnErrorListener onErrorListener = new OnErrorListener() {
#Override
public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
Log.v(TAG, "Error!!");
release();
return false;
}
};
public SoundService() {
// populate soundQueue
}
protected void doWakefulWork(Intent intent) {
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setOnComediaPlayerletionListener(onComediaPlayerletionListener);
mediaPlayer.setOnErrorListener(onErrorListener);
playNextFromQueue(mediaPlayer);
}
private boolean playNextFromQueue(MediaPlayer mediaPlayer) throws IllegalArgumentException, IllegalStateException, IOException {
SoundDescriptor descriptor = soundQueue.poll();
if (descriptor != null) {
mediaPlayer.setDataSource(descriptor.getFileDescriptor(), descriptor.getStartOffset(), descriptor.getLength());
descriptor.close();
mediaPlayer.prepare();
mediaPlayer.start();
return true;
} else {
return false;
}
}
}
To properly handle the wake lock and schedule the playback I used CommonsWare's WakefulIntentService.
That's not an appropriate choice.
Unfortunately, the IntentService's worker thread quits right after I call MediaPlayer.play() and neither MediaPlayer registered listeners are called.
That's why it's not an appropriate choice. :-)
The question is, how can I make the worker thread not quit before I let it do so (i.e. the has been processed and the onCompletion() method has been called for the last time?
Don't use WakefulIntentService. Don't use IntentService. Use Service. Manage the WakeLock yourself, and call stopSelf() on the service when the audio is finished.
The problem with MediaPlayer is that it caches a Handler to the current thread when it is instantiated. In case the current thread has no Looper, it defaults to the main thread. It will use this handler to execute the listeners callbacks.
Lets say you instantiate the player and use it inside a service's thread. If you call the player methods through the service, and then this service thread dies, the player will try to call the callbacks to the registered listeners using a handler pointing to the deceased thread, and it will throw the exception shown in the question.
I've find MediaPlayer very tricky and unreliable. The simplest solution is to use the player in the main thread. They are short calls, shouldn't raise ANR's.

How to make Handler accessible around the app

There are a few threads running in a service.
The threads need to post messages to UI / Activity
How would I pass over the Handler reference to the threads ? so that they can post their state changes to Activity ?
Or Better yet is there a way to globally expose handler ref like this ?
Handler getUIHandler();
Thank you in advance ;)
Create a Handler object in your UI thread. You can just create it at instantiation time if you like. Any thread launched from your activity can post messages or runnables to that handler. Threads launched from other activities, services, or whatnot will not work because there's no guarantee that your Activity is even running. (Actually, it might be a fun experiment to see if it works when the Activity is running, but you could never base a real app on this technique.)
In fact, you don't even need to create a Handler. Every View object contains its own Handler, so you can simply post your runnables to a view.
Or you could just call runOnUiThread()
From my notes on Handlers:
Usage patterns:
Pattern 1, handler plus runnables:
// Main thread
private Handler handler = new Handler()
...
// Some other thread
handler.post(new Runnable() {
public void run() {
Log.d(TAG, "this is being run in the main thread");
}
});
Pattern 2, handler plus messages:
// Main thread
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
Log.d(TAG, "dealing with message: " + msg.what);
}
};
...
// Some other thread
Message msg = handler.obtainMessage(what);
handler.sendMessage(msg);
Pattern 3, call runOnUiThread():
// Some other thread
runOnUiThread(new Runnable() { // Only available in Activity
public void run() {
// perform action in ui thread
}
});
Pattern 4, use the built-in handler of a View:
// Some other thread
myView.post(new Runnable() {
public void run() {
// perform action in ui thread, presumably involving this view
}
});
I've answered a similar question on how to report back to activity an error in the service.
Check Best practice for error handling in an Android Service, that will give you the aproach as well as a code example that you can use.
Regards.
OK, maybe we should get back to the base issue. Are you trying to make UI updates in your activity from the service? I see two approaches to this.
First, your service could send special Intents back up to the activity. Declare the activity with a launch mode of "singleTask" and implement onNewIntent() to receive intents from the service. Then, pack any relevant information into the intent and send it to the activity to be handled.
The better way, but somewhat more complicated, would be to bind the service from the activity, and then they can easily communicate with each other over the binder. If the service and activity are both part of the same application, and both running in the same process, this becomes much simpler.
Again, from my notes:
Declare an inner class named e.g. "LocalBinder" which extends Binder and contains a method named e.g. getService() which returns the instance of the service:
public class MyService extends Service
{
public class LocalBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
private final IBinder binder = new LocalBinder();
public IBinder onBind(Intent intent) {
return binder;
}
}
Your activity contains code that looks like:
// Subclass of ServiceConnection used to manage connect/disconnect
class MyConnection extends ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder svc) {
myService = ((MyService.LocalBinder)svc).getService();
// we are now connected
}
public void onServiceDisconnected(ComponentName name) {
// we are now disconnected
myService = null;
}
}
private MyService myService;
private MyConnection connection = new MyConnection();
/**
* Bind to the service
*/
void doBind() {
bindService(new Intent(MyClient.this, MyService.class),
connection, Context.BIND_AUTO_CREATE);
}
/**
* Unbind from the service
*/
void doUnbind() {
if (connection != null) {
unbindService(connection);
}
}

Android service does not work independently [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Android Service makes Activity not responding
I use service to synchronize data between database and view. But the service does not work properly, whenever I use service to work long task, the view stop response (I can't do any event in UI) and I have to wait the service has done. Here is my service:
public class SyncService extends Service{
private static final String TAG = "SyncService";
#Override
public void onCreate()
{
super.onCreate();
}
#Override
public void onDestroy()
{
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent)
{
Log.d(TAG, "call onBind");
return new DataManagerBinder();
}
private class DataManagerBinder extends Binder implements IUserDataManager
{
#Override
public void doProcess(Activity mView)
{
//do some long task (not touch UI thread)
// this will cause the view not response
syncDB();
// update view after process completed
mView.updateViewOnComplete();
}
}
I try to bind this service in client activity
//the interface to handle binder
IUserDataManager viewManager = null;
ServiceConnection serviceConnection = new ServiceConnection()
{
#Override
public void onServiceDisconnected(ComponentName name)
{
Log.i(TAG, "connection closed unexpectedly");
viewManager = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder binder)
{
Log.d(TAG, "serviceConnection onServiceConnected");
viewManager = (IUserDataManager) binder;
viewManager.doProcess(MyActivity.this);
}
};
Intent intent = new Intent(MyActivity.this, SyncService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Please give me the solution for that. Thanks in advance!
While others have given some responses, I don't know if the main point has been emphasized:
While a Service sounds like something that would automatically run in the background, it does not. It's merely a piece of code that can react to intents without maintaining a UI. However, the UI thread still does the processing for the Service.
By contrast, it looks like what you want, is a Service that sits in the background and does some work in a background thread. You will use the Service class to spawn a new thread, this is typically done on response to some intent, which you can define (usually in your onStart() or something similar). You will probably start a new thread which actually does the work updating the database, etc..., and use your main Service to coordinate to that thread.
As it looks like you also want to communicate with the service, you will have to implement an appropriate Messenger and Handler pair to keep track of messages you pass between the UI and the Service (coordinating a background thread), and also (possibly) some way (also perhaps a messenger) of coordinating between the Service and the background thread.
As others have noted, you can also use AsyncTask to do things on the UI thread and use a background thread "seamlessly."
Read up on the docs regarding services, specifically the "What is a Service?" paragraph. The service runs on the main UI thread. Take a look at AsyncTask, should solve your issue. The work here is done in a background thread, and the results are sent back to the UI thread.
Android closes service after sometime to save resources.
However you can prevent this from happening using something like
int onStartCommand(Intent, int, int){
return START_STICKY;
}
ref this

Categories

Resources