2012-07-15 11 views
24

Sé que esta pregunta se ha hecho muchas veces antes y podría parecer un conglomerado de varias preguntas, pero creo que es relevante e importante para muchos desarrolladores; Necesito crear una música de fondo Service que se puede ejecutar a través de múltiples actividades para mi juego para Android que termina cuando se termina la aplicación y se detiene en todas las circunstancias siguientes:Servicio integral de música a prueba de fallas de Android en múltiples actividades

  1. Un cierto Activity que tiene su propia música se inicia . (Reanudar cuando finalice este Activity. Esta es una actividad AndEngine).
  2. Se presiona la pantalla de inicio y la aplicación tiene un fondo o la aplicación finaliza. Se reanuda cuando la aplicación vuelve al primer plano. Requiere el uso de onUserLeaveHint(). Another helpful link.
  3. El teléfono recibe una llamada e interrumpe la aplicación. Se reanuda cuando la llamada ha sido tratada. Requiere el uso de TelephonyManager similar a this.
  4. La pantalla está bloqueada. (Se reanuda después de desbloquear la pantalla.) Requiere el uso de ACTION_USER_PRESENT, que parece beveryproblematic.
  5. Básicamente, la música se detiene cuando la aplicación no se muestra o cuando la actividad especial del # 1 se muestra al usuario.

Above es todo lo que necesito y la información que he reconstruido. Mi código actual básicamente se parece a this.

Me resulta curioso que AndEngine no tenga ninguno de estos problemas con su música, por lo que quizás buscar en el código fuente ayudaría a alguien a buscar una respuesta. Estoy usando the last functional GLES1 version from Google Code.

He echado un vistazo a los siguientes enlaces, así en la creación de una buena música Service:

Me gustaría que la solución Service a:

  • minimizar el uso de BroadcastReceivers y Android Manifiesto adiciones/permisos, si es posible
  • independiente y comprobación de errores

Otras Notas

  • En la actualidad, todas las actividades que requieren música de fondo se extienden a una especie común l clase.
  • La música necesita repetirse pero solo ejecuta una sola pista.

Gracias a todos por adelantado! ¡La mejor de las suertes!

Editar - Estas son fragmentos de código, no dude en mejorar o ignorar:

Media Player Envoltura

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) {} 
    } 
} 

servicio de música

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() { 

    } 
} 

Mejora de la base de tipo de actividad

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

Estoy en el proceso de tratar de lograr todos sus objetivos de mí y vi tu pregunta. ¿Cualquier progreso? Intentaré hacerte saber cómo va esto cuando termine. – ajacian81

+0

@ ajacian81 hace tiempo que no visitamos el sitio, decidimos retrasar la música y lanzarla antes como versión beta. – pqn

+0

bien, bien. Te dejaré saber cómo va mi intento, debería tener algunas respuestas en un mes más o menos. – ajacian81

Respuesta

5

Primero, aparece un código. A continuación te daré una explicación.

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

} 

Actividad:

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

En primer lugar usted tiene un servicio que se ejecuta en segundo plano. Este servicio crea el objeto mediaPlayer como lo hizo. Con localBinder puede vincular el Servicio en su (s) Actividad (s) y acceder a él como un Objeto Java normal. La actividad que publiqué bindes el Servicio. En su método onCreate() puede encontrar la manera de interactuar con su mediaPlayer. Puede vincular cualquier Actividad a su Servicio.

Otra solución:.

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) {} 
} 
} 

A continuación, puede pausar, reproducir y detener la música llamando CarefulMediaPlayer.getInstance() play();

+0

¿Esto aborda mis puntos 2, 3 y 4? – pqn

+0

Implemente pausando y reanudando el MediaPlayer en cada actividad individual y listo. Cada vez que se interrumpe, la música se detiene. También puede crear una clase de actividad "abstracta" que implemente esta función y todas sus actividades ampliarán esta. (esta es la versión más elegante) –

+0

Obtengo una excepción de puntero nulo en: StartupActivity.getService(). musicStart(); ¿Alguna idea de por qué esto podría ser nulo? –

0

En la actividad de inicio somos vinculantes y se inicia el servicio por separado. Esto es incorrecto ya que el servicio seguirá funcionando después de que finalice la actividad ya que no hemos llamado al stopService() en ninguna parte. Por lo tanto, la parte 'startService (service)' debe eliminarse ya que el servicio de vinculación ya está "Auto-Creating" también el servicio.

Por favor, corríjanme si alguien obtuvo resultados opuestos

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

lo hice de esta manera y estoy satisfecho con el resultado:

primero crear el servicio:

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

} 

2nd, crea una actividad base y extiende todas tus actividades en la que desees jugar el ba música ckground de ella:

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

Y eso es todo, ahora usted tiene el sonido de fondo en todas las actividades que se extendían desde ActivityBase.

Incluso puede controlar la funcionalidad de pausa/reanudación llamando a basePause()/baseResume().

No se olvide de declarar el servicio en el manifiesto:

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

Esto funcionó para mí con algunas modificaciones para adaptarse a mi código, gracias. – Jack

Cuestiones relacionadas