diumenge, 31 de gener del 2016

Utilización de librerias multimedia integradas Media Playback y Servicios

Media Playback

El framework multimedia de android incluye soporte para la reproducción de varios tipos de medios, de manera que se le puede integrar fácilmente audio, vídeo e imágenes en las aplicaciones.
Los recursos van en el directorio RAW.
El audio tiene más juego que el video... ¿Si se intenta hacer un streaming por bluetooth como marcas prioridad para el audio? Tenemos el AudioManager que permite manipular todo lo que afecta al tema de audio.

En ambos casos (audio o video) hay que poner permisos si queremos hacer streaming.
<uses-permission android:name="android.permission.INTERNET" />

Si la aplicación tiene que mantenerse en pantalla se puede usar MediaPlayer.setScreenOnWhilePlaying() o los métodos MediaPlayer.setWakeMode(), con sus permisos:
<uses-permission android:name="android.permission.WAKE_LOCK" />

La clase MediaPlayer permite obtener, decodificar y reproducir audio y video. Una URL internas, listas de audio, fotos... y URLs externas (streaming).

Los formatos soportados se pueden ver en:

Si queremos reproducir un fichero en local (RAW) tendremos que guardarlo en el directorio /res/raw:

MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.audio1);
mediaPlayer.start();

Si queremos usar el tema del streaming, tendremos que poner:

String url = "http://........";
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // puede tardar en cargar en el buffer.
mediaPlayer.start();

El método prepare() no se puede llamar nunca desde el hilo de la IGU (Interficie Grafica de Usuario). Imaginate que haces una sopa de letra y quieres música de fondo, si lo haces directamente en la activity bloquearía la interficie hasta que se cargue el buffer, la solución es meterlo en un servicio.

Hay un método llamado prepareAsync() que crearía un servicio de forma automática. Hará falta un listener, que llamará un callback y se disparará cuando este cargado.
El MediaPlayer funciona como una máquina de estados.

http://cfile23.uf.tistory.com/original/191F6F184C1E1BBE07D17F

Una vez se ha utilizado un medio, se necesita liberar con el método release(), es importante que se tenga en cuenta que cuando el usuario cambia la orientación se  vuelve a crear un objeto MediaPlayer, porque se vuelve a llamar a onCreate(), por eso se necesita liberar los recursos.

media.release();
media=null;

Si no queremos este comportamiento podemos guardar el estado y gestionarlo de manera manual.
Haciendo que cuando pasa un cambio de horientación o el teclado se oculta, entonces ejecuta el método por defecto que detecta cambios de configuración (onConfigurationChanged()) y se disparará teniendolo puesto en la activity. El objeto newConfig tendrá la información que podrás preguntar el estado.
Podiendo mirar la horientación podremos hacer condicionales y permite detectar que se disparará cuando hay un cambio.
Después llamará al onCreate().

Por ejemplo, para hacer un cambio de horientación:


















Código:

<manifest package="es.com.blogspot.fmesasc.cambiohorientacion">

<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">

<activity android:name=".MainActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/app_name">

<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>

</activity>

</application>

</manifest>


Como se puede ver, hemos añadido android:configChanges="orientation|keyboardHidden|screenSize" para que detecte la rotación de pantalla o la ocultación de teclado en este caso, para que esto funcione tendremos que sobreescribir una función:

package es.com.blogspot.fmesasc.cambiohorientacion;
import android.content.res.Configuration;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
        } else if (newConfig.orientation ==
                Configuration.ORIENTATION_PORTRAIT){
            Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
        }
    }
}


onConfigurationChanged únicamente funcionará si esta declarado en el manifest, de esta manera, podemos comprobar la nueva orientación y la comparamos para saber si es horizontal o vertical (Landscape o Portrait). De esta manera mostramos el toast del ejemplo inicial indicando el tipo de orientación.

Descargar.

Servicios

Los servicios son componentes de una aplicación que realizan operaciones de larga duración que no requieren la interacción de un usuario, o que ofrecen funcionalidades para otras aplicaciones. Normalmente envian correos, sincronizan datos o reproducen medios.
Igual que hay una activity en el manifest, tendremos que declarar también los servicios en el manifest. La comunicación es similar al sistema de comunicación que tienen actividades, los intents. Los servicios heredan de services o subclase de este. No tienen nunca interficie.
Cada servicio se declarará en el AndroidManifest.xml con <service>.
Los servicios se inician con:
Context.startService() y Context.bindService().
Los servicios se ejecutan como hilo principal del proceso. Eso implica que si consume muchos recursos se recomiende hacer uno nuevo.
La clase IntentService es una implementación estandard de Service que funciona como un hilo propio.
Entonces, un servicio no es un proceso separado. Se ejecuta en el mismo proceso que la aplicación. Tampoco es un hilo separado.
Los servicios se utilizan para hacer tareas en segundo plano, notificando cuando esta realizado. Se inicia con Context.startService(). Este concepto es muy similar al de activityResult.
Existe la posibilidad de exponer a otras aplicaciones parte de una aplicación en concreto. Se utiliza el método de clase Context.bindService().
Podemos especificar que el servicio este asociado a un otro proceso con la anotación de dos puntos (:). Los dos puntos indican que el proceso será privado a la aplicación, sino será global para las aplicaciones.
Por ejemplo:
<service
   android:name="WordService"
   android:process=":proces1"
   android:icon="@drawable/icon"
   android:label="@string/nom_servei"
   >
