Media Playback
El framework multimèdia d'Android inclou suport per a la reproducció de diversos tipus de mitjans, de manera que se li pot integrar fàcilment àudio, vídeo i imatges a les aplicacions.
Els recursos van al directori RAW.
L'àudio té més joc que el vídeo... Si s'intenta fer un streaming per Bluetooth, com marques la prioritat per a l'àudio? Tenim l'AudioManager que permet manipular tot el que afecta al tema de l'àudio.
En tots dos casos (àudio o vídeo) s'han de posar permisos si volem fer streaming.
<uses-permission android:name="android.permission.INTERNET" />
Si l'aplicació s'ha de mantenir en pantalla es pot fer servir MediaPlayer.setScreenOnWhilePlaying() o els mètodes MediaPlayer.setWakeMode(), amb els seus permisos:
<uses-permission android:name="android.permission.WAKE_LOCK" />
La classe MediaPlayer permet obtenir, descodificar i reproduir àudio i vídeo. URLs internes, llistes d'àudio, fotos... i URLs externes (streaming).
Els formats suportats es poden veure a:
http://developer.android.com/guide/appendix/media-formats.html
Si volem reproduir un fitxer en local (RAW) haurem de desar-lo al directori /res/raw:
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.audio1);
mediaPlayer.start();
Si volem fer servir el tema de l'streaming, haurem de posar:
String url = "http://........";
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // pot trigar a carregar en el buffer.
mediaPlayer.start();
El mètode prepare() no es pot cridar mai des del fil de la IGU (Interfície Gràfica d'Usuari). Imagina't que fas una sopa de lletres i vols música de fons, si ho fas directament a l'activity bloquejaria la interfície fins que es carregui el buffer, la solució és ficar-ho en un servei.
Hi ha un mètode anomenat prepareAsync() que crearia un servei de forma automàtica. Caldrà un listener, que cridarà un callback i es dispararà quan estigui carregat.
El MediaPlayer funciona com una màquina d'estats.
http://cfile23.uf.tistory.com/original/191F6F184C1E1BBE07D17F
Un cop s'ha utilitzat un mitjà, cal alliberar-lo amb el mètode release(), és important tenir en compte que quan l'usuari canvia l'orientació es torna a crear un objecte MediaPlayer, perquè es torna a cridar a onCreate(), per això cal alliberar els recursos.
media.release();
media=null;
Si no volem aquest comportament podem desar l'estat i gestionar-lo de manera manual.
Fent que quan es produeix un canvi d'orientació o el teclat s'oculta, aleshores executi el mètode per defecte que detecta canvis de configuració (onConfigurationChanged()) i es dispararà tenint-lo posat a l'activity. L'objecte newConfig tindrà la informació sobre la qual podràs consultar l'estat.
Pugent mirar l'orientació podrem fer condicionals i permet detectar que es dispararà quan hi ha un canvi.
Després cridarà a onCreate().
Per exemple, per fer un canvi d'orientació:
Codi:
`
<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>
`
Com es pot veure, hem afegit android:configChanges="orientation|keyboardHidden|screenSize" perquè detecti la rotació de pantalla o l'ocultació de teclat en aquest cas; perquè això funcioni haurem de sobreescriure una funció:
` 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 únicament funcionarà si està declarat al manifest; d'aquesta manera, podem comprovar la nova orientació i la comparem per saber si és horitzontal o vertical (Landscape o Portrait). D'aquesta manera mostrem el toast de l'exemple inicial indicant el tipus d'orientació.
Serveis
Els serveis són components d'una aplicació que realitzen operacions de llarga durada que no requereixen la interacció d'un usuari, o que ofereixen funcionalitats per a altres aplicacions. Normalment envien correus, sincronitzen dades o reprodueixen mitjans.
Igual que hi ha una activity al manifest, haurem de declarar també els serveis al manifest. La comunicació és similar al sistema de comunicació que tenen les activitats, els intents. Els serveis hereden de Service o una subclasse d'aquest. No tenen mai interfície.
Cada servei es declararà a l'AndroidManifest.xml amb <service>.
Els serveis s'inicien amb:
Context.startService() i Context.bindService().
Els serveis s'executen com a fil principal del procés. Això implica que si consumeix molts recursos es recomana crear-ne un de nou.
La classe IntentService és una implementació estàndard de Service que funciona com un fil propi.
Així doncs, un servei no és un procés separat. S'executa en el mateix procés que l'aplicació. Tampoc és un fil separat.
Els serveis s'utilitzen per fer tasques en segon pla, notificant quan s'han realitzat. S'inicia amb Context.startService(). Aquest concepte és molt similar al d'activityResult.
Existeix la possibilitat d'exposar a altres aplicacions part d'una aplicació en concret. S'utilitza el mètode de classe Context.bindService().
Podem especificar que el servei estigui associat a un altre procés amb l'anotació de dos punts (:). Els dos punts indiquen que el procés serà privat per a l'aplicació; si no, serà global per a les aplicacions.
Per exemple:
`
<service
android:name="WordService"
android:process=":proces1"
android:icon="@drawable/icon"
android:label="@string/nom_servei"
</service>
`
Un altre exemple:
` 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();
}
} `
Caldrà controlar els errors que es produeixen de manera asíncrona.
A onStartCommand li passem un intent amb la pista d'àudio que volem que soni, amb l'ACTION_PLAY sabrem si està sonant o no la música. En passar-li l'intent, aquest li envia la pista d'àudio que nosaltres volem reproduir. Una de les coses a tenir en compte és que podem tenir errors, per la qual cosa hi ha una altra interfície que es diu Media.Player.OnErrorListener, on fent override a onError el podrem configurar.
Et pots baixar un exemple d'un servei que permet descarregar:
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. } }*
Tindrà un botó que diu descàrregues, i un toast, mentre tant podem fer el que vulguem, quan estigui descarregat, el servei acabarà. Si li dones un altre cop al botó la descàrrega es torna a realitzar.
Mira el codi o descarrega't el zip.
Primer tenim l'IntentService.
A l'onCreate només cridem al layout.
A l'onClick fem un intent del servei, creem un missatge al qual li passem un handler i aquest s'encarregarà de veure si està descarregat o no. Això és perquè el handler és asíncron, per tant, no bloqueja res. Quan rebi el missatge es dispararà el handleMessage, si el resultat està bé sortirà correcte, només quan rep.
A l'intent li passem un missatge i el missatger, a l'intent també li passem un setData, que s'utilitza per obtenir el nom del fitxer. El putExtra serà precisament per poder indicar el path per saber on s'ha de desar.
El servei té un enter, result, que si tot va bé li posarem okey en comptes de result_canceled. A l'onHandleIntent, que és asíncron, obtenim l'urlpath i d'aquesta manera posem el nom del fitxer i fem un fitxer output per desar, si no el borrarem.
La lectura la farem amb l'InputStreamReader, en un principi fluxEntrada i fos seran iguals. Tècnicament el que fa és escriure'ls byte a byte, quan és -1 significa que cal tancar l'arxiu.
Posem la ruta, connexió, i l'InputStreamReader va posant els bytes i els converteix a UNICODE. Si és pdf o alguna altra cosa cal anul·lar els readers que els converteixen a UNICODE. Normalment es diuen fis i fos.
Després tenim un while que llegeix caràcter a caràcter i si no és igual a menys 1 escriu (Per a un pdf necessitarem eliminar-ho).
Quan acaba li posem result_ok i al .java veurem que ha retornat RESULT_OK. Si hi ha problemes hi ha un catch, i el finally s'executa tant si finalitza correctament com si hi ha una fallada.
Quan ja està, agafem el missatge i li posem com a argument 1 que tot ha anat correctament i com a objecte li posem la ruta de l'objecte.
Si hi ha algun problema et envia la resposta.
Un servei pot estar engegat mentre l'activitat està en pausa, però la CPU funcionaria i el wifi no, aleshores li podem programar què volem que faci. Per desactivar-ho o canviar-ho hem de posar wifiLock.acquire() o wifiLock.release(); d'aquesta manera controlem si funciona o no. Es posaria setWakeMode per indicar què ha de fer.
Si volem també es pot mostrar una notificació. Es programa amb Notification, per fer-ho s'han d'enviar mitjançant un intent especial, amb PendingIntent. Allí hi podrem posar la informació.
Activitats:
- Desenvolupa una aplicació per a Android que reprodueixi una pista de música desada als recursos del dispositiu. L'aplicació ha de tenir botons per aturar, pausar i iniciar la pista de música. Ha de mostrar el temps de reproducció. Solució 1. Solució profe.
- A partir de l'exercici anterior, crea un servei en primer pla que realitzi la reproducció de música mostrant una notificació d'informació a l'usuari a la barra d'estat. Solució.
Per a més informació, consulteu:
http://developer.android.com/guide/topics/manifest/manifest-intro.html


