Autore Topic: "Meccanismi" multithread.. quali usare?  (Letto 1767 volte)

Offline crc_error

  • Utente junior
  • **
  • Post: 85
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Windows 8, Ubuntu 12
"Meccanismi" multithread.. quali usare?
« il: 19 Marzo 2013, 13:05:50 CET »
0
Ciao a tutti,
sono alle quasi prime armi con android e java.

Sto affrontando il capitolo Thread, Runnable, Asynctask, IntentService, ecc..
Insomma, in poche parole: come lanciare una operazione senza bloccare la UI.

Vorrei capire bene come funzionano questi meccanismi, quali sono i loro pregi e loro limiti.
In giro si trovano parecchi tutorial su come implementarli ma non ho trovato niente che spieghi quando preferirne uno piuttosto che un altro.

Per chiarirmi le idee, e fissarmi in testa il tutto, vorrei creare una applicazione semplice semplice.
Questa app avrà 2 pulsantini (avvia e ferma). Il pulsante "avvia" farà partire un servizio che attiverà un locationmanager, che, tramite un locationlistener mi ritornerà un oggetto location ogni tot tempo.
I dati contenuti in questo oggetto, verranno memorizzati in una tabella di sqlite e usati per aggiornare la mappa e delle textview nell'activity (se è visibile).

Il processo principale, resterà in esecuzione fino a che non verrà premuto il pulsante "ferma".

Qual'è secondo voi, il miglior modo per gestire una app del genere?

Inizialmente pensavo di usare gli AsyncTask. 3 in totale (location manager, inserisci_nel_db, aggiorna_la_activity), ma poi ho letto nella doc ufficiale: AsyncTasks should ideally be used for short operations (a few seconds at the most.)
E poco più avanti, raccomanda di usare Executor, ThreadPoolExecutor o FutureTask.

La faccenda si complica..

Potrei continuare sulla mia strada, e usarne solo 2 (per aggiornare la activity e il db), e farli nascere e morire ad ogni nuova posizione ricevuta.
Ma per il processo principale? Cosa uso?

(In teoria, fino a che l'utente non preme il pulsante ferma, potrei continuare a ricevere posizioni aggiornate anche per giorni e giorni... )

Qualcuno riesce a chiarirmi un po' le idee?

Grazie mille :)

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:"Meccanismi" multithread.. quali usare?
« Risposta #1 il: 19 Marzo 2013, 16:06:54 CET »
0
Il main thread è quello della UI e non "dovrebbe" essere ingolfato da operazioni pesanti.

Le operazioni pesanti è bene siano eseguite in un thread a parte, implementabile come Thread (più a basso livello) o come AsyncTask (che usa i Thread con qualche automazione in più).

Il tuo caso presenta almeno una ulteriore complessità: la necessità di avere un servizio (cioè qualcosa che persista nel tempo più di qualche secondo) che comunica con l'activity.


Visto sei alle prime armi, io ti consiglio di esplorare ciascuno di questi argomenti separatamente per metterli insieme in un secondo momento. Comincia con qualche tutorial sugli asynctask, poi magari prova qualcosa sui service.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline crc_error

  • Utente junior
  • **
  • Post: 85
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Windows 8, Ubuntu 12
Re:"Meccanismi" multithread.. quali usare?
« Risposta #2 il: 20 Marzo 2013, 09:43:20 CET »
0
Ciao, grazie per la risposta!

Per ora ieri mi sono limitato a fare "l'interfaccia" (se così si può chiamare visto che ci sono 2 bottoni e una textview) e il servizio che implementa il location manager.

Ora dovrei far comunicare il servizio con l'attività. Ho visto che ci sono parecchi modi Broadcast receiver, Handler, ecc.. secondo te qual'è il migliore?

Il "comunicatore" è meglio "incapsularlo" (passami il termine) in un thread (che nasce e muore ogni volta che finisce di aggiornare l'attività) o no?

Grazie ancora

Ps. ho letto buona parte dei tuoi tutorial, davvero bravo :D grazie anche per quelli ;)

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:"Meccanismi" multithread.. quali usare?
« Risposta #3 il: 20 Marzo 2013, 09:51:36 CET »
0
Io personalmente per comunicare consiglio sempre i messaggi.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline crc_error

  • Utente junior
  • **
  • Post: 85
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Windows 8, Ubuntu 12
Re:"Meccanismi" multithread.. quali usare?
« Risposta #4 il: 20 Marzo 2013, 10:06:25 CET »
0
con gli Handler intendi?

