Bueno, ya va siendo hora de empezar el trabajo en JustPlay.
Después de mucho documentarme sobre Activities, Services y Threads, creo que tengo una aproximación bastante buena a la arquitectura que voy a intentar implementar.
Introducción
Un requisito fundamental es que la aplicación consuma los mínimos recursos posibles, con lo cual cabría pensar que se puede implementar mediante una Activity normal y corriente. Esto es un error, una Activity tiene un ciclo de vida determinado y cabe suponer (aunque no he hecho la prueba) que en cuanto pasara al estado “Paused”, se terminaría la reproducción de música.
Por tanto hay que buscar otro componente que escape del ciclo de vida de la Activity y con el cual se pueda comunicar la Activity es decir, un Service. El ciclo de vida del Service hace que este permanezca vivo aún cuando la Activity pase a cualquier estado que no sea “Resumed”.
En el tutorial de CrankPlay ya se ha visto un ejemplo de esto, sin embargo este ejemplo estaba claramente desfasado y se le pueden criticar varios puntos desde el punto de vista de mis requisitos:
- La comunicación entre procesos (IPC) no me es necesaria, tan solo necesito que una única actividad acceda al servicio.
- La definición de la interfaz con AIDL tan solo es necesaria si se desea IPC, por tanto, basta con heredar de Service.
- Además, en cuanto a este Service, hay que considerar que no es algo que yo lance y me pueda olvidar de él, sino que tendré que comunicarme periódicamente con él (modificación de la playlist, start/stop, etc...), es decir, necesito un BoundService.
- Por último, tal y como dice la documentación en numerosos sitios, un Service se ejecuta en el mismo thread que el UI Thread, con lo cual... necesitaré un thread propio que se encargue de la reproducción (actividad pesada por definición).
Gráficamente, esta propuesta quedaría así.
Ahora voy a montar la infraestructura básica.
Implementación
La actividad de momento no tiene mayor historia, basta con dejarla tal cual.
Para implementar el Service, creo una nueva clase (MediaService) y hago que herede de Service. Para que sea realmente funcional, lo declaro en el AndroidManifest.xml:
<service android:name="com.crankcode.services.MediaService" android:exported="false"/>;
El atributo “android:exported=false”, lo que indica es que el servicio no estará disponible para otras actividades del sistema, un poco de programación defensiva.
Para el thread, simplemente creo la clase MediaThread y hago que implemente Runnable.
Para arrancar el MediaService, en el onCreate de MediaPlayer escribo lo siguiente:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(getBaseContext(), MediaService.class));
setContentView(R.layout.main);
}
Y en el MediaService, defino su onStart así:
@Override
public void onStart(Intent intent, int startId) {
Log.d("CrankPlayer", "MediaService.onStart()");
this.mediaThread = new Thread(new MediaThread());
this.mediaThread.start();
super.onStart(intent, startId);
}
Por último, en el run de MediaService meto un chivato:
public void run() {
Log.d("CrankPlayer", "MediaThread.run");
}
Ahora, a arrancar y ver si todo ha ido bien.
Y todo correcto, funcionando a la primera.
TODO
- Añadir la instancia de MediaPlayer al Thread
- Hacer el bind del service en la activity.
Código
Como de costumbre, el código esta en GitHub: