跨多种活动的Android全面防故障音乐服务

时间:2012-07-15 07:25:27

标签: android service background andengine playback

我知道这个问题之前已被问过很多次,似乎是几个问题的集合,但我觉得这对很多开发人员来说都很重要;我需要创建一个背景音乐Service,它可以在Android游戏的多个活动中运行,在应用程序终止时结束,并在以下所有情况下暂停:

  1. 启动了具有自己音乐的某个Activity。 (当Activity完成时恢复。这恰好是AndEngine活动。)
  2. 按下主屏幕,应用程序背景,或终止应用程序。应用程序返回前台时恢复。需要使用onUserLeaveHint()Another helpful link.
  3. 手机接听电话并中断该应用。处理呼叫时恢复。需要使用与this类似的TelephonyManager
  4. 屏幕已锁定。 (屏幕解锁后恢复。)需要使用ACTION_USER_PRESENTbe very problematic
  5. 基本上,只要没有显示应用程序或者向用户显示#1中的特殊活动,音乐就会暂停。
  6. 以上是我需要的所有内容以及我拼凑在一起的信息。 我当前的代码基本上类似于this

    我很遗憾AndEngine设法在音乐中没有这些问题,因此查看源代码可能有助于寻找答案的人。我正在使用the last functional GLES1 version from Google Code

    我已经看过以下链接以及创建好音乐Service

    我希望解决方案Service能够:

    • 尽可能减少使用BroadcastReceivers和Android Manifest添加/权限
    • 自包含和错误检查

    其他笔记

    • 目前,所有需要背景音乐的活动都扩展了一个共同的特殊课程。
    • 音乐需要循环播放,但只能播放单曲。

    提前感谢大家!祝你好运!

    编辑 - 以下是代码段,随意改进或忽略:

    Media Player Wrapper

    import android.content.SharedPreferences;
    import android.media.MediaPlayer;
    import android.preference.PreferenceManager;
    import android.util.Log;
    
    public class CarefulMediaPlayer {
        final SharedPreferences sp;
        final MediaPlayer mp;
        private boolean isPlaying = false;
    
        public CarefulMediaPlayer(final MediaPlayer mp, final MusicService ms) {
            sp = PreferenceManager.getDefaultSharedPreferences(ms.getApplicationContext());
            this.mp = mp;
        }
    
        public void start() {
            if (sp.getBoolean("com.embed.candy.music", true) && !isPlaying) {
                mp.start();
                isPlaying = true;
            }
        }
    
        public void pause() {
            if (isPlaying) {
                mp.pause();
                isPlaying = false;
            }
        }
    
        public void stop() {
            isPlaying = false;
            try {
                mp.stop();
                mp.release();
            } catch (final Exception e) {}
        }
    }
    

    音乐服务

    import android.app.Service;
    import android.content.Intent;
    import android.media.MediaPlayer;
    import android.os.IBinder;
    
    public class MusicService extends Service {
        static CarefulMediaPlayer mPlayer = null;
    
        @Override
        public IBinder onBind(final Intent arg0) {
            return null;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            final MediaPlayer mp = MediaPlayer.create(this, R.raw.title_music);
            mp.setLooping(true);
            mPlayer = new CarefulMediaPlayer(mp,this);
        }
    
        @Override
        public int onStartCommand(final Intent intent, final int flags, final int startId) {
            mPlayer.start();
            return 1;
        }
    
        @Override
        public void onStart(final Intent intent, final int startId) {
    
        }
    
        public IBinder onUnBind(final Intent arg0) {
            return null;
        }
    
        public static void onStop() {
            mPlayer.stop();
        }
    
        public static void onPause() {
            if (mPlayer!=null) {
                mPlayer.pause();
            }
        }
    
        public static void onResume() {
            if (mPlayer!=null) {
                mPlayer.start();
            }
        }
    
        @Override
        public void onDestroy() {
            mPlayer.stop();
            mPlayer = null;
        }
    
        @Override
        public void onLowMemory() {
    
        }
    }
    

    改进了基本活动类

    import android.app.Activity;
    import android.content.Intent;
    import android.os.PowerManager;
    import android.telephony.TelephonyManager;
    import android.view.KeyEvent;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.ViewGroup.LayoutParams;
    import android.widget.ImageView;
    
    public abstract class BetterActivity extends Activity {
    
        private boolean isHome = true;
    
        @Override
        protected void onResume() {
            System.gc();
            super.onResume();
            MusicService.onResume();
            isHome = true;
        }
    
        @Override
        protected void onPause() {
            if (((TelephonyManager)getSystemService(TELEPHONY_SERVICE)).getCallState()==TelephonyManager.CALL_STATE_RINGING
                    || !((PowerManager)getSystemService(POWER_SERVICE)).isScreenOn()) {
                MusicService.onPause();
            }
            super.onPause();
            System.gc();
        }
    
        @Override
        public boolean onKeyDown (final int keyCode, final KeyEvent ke) {
            switch (keyCode) {
            case KeyEvent.KEYCODE_BACK:
                isHome = false;
            default:
                return super.onKeyDown(keyCode, ke);
            }
        }
    
        @Override
        public void startActivity(final Intent i) {
            isHome = false;
            super.startActivity(i);
        }
    
        @Override
        protected void onUserLeaveHint() {
            if (isHome) {
                MusicService.onPause();
            }
            super.onUserLeaveHint();
        }
    
    }
    