</service>


Otro ejemplo:
public class ServeiMP extends Service implements
MediaPlayer.OnPreparedListener {
    private static final ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mMediaPlayer = null;
     public int onStartCommand(Intent intent, int flags, int startId) {
     ...
     if (intent.getAction().equals(ACTION_PLAY)) {
     mMediaPlayer = ... // inicialització
     mMediaPlayer.setOnPreparedListener(this);
     mMediaPlayer.prepareAsync(); }
     }
     /** cridat quan MediaPlayer està llest */
     public void onPrepared(MediaPlayer player) {
     player.start();
     }
}

Será necesario controlar los errores que se producen de manera asíncrona.
En onStartCommand le pasamos un intent, con la pista de audio que queremos que suene, con el ACTION_PLAY tendremos que sabemos si esta sonando o no la música. Al pasarle el intent, este le envía la pista de audio que nosotros queremos reproducir. Una de lsa cosas a tener en cuenta que podemos tener errores, por lo que hay otra interfaz que se llama Media.Player.OnErrorListener, haciendo override en onError podremos configurarlo.

Puedes bajarte un ejemplo de un servicio que permite descargar:
Descargar.

public class Servei extends Service implements MediaPlayer.OnErrorListener{
   MediaPlayer mMediaPlayer;
   public void initMediaPlayer() {
      // inicia MediaPlayer
      mMediaPlayer.setOnErrorListener(this);
   }
 @Override
 public boolean onError(MediaPlayer mp, int what, int extra) {
      // The MediaPlayer esta en estado de error.
    }
}


Tendrá un boton que dice descargas, y un toast, mientras tanto podemos hacer lo que queremos, cuando este descargado, el servició terminará. Si le das otra vez el botón la descarga se vuelve a realizar.

Mira el código o descárgate el zip.

Primero tenemos el IntentService.
En el onCreate solo llamamos al layout.
En el onCLick hacemos un intent del servicio, creamos un mensaje que le pasamos un handler y este se encargará si esta descargado o no. Eso es porque el handle es asincrono, por lo tanto, no bloquea nada. Cuando reciba el mensaje se disparará el handleMessage, si el resultado esta bien saldrá correcto, solo cuando recibe.
En el intent le pasamos un mensaje y el mensajero, en el intent también le pasamos un setData, que se utiliza para obtener el nombre del fichero. El putExtra será precisamente para poder indicar el path para saber donde tiene que guardar.

El servicio tiene un entero, result, que si todo va bien le pondremos okey en vez de result_canceled. en el onHandleINtent, que es asincrono y obtenemos el urlpath y de esta manera ponemos el nombre del fichero y hacemo sun fichero output para guardar. sino lo borraremos.
La lectura la haremos con el inputStreamReader, en un principio a fluxEntrada y fos serán iguales. Técnicamente lo que hace es byte a byte escribirlos, cuando es -1 significa que hay que cerrar el archivo.

Ponemos la ruta, conexión, y el InputStreamReader va poniendo los bytes y los convierte en UNICODE. Si es pdf o algo hay que anular los readers que los convierten a UNICODE. Normalmente se llaman fis y fos.
Después tenemos un while que lee carácter a carácter y si no es igual a menos 1 escribe (Para un pdf necesitaremos eliminarlo).

Cuando acaba le ponemos result_ok y en el .java veremos que ha devuelto RESULT_OK. Si hay problemas hay un catch, y el finally se ejecuta porque finaliza correctamente o hay un fallo.

Cuando ya esta, cogemos el mensaje y le ponemos como argumento 1 que todo ha ido correctamente y como objeto le ponemos la ruta del objecto.
Si hay algún problema te envia la respuesta.

Un servicio puede estar encendido mientras la actividad esta en pause, pero la CPU funcionaria, el wifi no, entonces podemos programarle que queremos que haga. Para desactivarlo o cambiarlo tenemos que poner wifiLock.acquire() o wifiLock.release(); de esta manera controlamos si funciona o no. Se pondria setWakeMode para indicar que tiene que hacer.

Si queremos también se puede mostrar una notificación. Se programa con Notification, para hacerlo se tienen que enviar mediante un intent especial, con PendingIntent. Allí podremos poner la información.

Actividades:
  1. Desarrolla una aplicación para android que reproduzca una pista de música guardada en los recursos del dispositivo. La aplicación tiene que tener botones para parar, pausar y iniciar la pista de música. Tiene que mostrar el tiempo de reproducción.
    Solución 1.
    Solución profe.
  2. A partir del ejercicio anterior, crea un servicio en primer plano que realice la reproducción de música mostrando con una notificación de información al usuario en la barra de estado.
    Solución.


Apuntes.
Ejercicios.

Para más información, consultad:
http://developer.android.com/guide/topics/manifest/manifest-intro.html

Cap comentari :

Publica un comentari a l'entrada