How to prevent mediaplayer to stop when screen goes off?
Asked Answered
N

2

8

I have a mediaplayer in a Music class that is called from another secondary Activity. It works fine.

But when screen goes off (either by timeout or button), the music stops playing, and when coming back and try to close the activity the program goes to "App Not Responding", because an IllegalStateException at a query like mediaplayer.isPlaying().

How can I prevent the mediaplayer to stop when screen goes off?

Does it have to be through a service??

Assuming that the answer is yes, I tried to convert the Music class into a service (see below). I also added <service android:enabled="true" android:name=".Music" /> into the Manifest.xml, and I am calling the Music class like this:

startService(new Intent(getBaseContext(), Music.class));
Music track = Music(fileDescriptor);

The only 2 new lines in the main Activity are startService(new Intent(getBaseContext(), Music.class)); and stopService(new Intent(getBaseContext(), Music.class));, together with the corresponding imports.

But now I get InstantiationException error because can't instantiate class when trying to start the service. What am I missing?

This is the exception:

E/AndroidRuntime(16642): FATAL EXCEPTION: main
E/AndroidRuntime(16642): java.lang.RuntimeException: Unable to instantiate service com.floritfoto.apps.ave.Music:                                                                             java.lang.InstantiationException: can't instantiate class com.floritfoto.apps.ave.Music; no empty constructor                                                                   
E/AndroidRuntime(16642):    at android.app.ActivityThread.handleCreateService(ActivityThread.java:2249)
E/AndroidRuntime(16642):    at android.app.ActivityThread.access$1600(ActivityThread.java:127)
E/AndroidRuntime(16642):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1213)
E/AndroidRuntime(16642):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(16642):    at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime(16642):    at android.app.ActivityThread.main(ActivityThread.java:4507)
E/AndroidRuntime(16642):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(16642):    at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime(16642):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
E/AndroidRuntime(16642):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
E/AndroidRuntime(16642):    at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(16642): Caused by: java.lang.InstantiationException: can't instantiate class com.floritfoto.apps.ave.Music; no empty constructor
E/AndroidRuntime(16642):    at java.lang.Class.newInstanceImpl(Native Method)
E/AndroidRuntime(16642):    at java.lang.Class.newInstance(Class.java:1319)
E/AndroidRuntime(16642):    at android.app.ActivityThread.handleCreateService(ActivityThread.java:2246)
E/AndroidRuntime(16642):    ... 10 more

and this is the Music.class:

package com.floritfoto.apps.ave;

import java.io.FileDescriptor;
import java.io.IOException;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.IBinder;
import android.widget.Toast;

public class Music extends Service implements OnCompletionListener{
    MediaPlayer mediaPlayer;
    boolean isPrepared = false;

    //// TEstes de servico
    @Override
    public void onCreate() {
        super.onCreate();
        info("Servico criado!");
    }
    @Override
    public void onDestroy() {
        info("Servico fudeu!");
    }
    @Override
    public void onStart(Intent intent, int startid) {
        info("Servico started!");
    }   
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    public void info(String txt) {
        Toast toast = Toast.makeText(getApplicationContext(), txt, Toast.LENGTH_LONG);
        toast.show();
    }
    //// Fim testes de servico

    public Music(FileDescriptor fileDescriptor){
        mediaPlayer = new MediaPlayer();
        try{
            mediaPlayer.setDataSource(fileDescriptor);
            mediaPlayer.prepare();
            isPrepared = true;
            mediaPlayer.setOnCompletionListener(this);
        } catch(Exception ex){
            throw new RuntimeException("Couldn't load music, uh oh!");
        }
    }

    public void onCompletion(MediaPlayer mediaPlayer) {
        synchronized(this){
            isPrepared = false;
        }
    }

    public void play() {
        if(mediaPlayer.isPlaying()) return;
        try{
            synchronized(this){
                if(!isPrepared){
                    mediaPlayer.prepare();
                }
                mediaPlayer.seekTo(0);
                mediaPlayer.start();
            }
        } catch(IllegalStateException ex){
            ex.printStackTrace();
        } catch(IOException ex){
            ex.printStackTrace();
        }
    }

    public void stop() {
        mediaPlayer.stop();
        synchronized(this){
            isPrepared = false;
        }
    }

    public void switchTracks(){
        mediaPlayer.seekTo(0);
        mediaPlayer.pause();
    }

    public void pause() {
        mediaPlayer.pause();
    }

    public boolean isPlaying() {
        return mediaPlayer.isPlaying();
    }

    public boolean isLooping() {
        return mediaPlayer.isLooping();
    }

    public void setLooping(boolean isLooping) {
        mediaPlayer.setLooping(isLooping);
    }

    public void setVolume(float volumeLeft, float volumeRight) {
        mediaPlayer.setVolume(volumeLeft, volumeRight);
    }

    public String getDuration() {
        return String.valueOf((int)(mediaPlayer.getDuration()/1000));
    }
    public void dispose() {
        if(mediaPlayer.isPlaying()){
            stop();
        }
        mediaPlayer.release();
    }
}
Nkvd answered 27/10, 2012 at 2:29 Comment(2)
have you read the documentation on how to use a service ? developer.android.com/reference/android/app/Service.htmlCurlicue
@Curlicue Of course not! I just want to implement the most basic mediaplayer ever made!! I don't want (and expect do not need) to know all its subtleties. I am just trying to follow the (famous?) marakana example here marakana.com/forums/android/examples/60.html. What I am doing wrong...Nkvd
S
6

This line from Logcat is the important one:

Caused by: java.lang.InstantiationException: can't instantiate class com.floritfoto.apps.ave.Music; no empty constructor

Your service needs another constructor that takes no arguments:

public Music() {
    super("Music");
}

EDIT:

Using a service is the correct approach if you want to keep the music playing when the screen is off. However, the phone will try to sleep when the screen is off, and this can interrupt your MediaPlayer.

The most reliable solution is to use a partial WakeLock to prevent the device from sleeping while you're playing music. Be sure to release the WakeLock properly when you're not actively playing music; otherwise the battery will drain.

You may also want to use startForeground(), which will reduce the risk of your service being killed when there is memory pressure. It will also create a nice user experience by showing a persistent notification when your service is running.

Instantiating the Music class with Music track = Music(fileDescriptor); is probably doing some harm. A better approach is to pass the file descriptor as an Extra in the Intent that you pass to startService():

Intent serviceIntent = new Intent(this, Music.class);
serviceIntent.putExtra("ServiceFileDescriptor", fileDescriptor);
startService(serviceIntent);

Then, retrieve the file descriptor from that same Intent when it's passed to your service's onStartCommand() method:

public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStart();

    Bundle bundle = intent.getExtras();
    // NOTE: The next line will vary depending on the data type for the file
    // descriptor. I'm assuming that it's an int.
    int fileDescriptor = bundle.getIntExtra("ServiceFileDescriptor");
    mediaPlayer = new MediaPlayer();
    try {
        mediaPlayer.setDataSource(fileDescriptor);
        ...
    ...
    return START_STICKY;
}

A few of things to note here. I've moved the code from your original constructor (which should be removed) into onStartCommand(). You can remove the onStart() method as well, since it will only be called on pre-2.0 devices. If you want to support modern Android versions, you'll need to use onStartCommand() instead. Finally, the START_STICKY return value will ensure that the service stays running until you call stopService() from your activity.

EDIT 2:

Using a service enables your users to move between activities without interrupting the MediaPlayer. You don't have much control over how long an Activity will stay in memory, but an active Service (especially if you call startForeground()) won't be killed unless there is very strong memory pressure.

To interact with the MediaPlayer after the service is started, you have a couple of options. You can pass additional commands to the service by creating Intents and using the action string (and/or some extras) to tell the service what you would like it to do. Just call startActivity() again with the new Intent, and onStartCommand() will be called in the service, at which point you can manipulate the MediaPlayer. The second option is to use a bound service (example here) and to bind/unbind each time you enter/leave an activity that needs to communicate with the service. Using a bound service "feels" as though you're directly manipulating the service, but it's also more complex since you need to manage binding and unbinding.

Selfrespect answered 27/10, 2012 at 2:53 Comment(11)
You mean following the line public Music(FileDescriptor fileDescriptor){? I did it, and got "The constructor Service(String) is undefined", and asked me to remove the "Music". I did it, and get the very same InstantiationException. :(Nkvd
Now I understood what you meant (but I had to delete the "Music" inside the parenthesis). But then I still get the same problem: I see in the logcat that the mediaplayer gets disconnected when screen is off and I get IllegalStateException when query mediaPlayer.isPlaying, like if no service was being executed. :(Nkvd
Maybe Services are not what I need to solve this, and there is some other much simpler solution?? Please help...Nkvd
Or maybe the problem is that the mediaPlayer is not created when service is created...? :o(Nkvd
Yes, that's part of the problem. Please see my updated answer.Selfrespect
MMmmm... beginning to understand! While trying to implement your solution, please be (even more) kind enough to answer two little questions: 1) If the problem was the wakelock, why to use services at all, i.e., why not just add a wakelock to the program I already had? 2) How do I interact with the Music.mediaPlayer? I tried both Music.mediaPlayer.getDuration() and Music.getDuration() and got null pointer exception. Thanks!!Nkvd
Got it. I will implement the service as you suggest. Thanks a zillion for your time and kindness!!!Nkvd
Sorry to bother you again, but I still don't get how to interact with the server. I started the service with Intent, bundle extras (for the filename), and startService(). But for example how do I know that the player is playing, or the song duration? Thanks!Nkvd
Once you've started the service with an Intent, you can bind to it and call any public methods that you've defined on your Service. For example, you could define methods that get the playback status or the duration of the current song. Please take a look at the example link near the bottom of my answer.Selfrespect
I implemented a bound service as you suggested, and everything is working well. In case anyone needs the actual code, I can post it. Thanks A LOT for all the help and patience!!!! :o)Nkvd
@LuisA.Florit, yes please do post the code. Thanks!Kevakevan
P
4

as an option you can keep the screen activated to maintain the MediaPlayer reproducing the media:

 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Pestana answered 28/3, 2017 at 16:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.