2012-07-15 31 views
24

我知道這個問題之前已經被問過好幾次,看起來可能是幾個問題的集合,但我覺得這對許多開發者來說都是相關且重要的;我需要創建一個背景音樂Service可以在多個活動,爲我的Android遊戲應用程序時,結束該結束,在所有的下列情況暫停運行:跨多個活動的Android綜合失效音樂服務

  1. 一定Activity,有它自己的音樂開始。 (當此Activity完成後繼續,這恰好是AndEngine活動。)
  2. 主屏幕被按下,應用程序會後退,或者應用程序被終止。當應用程序返回到前臺時繼續。需要使用onUserLeaveHint()Another helpful link.
  3. 手機接到來電並中斷應用程序。電話處理完畢後繼續。需要使用TelephonyManager類似於this
  4. 屏幕鎖定。 (屏幕解鎖後恢復。)需要使用ACTION_USER_PRESENT,這似乎是beveryproblematic
  5. 基本上,音樂在應用程序未顯示或從用戶顯示#1的特殊活動時暫停。

以上就是我所需要的和我拼湊在一起的信息。 我目前的代碼基本上類似於this

我覺得很奇怪的是AndEngine管理有沒有這些問題與他們的音樂,也許看在源代碼將幫助別人尋找一個答案。我正在使用the last functional GLES1 version from Google Code

我已經採取了看看下面的鏈接,以及如何建立一個好的音樂Service

我想解決Service到:

  • 最小化
  • 自給使用BroadcastReceivers和Android清單添加/權限,如果可能的話
  • 和錯誤檢查

其他注意事項

  • 目前所有需要背景音樂的活動都會擴展一個共同特色升級。
  • 音樂需要循環播放,但只能播放單曲。

感謝大家提前!祝你好運!

編輯 - 下面是代碼片段,隨意提高或忽略:

媒體播放器包裝

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(); 
    } 

} 
+0

我在試圖完成所有的目標自己的過程是,看到你的問題。任何進展?我會盡力讓你知道我完成後的情況。 – ajacian81 2012-10-01 18:00:31

+0

@ ajacian81暫時還沒有上過這個網站,我們決定推遲推出這款音樂,並將其作爲beta版發佈。 – pqn 2012-10-21 18:13:21

+0

好吧,我會讓你知道我的嘗試如何,我應該在一個月左右得到答案。 – ajacian81 2012-10-21 19:52:12

回答

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,您可以將活動中的服務綁定到活動中並像普通的Java對象一樣訪問它。 我發佈的活動與服務相關。在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()停止音樂播放();

+0

這是否可以解決我的問題2,3和4? – pqn 2012-07-25 17:04:23

+0

在每一個Activty中實現暫停和恢復MediaPlayer,然後就完成了。每當它被中斷時,音樂都會暫停。您也可以創建一個「抽象」 - 實現此功能的活動類,並且您的所有活動都將擴展此功能。 (這是更優雅的版本) – 2012-07-25 18:17:29

+0

我得到一個空指針例外:StartupActivity.getService()。musicStart();任何想法,爲什麼這可能是空的? – 2013-01-23 15:27:06

0

在啓動活動中,我們分別綁定和啓動服務。這是錯誤的,因爲服務將在活動退出後繼續運行,因爲我們沒有在任何地方調用stopService()。所以「startService(服務)」部分應該被刪除,因爲綁定服務已經是「自動創建」服務了。

請糾正我,如果沒有人得到相反的結果

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

我就是這麼做的,我很高興與結果:

1日創建服務:

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; 
     } 
    } 

} 

第二,創建一個基本的活動,並擴展所有的活動在你想玩的女巫從它的ckground音樂:

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" /> 
+0

這適用於我的一些修改,以適應我的代碼,謝謝。 – Jack 2015-02-18 00:12:29