Offline crc_error

  • Utente junior
  • **
  • Post: 85
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Windows 8, Ubuntu 12
Re:"Meccanismi" multithread.. quali usare?
« Risposta #5 il: 25 Marzo 2013, 22:50:30 CET »
0
Rieccomi qua.

Sto cercando di capire come funzionano gli AsyncTask.

Nella class main ho creato asynctask per alleggerire il thread UI.

In pratica la mia inner class esegue una query nel db sqlite e stampa il risultato in una listview con un simplecursoradapter e viewbinder.

Funziona tutto correttamente. Nel locat però ho sempre questa stringa:

Citazione
Skipped 55 frames!  The application may be doing too much work on its main thread.

Sia che ci sia 1 riga da impaginare o 100 è quasi sempre presente, i frame saltati vanno da 20 a 150..

Da che dipende? E' il telefono che ha un HW limitato o c'è qualche cosa nel codice?

Grazie mille

Codice (Java): [Seleziona]
private class TestP extends AsyncTask<Void, Integer, SimpleCursorAdapter> {

                Cursor upd_listaTk;
               
                @Override
                protected void onPreExecute() {
                        pd=ProgressDialog.show(MainActivity.this,"","Please Wait");            
                }
               
                @Override
                protected SimpleCursorAdapter doInBackground(Void... arg0) {
                        SimpleCursorAdapter listaAdapter = null;
                        upd_listaTk = dbTrk.rawQuery(// - QUERY - );           

                       
                        if(upd_listaTk.moveToFirst() == true) {

                                String[] colonne = new String[] { COL1, COL2, COL3 };
                                int[] to = new int[] { R.id.tvCOL1, R.id.tvCOL2, R.id.tvCOL3 };
                               
                                listaAdapter = new SimpleCursorAdapter(thisContext, R.layout.lv_main, upd_listaTk, colonne, to, 0);
                               
                                listaAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
                               
                                        @Override
                                        public boolean setViewValue(View view, Cursor cursor, int colInd) {
                                                int nDistIndx = cursor.getColumnIndexOrThrow(COL2);
                                                if(colInd == nDistIndx) {
                                                        TextView tv = (TextView) view;
                                                        double dt = cursor.getDouble(cursor.getColumnIndex(COL2));
                                                        //dt = Math.rint(dt / 1000);
                                                        String dtS = String.valueOf(dt);
                                                        tv.setText(dtS);
                                                        return true;
                                                }
                                                return false;
                                        }
                                });
                        }
                        return listaAdapter;
                }
               
                @Override
                protected void onProgressUpdate(Integer...progress) {
                    Log.i("stato", "stato - avanzamento: " + progress[0] + "%");
                }
               
                @Override
            public void onPostExecute(SimpleCursorAdapter alistaAdapter) {
                        Log.i("query", "stato - fine recupero lista");
                pd.dismiss();
                exvTracce.setAdapter(alistaAdapter);
            }
        }
« Ultima modifica: 25 Marzo 2013, 22:54:15 CET da crc_error »

Offline lorux

  • Utente junior
  • **
  • Post: 97
  • Respect: +12
    • Google+
    • lor.catalano
    • Mostra profilo
  • Dispositivo Android:
    Galaxy S2
  • Sistema operativo:
    Windows
Re:"Meccanismi" multithread.. quali usare?
« Risposta #6 il: 26 Marzo 2013, 00:14:09 CET »
0
da quanto leggo in giro non è rilevante come errore se ti esce usando l'emulatore,xkè è di una lentezza disarmante.
se invece usi un cellulare beh è brutto XD
per sicurezza puoi postare il pezzo di codice dove è usata l'asynctask così vediamo come è stata usata?

cmq non sono pratico di cursoradapter,ma correggimi se sbaglio,devi per forza usare l'asynctask e fare l'override del setViewBinder del simplecursoadapter?mi spiego meglio..

