bytes.cat

La wiki d'FP d'informàtica

Eines de l'usuari

Eines del lloc


android_threads

Diferències

Ací es mostren les diferències entre la revisió seleccionada i la versió actual de la pàgina.

Enllaç a la visualització de la comparació

Ambdós costats versió prèvia Revisió prèvia
Següent revisió
Revisió prèvia
android_threads [2022/10/10 09:28]
enrique_mieza_sanchez [Conceptes previs]
android_threads [2023/10/17 14:36] (actual)
enric_mieza_sanchez [Exercicis]
Línia 2: Línia 2:
  
 Aclarirem certs conceptes de //threading// aplicats a Android. Aclarirem certs conceptes de //threading// aplicats a Android.
 +
 +{{ android:androidthreads.jpg?450 }}
  
 Referències: Referències:
   * [[Android]]   * [[Android]]
-  * [[Android imatges]] 
   * [[Android ListView]]   * [[Android ListView]]
  
Línia 39: Línia 40:
 <file java> <file java>
 ExecutorService executor = Executors.newSingleThreadExecutor(); ExecutorService executor = Executors.newSingleThreadExecutor();
-Handler handler = new Handler(Looper.getMainLooper()); 
  
 executor.execute(new Runnable() { executor.execute(new Runnable() {
Línia 45: Línia 45:
     public void run() {     public void run() {
  
-        //Background work here+        // Tasques en background (xarxa)
  
 +        Handler handler = new Handler(Looper.getMainLooper());
         handler.post(new Runnable() {         handler.post(new Runnable() {
             @Override             @Override
             public void run() {             public void run() {
-                //UI Thread work here+             
 +                // Tasques a la interfície gràfica (GUI) 
 +                
             }             }
         });         });
Línia 84: Línia 87:
 Teniu [[https://medium.com/@yossisegev/understanding-activity-runonuithread-e102d388fe93|aquest article]] que explica força coses de com funcionen els objectes ''Handler'', ''Looper'', ''Message'' i altres. Teniu [[https://medium.com/@yossisegev/understanding-activity-runonuithread-e102d388fe93|aquest article]] que explica força coses de com funcionen els objectes ''Handler'', ''Looper'', ''Message'' i altres.
  
 +\\
 +
 +===== Threads de comunicació =====
 +
 +Una aplicació típica és la utilització de //threads// per a comunicació. Tal i com hem dit, no podem executar funcions que bloqueginen el mainUIthread d'Android, i les comunicacions ho fan en tant que esperen la resposta remota.
 +
 +Posem aquest codi d'una crida HTTP en una funció ''getDataFromUrl'' que podem afegir a la nostra ''MainActivity''. Aquí tens més [[https://www.twilio.com/blog/5-ways-to-make-http-requests-in-java|info sobre llibreries per a crides HTTP en Java]].
 +
 +Si l'executem dins el ''MainThread'' ens saltarà una excepció ''NetworkOnMainThreadException''. Per això necessitem el codi del //thread// de mes amunt per poder-la executar.
 +
 +<file java MainActivity.java>
 +    String error = ""; // string field
 +    private String getDataFromUrl(String demoIdUrl) {
 +
 +        String result = null;
 +        int resCode;
 +        InputStream in;
 +        try {
 +            URL url = new URL(demoIdUrl);
 +            URLConnection urlConn = url.openConnection();
 +
 +            HttpsURLConnection httpsConn = (HttpsURLConnection) urlConn;
 +            httpsConn.setAllowUserInteraction(false);
 +            httpsConn.setInstanceFollowRedirects(true);
 +            httpsConn.setRequestMethod("GET");
 +            httpsConn.connect();
 +            resCode = httpsConn.getResponseCode();
 +
 +            if (resCode == HttpURLConnection.HTTP_OK) {
 +                in = httpsConn.getInputStream();
 +
 +                BufferedReader reader = new BufferedReader(new InputStreamReader(
 +                        in, "iso-8859-1"), 8);
 +                StringBuilder sb = new StringBuilder();
 +                String line;
 +                while ((line = reader.readLine()) != null) {
 +                    sb.append(line).append("\n");
 +                }
 +                in.close();
 +                result = sb.toString();
 +            } else {
 +                error += resCode;
 +            }
 +        } catch (IOException e) {
 +            e.printStackTrace();
 +        }
 +        return result;
 +    }
 +</file>
 +
 +Per poder-se connectar a internet, cal [[https://developer.android.com/training/basics/network-ops/connecting|activar el permís Android d'accés a Internet]]:
 +
 +<file xml AndroidManifest.xml>
 +<uses-permission android:name="android.permission.INTERNET" />
 +<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 +</file>
 +
 +\\
 +
 +==== APIs i JSON ====
 +Les crides a APIs externes ens retornaran objectes JSON que cal descodificar. Per exemple, pots provar ''https://api.myip.com'' per veure la teva IP. Des de la //shell// fariem:
 +  $ curl https://api.myip.com
 +  {"ip":"139.47.113.84","country":"Spain","cc":"ES"}
 +
 +Es recomana aquest [[https://stackoverflow.com/questions/16574482/decode-json-string-in-java-with-json-simple-library|exemple per descodificar els missatges JSON]].
 +
 +<WRAP important>
 +Ull, perquè si el server de **myip.com** rep moltes peticions seguides **acaba per bloquejar-se ja que es pensa que som atacants**.
 +
 +Si veieu que amb el CURL no funciona, cerqueu una altra API. Cerqueu alguna que us agradi d'aquest [[https://github.com/public-apis/public-apis|llistat de public APIs]]. Per exemple, aquesta ens retorna la URL d'una imatge aleatòria de guineus:
 +  $ curl https://randomfox.ca/floof/
 +
 +</WRAP>
 +
 +\\
 +
 +===== Exercicis =====
 +
 +<WRAP todo>
 +Llençar comunicacions des d'un //thread// alternatiu:
 +  - Crea un nou projecte [[Android]] amb un botó.
 +  - Afegeix la funció ''getDataFromUrl'' i crida-la amb una URL (per exemple ''https://api.myip.com'') al prémer el botó, directament des del ''onClick''.
 +  - Comprova que ens salta l'excepció ''NetworkOnMainThreadException''.
 +  - Afegeix el codi amb ''Executor'' i crida la funció de xarxa per obtenir les dades d'una URL. Mostra les dades per la consola de //debug// amb ''Log.i''.
 +  - No oblidis activar el permís Android per a accés a Internet o obtindràs una altra excepció.
 +</WRAP>
 +
 +
 +<WRAP todo>
 +Actualitzar la GUI:
 +  - Afegeix un ''TextView'' en el projecte.
 +  - Quan carreguem dades d'internet, actualitza-les en el ''TextView''. Comprova que si ho fem en el mateix cos del //thread//, la funció ''run'', funciona.
 +
 +Anem a posar alguna acció gràfica que ens obligui a utilitzar el ''Handler'', que serà el què ens permetrà executar en el //thread// principal (el de gràfics).
 +
 +Farem servir una ''ImageView'':
 +  - Afegeix la ''ImageView'' al //layout//.
 +  - Descarrega una imatge d'internet i transforma-la en ''Bitmap''. Ho podem fer així <code java>
 +String urldisplay = "https://randomfox.ca/images/122.jpg";
 +Bitmap bitmap;
 +try {
 +    InputStream in = new java.net.URL(urldisplay).openStream();
 +    bitmap = BitmapFactory.decodeStream(in);
 +} catch (Exception e) {
 +    Log.e("Error", e.getMessage());
 +    e.printStackTrace();
 +}
 +</code>
 +  - Mostra la imatge al ''ImageView''. Comprova que al fer el ''imageView.setImageBitmap(bitmap)'' ens falla amb una excepció si ho fem al //thread// de comunicacions.
 +  - Utilitza el ''Handler'' com a l'exemple i comprova que ara canvia la imatge i no peta.
 +
 +Si et queda temps, crida la API randomfox (explicada mes amunt) i obtingues una imatge diferent cada cop, i mostra-la al ''ImageView''.
 +
 +</WRAP>
 +
 +
 +Per si et resulta avorrit, mes feina (exercici optatiu):
 +
 +<WRAP todo>
 +Anem a provar amb una GUI més ambiciosa: una ''ListView''.
 +  - Afegeix una [[Android ListView]] al //layout//.
 +    * Pots provar amb una simple ''ArrayList<String>'' com a model per no haver de fer //layouts// personalitzats.
 +  - Afegeix //items// al model quan premem el botó.
 +  - Comprova que al refrescar el GUI des del //thread// de comunicacions amb<code java>
 +    adapter.notifyDataSetChanged();
 +</code> ens surt una ''ViewRootImpl$CalledFromWrongThreadException''.
 +  - Posa el refresc del GUI dins el ''handler.post'' i comprova que funciona.
 +</WRAP>
 +
 +\\
  
android_threads.1665394104.txt.gz · Darrera modificació: 2022/10/10 09:28 per enrique_mieza_sanchez