2013-06-05 12 views
20

Ich arbeite an einer App, die eine JSON-Antwort von einem Webservice erhalten sollte und jedes Element in einem listview schreiben, ich habe gelesen, dass ich mit AsyncTask arbeiten sollte, um die HTTP-Antwort zu bekommen, und ich tat es und ich konnte Daten vom Webservice abrufen und sie in TextViews anzeigen. Aber wenn ich versuche Elemente in einer Listenansicht angezeigt werden ist es nicht angezeigt werden etwas und gibt mir die folgende Meldung in der 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.Übersprungen 60 Frames! Die Anwendung möglicherweise zu viel Arbeit an seinem Hauptthread

hier ist mein Haupt-Code:

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); 
       } 
      } 
     } 
    } 
    } 

Meine Liste Adapter:

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; 
    } 
} 

und schließlich SearchResults.java:

public class SearchResults { 
    private String address = ""; 

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

    public String getAddress() { 
     return address; 
    } 
} 

Also, was mache ich falsch? Hast du eine Vorstellung davon?

Vielen Dank.

+1

Dies ist keine Lösung, sondern ein Test, der helfen könnte herauszufinden, wo das Problem herkommt. Diese for-Schleife könnte das Problem sein, versuchen Sie es mit einer Zahl wie 1 anstelle von 'records.count()' und sehen Sie, ob Sie immer noch den Fehler erhalten, versuchen Sie einfach, jeden Bereich, der das Problem sein kann. – Osman

+0

lassen Sie mich wissen, was passiert ... oder Sie können die Länge der Datensätze ausgeben, wenn es zu lang ist, versuchen Sie die Verarbeitung auf dem hinteren Thread, bevor Sie es auf den Hauptthread bringen ... – Osman

+1

Vielen Dank. es zeigt nichts an, aber gibt mir '06-05 21: 27: 32,367: I/dalvikvm-heap (23426): Wachsen Heap (frag case) auf 8,924 MB für 691216-Byte-Zuweisung ' – Copernic

Antwort

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); 

    } 
} 
} 

diese Zeile hinzufügen, bevor der oncreate mit allen anderen globalen var

//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) 

Paste dies über Ihre Oncreate-Methode (ersetzen Sie sie)

und fügen Sie diese Methode zum Adapter (MyCus tomnBaseAdapter Klasse) Code

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

Ich habe mich gewundert, warum Ihre Listenansicht leer war. Das Überspringen von Frames ist nicht genug, um zu einer leeren Listenansicht zu führen ... weil Sie nie 'notifyDataSetChanged(); " auf dem Adapter .. das ist, weil es wissen muss, wenn Sie die Daten im Adapter geändert haben, so dass es neu laden (aktualisieren) .... So Karakuri weist auf ein paar Dinge, die Sie möglicherweise nicht realisiert haben ... – Osman

+0

Ich verstehe jetzt .. Danke .. Aber ich habe ein Problem, ist über den Rückgabetyp des doInBackground, ich kann es nicht in 'Arraylist' oder' List' ändern, um die Liste zurückzugeben, die Rückkehr muss ein String sein .. Was soll ich tun ? – Copernic

+0

Entschuldigung, ich meinte nur, dass als Idee Code, lass es mich ein wenig ändern, um zu sehen, ob ich es funktionieren – Osman

22

onPostExecute() geschieht auf dem Haupt-UI-Thread. Es sieht so aus, als ob Sie immer noch ziemlich viel Arbeit in dieser Methode machen, die außerhalb des UI-Threads ausgeführt werden sollte, dh die Antwort verarbeiten, über JSON-Objekte iterieren usw. Machen Sie das in doInBackground() und haben Sie eine Liste von Ergebnissen, also Das einzige, was onPostExecute tun muss, ist, die neuen Elemente an Ihren Listenadapter zu übergeben.

Verwenden Sie nicht die gleiche ArrayList wie die, die Ihr Adapter enthält. Wenn der Adapter aus irgendeinem Grund erkennt, dass sich die Daten geändert haben, ohne dass Sie notifyDataSetChanged() aufgerufen haben, wird er wahrscheinlich abstürzen (oder zumindest seltsame Verhalten anzeigen). Erstellen Sie ein neues Arraylist in Ihrem AsyncTask, setzen diese dann in Ihrem Adapter und nennen es von OnPostExecute:

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

Danke, es war sehr interessant und hilfreich, aber sollte die Rückgabe von doInBackground nicht nur eine Zeichenfolge sein? – Copernic

+1

die Rückkehr kann sein, was auch immer Sie wollen, ich denke, Sie sollten mehr darüber lernen, wie Methoden in Java und genauer die Async-Klasse, die Sie hier verwenden ... Lassen Sie mich einige Ressourcen finden ... – Osman

+1

Siehe hier http: // mobileorchard .com/android-app-entwicklung-threading-teil-2-async-tasks/http://androidresearch.wordpress.com/2012/03/17/understanding-asynctask-once-and-forever/ http://developer.android .com/reference/android/os/AsyncTask.html – Osman