3 个答案:

答案 0 :(得分:5)

首先是一些代码。下面我会给你一个解释。

public class MusicService extends Service {

    // service binder
    private final IBinder mBinder = new LocalBinder();

    // music player controling game music
    private static CarefulMediaPlayer mPlayer = null;

    @Override
    public void onCreate() {
        // load music file and create player
        MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.title_music);
        mediaPlayer.setLooping(true);
        mPlayer = new CarefulMediaPlayer(mediaPlayer, this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    // =========================
    // Player methods
    // =========================
    public void musicStart() {
        mPlayer.start();
    }

    public void musicStop() {
        mPlayer.stop();
    }

    public void musicPause() {
        mPlayer.pause();
    }

    /**
     * Class for clients to access. Because we know this service always runs in
     * the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        MusicService getService() {
            return MusicService.this;
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }

}

的活动:

public class StartupActivity extends Activity {

// bounded service
private static MusicService mBoundService;

// whetere service is bounded or not
private boolean mIsBound;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_startup);
    doBindService();

    // HOW TO WORK WITH THE SERVICE:
    // call the following methods whenever
    // you want to interact with you 
    // music player
    // ===================================

    // call this e.g. in onPause() of your Activities
    StartupActivity.getService().musicPause();

    // call this e.g. in onStop() of your Activities
    StartupActivity.getService().musicStop();

    // call this e.g. in onResume() of your Activities
    StartupActivity.getService().musicStart();
}

@Override
public void onDestroy() {
    super.onDestroy();
    doUnbindService();
}

private final ServiceConnection mServiceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName className, IBinder service) {
        setService(((MusicService.LocalBinder) service).getService());
    }

    @Override
    public void onServiceDisconnected(ComponentName className) {
        setService(null);
    }
};

private void doBindService() {
    Intent service = new Intent(getBaseContext(), MusicService.class);
    // start service and bound it
    startService(service);
    bindService(new Intent(this, MusicService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
    mIsBound = true;
}

private void doUnbindService() {
    if (mIsBound) {
        // Detach existing connection.
        unbindService(mServiceConnection);
        mIsBound = false;
    }
}

public static MusicService getService() {
    return mBoundService;
}

private static void setService(MusicService mBoundService) {
    StartupActivity.mBoundService = mBoundService;
}
}

首先,你有一个在后台运行的服务。此服务像您一样创建mediaPlayer对象。使用localBinder,您可以在Activity中绑定Service,并像普通的Java-Object一样访问它。 我发布的活动bindes the Service。在它的onCreate()方法中,您可以找到一种如何与您的mediaPlayer进行交互的方法。 您可以将任何活动绑定到您的服务。

另一种解决方案:

public class CarefulMediaPlayer {
final SharedPreferences sp;
final MediaPlayer mp;
private boolean isPlaying = false;
private static CarefulMediaPlayer instance;

public CarefulMediaPlayer(final MediaPlayer mp, final MusicService ms) {
    sp = PreferenceManager.getDefaultSharedPreferences(ms.getApplicationContext());
    this.mp = mp;
    instance = this;
}

public static CarefulMediaPlayer getInstance() {
    return instance;
}

public void start() {
    if (sp.getBoolean("com.embed.candy.music", true) && !isPlaying) {
        mp.start();
        isPlaying = true;
    }
}

public void pause() {
    if (isPlaying) {
        mp.pause();
        isPlaying = false;
    }
}

public void stop() {
    isPlaying = false;
    try {
        mp.stop();
        mp.release();
    } catch (final Exception e) {}
}
}

然后你可以通过调用CarefulMediaPlayer.getInstance()来暂停,播放和停止音乐.play();

答案 1 :(得分:2)

我是这样做的,我对结果感到满意:

第一次创建服务:

public class LocalService extends Service
{
    // This is the object that receives interactions from clients. See RemoteService for a more complete example.
    private final IBinder mBinder = new LocalBinder();
    private MediaPlayer player;

    /**
     * Class for clients to access. Because we know this service always runs in
     * the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder
    {
        LocalService getService()
        {
            return LocalService.this;
        }
    }

    @Override
    public void onCreate()
    {

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        // We want this service to continue running until it is explicitly stopped, so return sticky.
        return START_STICKY;
    }

    @Override
    public void onDestroy()
    {
        destroy();
    }

    @Override
    public IBinder onBind(Intent intent)
    {
        return mBinder;
    }


    public void play(int res)
    {
        try
        {
            player = MediaPlayer.create(this, res);
            player.setLooping(true);
            player.setVolume(0.1f, 0.1f);
            player.start();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }


    public void pause()
    {
        if(null != player && player.isPlaying())
        {
            player.pause();
            player.seekTo(0);
        }
    }


    public void resume()
    {
        try
        {
            if(null != player && !player.isPlaying())
            {
                player.start();
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }


    public void destroy()
    {
        if(null != player)
        {
            if(player.isPlaying())
            {
                player.stop();
            }

            player.release();
            player = null;
        }
    }

}

第二次,创建一个基本活动并扩展您希望播放背景音乐的所有活动:

public class ActivityBase extends Activity
{
    private Context context = ActivityBase.this;
    private final int [] background_sound = { R.raw.azilum_2, R.raw.bg_sound_5 };
    private LocalService mBoundService;
    private boolean mIsBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        doBindService();
    }

    @Override
    protected void onStart()
    {
        super.onStart();

        try
        {
            if(null != mBoundService)
            {
                Random rand = new Random();
                int what = background_sound[rand.nextInt(background_sound.length)];
                mBoundService.play(what);
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    @Override
    protected void onStop()
    {
        super.onStop();
        basePause();
    }



    protected void baseResume()
    {
        try
        {
            if(null != mBoundService)
            {
                mBoundService.resume();
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }


    protected void basePause()
    {
        try
        {
            if(null != mBoundService)
            {
                mBoundService.pause();
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }



    private ServiceConnection mConnection = new ServiceConnection()
    {
        public void onServiceConnected(ComponentName className, IBinder service)
        {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service. Because we have bound to a explicit
            // service that we know is running in our own process, we can
            // cast its IBinder to a concrete class and directly access it.
            mBoundService = ((LocalService.LocalBinder) service).getService();

            if(null != mBoundService)
            {
                Random rand = new Random();
                int what = background_sound[rand.nextInt(background_sound.length)];
                mBoundService.play(what);
            }
        }

        public void onServiceDisconnected(ComponentName className)
        {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            // Because it is running in our same process, we should never
            // see this happen.
            mBoundService = null;

            if(null != mBoundService)
            {
                mBoundService.destroy();
            }
        }
    };

    private void doBindService()
    {
        // Establish a connection with the service. We use an explicit
        // class name because we want a specific service implementation that
        // we know will be running in our own process (and thus won't be
        // supporting component replacement by other applications).

        Intent i = new Intent(getApplicationContext(), LocalService.class);
        bindService(i, mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    private void doUnbindService()
    {
        if (mIsBound)
        {
            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
        }
    }


    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        doUnbindService();
    }
}

就是这样,现在你在从ActivityBase扩展的所有活动中都有背景声音。

您甚至可以通过调用basePause()/ baseResume()来控制暂停/恢复功能。

不要忘记在清单中声明服务:

<service android:name="com.gga.screaming.speech.LocalService" />

答案 2 :(得分:0)

在启动活动中,我们分别绑定和启动服务。这是错误的,因为服务将在活动退出后继续运行,因为我们在任何地方都没有调用 stopService()。因此,应该删除部分'startService(service)',因为绑定服务已经“自动创建”服务。

如果有人得到相反的结果,请纠正我

startService(service);// remove this part
bindService(new Intent(this, MusicService.class), mServiceConnection, Context.BIND_AUTO_CREATE);