2013-06-05 8 views
20

Sto lavorando su un'app che dovrebbe ricevere una risposta JSON da un webservice e scrivere ogni elemento in una listview , ho letto che dovrei lavorare con AsyncTask per ottenere la risposta HTTP e l'ho fatto e ho potuto recuperare i dati dal webservice e visualizzarli in TextViews. Ma quando cerco di visualizzare gli elementi in una lista non visualizza nulla e mi dà il seguente messaggio nel logcat: 06-05 19:44:27.418: I/Choreographer(20731): Skipped 60 frames! The application may be doing too much work on its main thread.Ha saltato 60 fotogrammi! L'applicazione potrebbe fare troppo lavoro sul suo thread principale

Ecco il mio codice principale:

public class MainActivity extends Activity { 

    private static JsonObject response = new JsonObject(); 
    private ArrayList<SearchResults> results = new ArrayList<SearchResults>(); 
    private SearchResults sr1 = null; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     new LoginAction().execute(""); 

     ArrayList<SearchResults> searchResults = results; 
     final ListView lv1 = (ListView) findViewById(R.id.ListView01); 
     lv1.setAdapter(new MyCustomBaseAdapter(this, searchResults)); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     // Inflate the menu; this adds items to the action bar if it is present. 
     getMenuInflater().inflate(R.menu.main, menu); 
     return true; 
    } 

    private class LoginAction extends AsyncTask<String, Void, String> { 

     @Override 
     protected String doInBackground(String... params) { 

      Map<String, String> callArgs = new HashMap<String, String>(1); 

      callArgs.put("suuid", "dtr0bdQGcqwSh3QO7fVwgVfBNWog6mvEbAyljlLX9E642Yfmur"); 

      try { 
       response = EventPulseCloud.call("ListEvents", callArgs); 
      } catch (HttpClientException e) { 
       e.printStackTrace(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } catch (JsonException e) { 
       e.printStackTrace(); 
      } 

      return response.get("Type").toString(); 
     } 

     protected void onPostExecute(String result) { 

      if(result.equals("success")) { 
       JsonArray records = null; 
       try { 
        records = response.getObject ("Data").getArray ("Records"); 
       } catch (JsonException e) { 
        e.printStackTrace(); 
       } 

       for(int i = 0; i < records.count(); i++) { 
        JsonObject record = (JsonObject) records.get(i); 
        sr1 = new SearchResults(); 
        sr1.setAddress(record.get("address").toString()); 
        results.add(sr1); 
       } 
      } 
     } 
    } 
    } 

mio adattatore lista:

public class MyCustomBaseAdapter extends BaseAdapter { 
    private static ArrayList<SearchResults> searchArrayList; 

    private LayoutInflater mInflater; 

    public MyCustomBaseAdapter(Context context, ArrayList<SearchResults> results) { 
     searchArrayList = results; 
     mInflater = LayoutInflater.from(context); 
    } 

    public int getCount() { 
     return searchArrayList.size(); 
    } 

    public Object getItem(int position) { 
     return searchArrayList.get(position); 
    } 

    public long getItemId(int position) { 
     return position; 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 
     ViewHolder holder; 
     if (convertView == null) { 
      convertView = mInflater.inflate(R.layout.custom_row_view, null); 
      holder = new ViewHolder(); 
      holder.txtAddress = (TextView) convertView.findViewById(R.id.address); 

      convertView.setTag(holder); 
     } else { 
      holder = (ViewHolder) convertView.getTag(); 
     } 

     holder.txtAddress.setText(searchArrayList.get(position).getAddress()); 

     return convertView; 
    } 

    static class ViewHolder { 
     TextView txtAddress; 
    } 
} 

e, infine, SearchResults.java:

public class SearchResults { 
    private String address = ""; 

    public void setAddress(String address) { 
     this.address = address; 
    } 

    public String getAddress() { 
     return address; 
    } 
} 

Quindi, cosa faccio di sbagliato? Hai un'idea a riguardo?

Grazie.

+1

Questa non è una soluzione ma un test che potrebbe aiutare a capire da dove il problema proviene. Il ciclo for potrebbe essere il problema, prova a usare un numero come 1 invece di 'records.count()' e vedi se ottieni ancora l'errore, prova a risolvere ogni area che può essere il problema. – Osman

+0

fammi sapere cosa succede ... oppure puoi produrre la lunghezza dei record, se è troppo lungo provare a eseguire l'elaborazione sul thread precedente prima di portarlo sul thread principale ... – Osman

+1

Grazie. non visualizza nulla ma mi dà '06-05 21: 27: 32.367: I/dalvikvm-heap (23426): Grow heap (caso frag) a 8.924 MB per allocazione 691216 byte ' – Copernic

risposta

11
private class LoginAction extends AsyncTaskList<String, Void, ArrayList<SearchResult>> { 