Codice (Java): [Seleziona]
String[] colonne = new String[] { COL1, COL2, COL3 };
                                int[] to = new int[] { R.id.tvCOL1, R.id.tvCOL2, R.id.tvCOL3 };
questi array indicano quali delle tue colonne devono "andare a finire" nelle view con quei id.
Se tu hai  3 colonne ad esempio _id,nome,cognome e nel layout della riga (R.layout.lv_main) hai 3 textview con id R.id.personaid,R.id.personanome,R.id.personacognome e all'adapter passi questi 2 array,il bind lo fà automaticamente,non c'è bisogno di scrivere codice..

comunque puoi dichiarare e settare l'adapter(con cursor null) fuori dalla task e alla task fai tornare il cursor,poi nel postexecute lo setti all'adapter

Offline crc_error

  • Utente junior
  • **
  • Post: 85
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Windows 8, Ubuntu 12
Re:"Meccanismi" multithread.. quali usare?
« Risposta #7 il: 26 Marzo 2013, 10:56:28 CET »
0
Il problema dei frame saltati si presenta quasi sempre e il numero varia.
Non uso l'emulatore ma uno smartphone, è un telefono della fine 2010 ed è stato moddato e aggiornato dalla ginger alla 4.2.1... come dicevo sopra, magari è l'HW datato che non ce la fa..

Per quanto riguarda il cursor:

Ho usato l'asynctask per 2 motivi, il primo per capirne i meccanismi (visto che sono agli inizi) il secondo invece è per non appesantire troppo il thread UI.

In pratica con l'async faccio la query e metto il risultato in un cursore. Il risultato viene poi bind-ato (passami il termine) con le view nella listview.

Potrei usare tranquillamente un SimpleCursorAdapter e far fare tutto a lui ma ho bisogno di formattare i risultati presi dal db (ho degli ingeger da convertire in date, e dei double da arrotondare e limitare a 2 decimali).

Attualmente sto facendo delle prove per capire se è più performante far fare queste operazioni a sqlite in fase di query oppure a Java

P.e.
Codice (Java): [Seleziona]
"DATETIME("+DbPosHelper.TBT_DATAINIZIO+", 'unixepoch', 'localtime') AS dInizio
Nella query che poi metto nel cursor, oppure usare il "Date" di Java.

Il codice che uso per lanciare l'async è questo:

Codice (Java): [Seleziona]
        private static TestP testAsynk;

        @Override
        protected void onStart() {
                super.onStart();
                testAsynk = new TestP();
                testAsynk.execute();
                Log.i("stato", "stato START idTraccia: " + idTraccia + " tracciaAperta: " + tracciaAperta);
        }

Ho provato a metterlo anche nell'onCreate e nel onResume, ma nello onStart ho meno frame skypped, forse perchè nell'onCreate deve già creare la UI,  l'Helper per il DB, ecc..
« Ultima modifica: 26 Marzo 2013, 11:00:26 CET da crc_error »

Offline lorux

  • Utente junior
  • **
  • Post: 97
  • Respect: +12
    • Google+
    • lor.catalano
    • Mostra profilo
  • Dispositivo Android:
    Galaxy S2
  • Sistema operativo:
    Windows
Re:"Meccanismi" multithread.. quali usare?
« Risposta #8 il: 26 Marzo 2013, 13:50:58 CET »
0
cmq secondo me ci stiamo confondendoXD

non penso che l'asynctask serva,il motivo è che(credo)il cursoradapter esegue la query nel momento in cui ha bisogno di visualizzare le righe della lista(quando crea quelle visualizzate all'inizio e quando scrolli la lista stessa).

hai provato a fare solo creazione del cursor->creazione dell'adapter nell'oncreate?


se invece vuoi tirare su tutte le righe dalla tua tabella e piazzarle nell'adapter allora il discorso è diverso

Offline crc_error

  • Utente junior
  • **
  • Post: 85
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Windows 8, Ubuntu 12
Re:"Meccanismi" multithread.. quali usare?
« Risposta #9 il: 26 Marzo 2013, 14:53:58 CET »
0
Quello che dovrei fare:
 - è recuperare i dati dal database e metterli nel cursor
 - prendere i dati nel cursor, formattarli e metterli nel cursoradapter
 - riempire una listview con i dati del cursoradapter.

In teoria potrebbe essere una cosa da delegare ad un thread separato, in modo che l'app non sembri impallata e che non esca l'alert attendi/termina dopo i 5 sec..

Sono riuscito a completare tutti i punti, ma nel logcat ogni tanto (2 volte su 3 più o meno), ho quella info di "frame skipped" in "Choreographer".

Ho provato a fare come di ci tu ma.. Niente da fare..
Ho provato a far creare il cursor nell'onCreate e poi passarlo all'asyncTask ma niente da fare, i frame skipped da "Choreographer" ci sono comunque.
Inoltre con questo metodo ho anche un ritardo nella creazione dell'UI visto che il Thread UI deve fare anche la query e metterla nel cursor.

Ho anche provato a eliminare l'async task prendendo il codice in doInBackground() e facendolo diventare una funzione da chiamare all'onCreate. In questo caso, oltre ad avere "frame skipped" costanti (questa volta ci sono sempre e sempre maggiori di 30), ho ancora più ritardo nella creazione della UI.

Dopo varie prove:
- elaborazione dei dati nella query
- elaborazione dei dati da java
- binding dei dati senza elaborazioni
- tolto l'override su setViewValue()
- limitato il risultato della query a 1
- limitato il risultato della query a 0

La info Frame Skipped c'era sempre...

Poi ho trovato la causa...

Codice (Java): [Seleziona]
           @Override
            public void onPostExecute(SimpleCursorAdapter alistaAdapter) {
                        Log.i("query", "stato - Percorsi fine recupero lista");
                pd.dismiss();
                exvTracce.setAdapter(alistaAdapter);  // <-- E' lui che ritarda
            }

onPostExecute è eseguito sul Thread UI.
Quando vado a fare il binding del list adapter sulla listview crea un lag che fa skippare i frame... se lo commento le righe possono essere anche 200 ma non ho lag.

Allego i 2 xml, magari c'è qualcosa di non corretto li...

Activity
Codice (XML): [Seleziona]
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/LinearLayout1"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context=".MainActivity" >

    <ListView
       android:id="@+id/exvTracce"
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="0.9" >
    </ListView>  

    <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="horizontal" >

        <Button
           android:id="@+id/btnFerma"
           android:layout_width="0dp"
           android:layout_height="wrap_content"
           android:layout_weight="0.5"
           android:text="@string/btnFermaServizio" />

        <Button
           android:id="@+id/btnAvvia"
           android:layout_width="0dp"
           android:layout_height="wrap_content"
           android:layout_weight="0.5"
           android:text="@string/btnAvviaServizio" />
    </LinearLayout>

</LinearLayout>

XML per il cursoradapter
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:orientation="horizontal"
   android:baselineAligned="false">

    <LinearLayout
       android:layout_width="0dp"
       android:layout_height="match_parent"
       android:orientation="vertical"
       android:layout_weight="0.7" >

        <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text="@string/tvNomeTraccia"
           android:textSize="12sp" />
         
        <TextView
           android:id="@+id/tvNomeTraccia"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text=""
           android:textSize="24sp" />

        <TextView
           android:id="@+id/tvDataInizioTraccia"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text=""
           android:textSize="14sp" />
       
    </LinearLayout>
    <LinearLayout
       android:layout_width="0dp"
       android:layout_height="match_parent"
       android:orientation="vertical"
       android:layout_weight="0.3" >

        <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text="@string/tvDistanza"
           android:textSize="12sp" />        
 
        <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text="@string/tvUm"
           android:textSize="14sp" />
       
        <TextView
           android:id="@+id/tvDistTraccia"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text=""
           android:textSize="24sp" />

    </LinearLayout>    
</LinearLayout>


Come posso risolvere?
Se portassi l'asynctask in un file esterno e sul metodo onPostExecute usassi un handler che manda alla activity il listadapter?




Offline lorux

  • Utente junior
  • **
  • Post: 97
  • Respect: +12
    • Google+
    • lor.catalano
    • Mostra profilo
  • Dispositivo Android:
    Galaxy S2
  • Sistema operativo:
    Windows
Re:"Meccanismi" multithread.. quali usare?
« Risposta #10 il: 27 Marzo 2013, 00:43:38 CET »
0
beh se commenti il setAdapter la tua listview è vuota,x forza non ci sono lag,non sta renderizzando niente..
anche io uso il setadapter nel postexecute e non ho problemoni..

ora ho provato un po' a stressare la mia app,stando in debug e scrollando velocissimo la lista sono riuscito a fargli skippare dei frame,io ho questa view per un singolo elemento della lista:
textview
textview(faccio il parse di una data e concateno 3 stringhe)
textview con una somma e un if
textview con un if che testa quale dei 6 colori mostrare

quindi vabbè rispetto toh alla rubrica dove si vede solo una textview col nome del contatto e un'immagine caricata lazy la mia fà un po' + di lavoro

considera anche che io la lista la carico tutta prima e poi creo l'adapter,quindi a sto punto direi che quel warning(anzi logcat lo logga come info) dipende esclusivamente da quanto scrolli e quanti elementi alla volta deve renderizzare

considera pure che se sei in debug o cmq attaccato usb a eclipse và tutto un pochino più lento

boh non sò + che dirtiXD
ti chiedo una cosa,ma xkè fai questo if?(tralasciando l' == true che nun se po' vedè) XD
Codice (Java): [Seleziona]
if(upd_listaTk.moveToFirst() == true) {

Offline crc_error

  • Utente junior
  • **
  • Post: 85
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Windows 8, Ubuntu 12
Re:"Meccanismi" multithread.. quali usare?
« Risposta #11 il: 27 Marzo 2013, 11:11:21 CET »
0
Ciao, ti quoto un po' 'a tranci' :D

Citazione
beh se commenti il setAdapter la tua listview è vuota,x forza non ci sono lag,non sta renderizzando niente..
anche io uso il setadapter nel postexecute e non ho problemoni..

Era per capire se il problema era nella fase di recupero dati (query e "formattazione") oppure se era nella fase di presentazione.
Il fatto che non ho lag se commento il setAdapter(), vuol dire che il problema è proprio li...
Ieri poi ho fatto diverse prove e il fatto che salti dei frame non sembra rompere le scatole, l'UI rimane sempre abbastanza fluida...

Citazione
ora ho provato un po' a stressare la mia app,stando in debug e scrollando velocissimo la lista sono riuscito a fargli skippare dei frame,io ho questa view per un singolo elemento della lista:

Che che device la stai provando? Perchè se è l'S2 che hai ne "Dispositivo Android:" non c'è neanche da paragonarlo col mio LG.. Sarebbe come paragonare una F1 con una Bianchina..  :D
Il mio era uno dei più economici android del 2010.. Avrà dentro la stessa CPU di una calcolatrice.. :D

Citazione
ti chiedo una cosa,ma xkè fai questo if?(tralasciando l' == true che nun se po' vedè) XD

Beh.. sono ancora in fase Test/CapendoCosaSuccede.
L'if "== true" o "== false" piuttosto che "if(bool)" o "if(!bool)", mi rende più veloce la lettura del codice. Poi quando avrò finito darò una pettinata al codice  ;-)

L'if in questione l'ho messo per assicurarmi che il cursore sia sulla prima riga (spostandolo nel caso non lo sia). Non serve?

Grazie ancora ciao


Offline blackgin

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 1387
  • Respect: +164
    • Google+
    • blackgins
    • blackginsoft
    • Mostra profilo
  • Dispositivo Android:
    Galaxy Nexus
  • Sistema operativo:
    Mac OSX 10.8
Re:"Meccanismi" multithread.. quali usare?
« Risposta #12 il: 27 Marzo 2013, 12:02:08 CET »
0
L'if in questione l'ho messo per assicurarmi che il cursore sia sulla prima riga (spostandolo nel caso non lo sia). Non serve?
Ti servirebbe se recuperassi i dati manualmente, ma dato che lo fa l'adapter é superfluo.
Postate il LogCat LogCat LogCat LogCat LogCat