    @Override 
    protected ArrayList<SearchResult> doInBackground(String... params) { 
     List<SearchResults> resultList = new ArrayList<SearchResults>(); 

     Map<String, String> callArgs = new HashMap<String, String>(1); 

     callArgs.put("suuid", "dtr0bdQGcqwSh3QO7fVwgVfBNWog6mvEbAyljlLX9E642Yfmur"); 

     try { 
      response = EventPulseCloud.call("ListEvents", callArgs); 
     } catch (HttpClientException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } catch (JsonException e) { 
      e.printStackTrace(); 
     } 
     //See here I am running the loop in the background so its not on the main thread, then passing the list off to the onpostexecute that way all the main thread does is set the adapter list and notify it of the data update and the list should be updated on the screen 
     if(response.get("Type").toString().equals("success")) { 
      JsonArray records = null; 
      try { 
       records = response.getObject ("Data").getArray ("Records"); 
      } catch (JsonException e) { 
       e.printStackTrace(); 
      } 

      for(int i = 0; i < records.count(); i++) { 
       JsonObject record = (JsonObject) records.get(i); 
       sr1 = new SearchResults(); 
       sr1.setAddress(record.get("address").toString()); 
       resultList.add(sr1); 
      } 
     } 
     return resultList; 
    } 

    protected void onPostExecute(ArrayList<SearchResult> resultList) { 
      setListItems(resultList); 

    } 
} 
} 

aggiungere questa linea prima onCreate con tutti gli altri var globale

//here you want to create an adapter var with your base adapter so you can set it the updated list later when you have populated data from the internet 
     ArrayList<SearchResults> searchResults = new ArrayList<SearchResults>(); 
     MyCustomBaseAdapter adapter = new MyCustomBaseAdapter(this, searchResults) 

pasta questo sopra il tuo metodo oncreate (sostituirlo)

//here is just the code to update your main method to reflect all the changes I made 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    new LoginAction().execute(""); 

    final ListView lv1 = (ListView) findViewById(R.id.ListView01); 
    lv1.setAdapter(adapter); 

} 

e aggiungere questo metodo all'adattatore (MyCus classe tomnBaseAdapter)

public void setListItems(ArrayList<SearchResult> newList) { 
    searchArrayList = newList; 
    notifyDataSetChanged(); 
} 
+1

Continuavo a chiedermi perché la vista elenco fosse vuota .. saltare i frame non è sufficiente per ottenere una vista elenco vuota ... è perché non hai mai chiamato "notifyDataSetChanged(); ' sull'adattatore .. è perché ha bisogno di sapere quando hai cambiato i dati nell'adattatore in modo che possa ricaricare (aggiornamento) .... Quindi karakuri indica alcune cose che potresti non aver realizzato ... – Osman

+0

Capisco ora .. Grazie .. Ma ho un problema, riguarda il tipo di ritorno del doInBackground, non posso cambiarlo in 'Arraylist' o' List' per restituire l'Elenco, il ritorno deve essere una stringa .. Cosa dovrei ? – Copernic

+0

Scusa, ho solo inteso che come codice idea, permettimi di modificarlo un po 'per vedere se riesco a farlo funzionare – Osman

22

onPostExecute() si verifica sul thread principale dell'interfaccia utente. Sembra che tu stia ancora facendo un buon lavoro con quel metodo che dovrebbe essere fatto fuori dal thread dell'interfaccia utente, cioè elaborando la risposta, iterando su oggetti JSON, ecc. Fallo in doInBackground() e restituisca un elenco di risultati, quindi l'unica cosa che onPostExecute deve fare è passare i nuovi articoli alla tua scheda di lista.

Inoltre, non utilizzare lo stesso ArrayList di quello in uso. Se per qualche motivo l'adattatore rileva che i dati sono cambiati senza aver chiamato notifyDataSetChanged(), probabilmente si bloccherà (o almeno mostrerà comportamenti dispari). Creare un nuovo ArrayList nella vostra AsyncTask, poi mettere questo nel vostro adattatore e chiamare da OnPostExecute:

public void setListItems(ArrayList<SearchResult> newList) { 
    searchArrayList = newList; 
    notifyDataSetChanged(); 
} 
+0

Grazie, è stato molto interessante e utile, ma il ritorno di doInBackground non dovrebbe essere solo una stringa? – Copernic

+1

il ritorno può essere quello che vuoi, penso che dovresti guardare più a come i metodi funzionano in java e più specificatamente alla classe async che stai usando qui ... fammi trovare alcune risorse ... – Osman

+1

Vedi qui http: // mobileorchard .com/android-app-developmentthreading-part-2-async-tasks/http://androidresearch.wordpress.com/2012/03/17/understanding-asynctask-once-and-forever/ http://developer.android .com/riferimento/android/os/AsyncTask.html – Osman