2013-06-05 19 views
20

Pracuję nad aplikacją, która powinna uzyskać odpowiedź JSON z usługi sieciowej i napisać każdy element z listy listview, czytałem, że powinienem pracować z AsyncTask, aby uzyskać odpowiedź HTTP i zrobiłem to i mogłem pobrać dane z usługi internetowej i wyświetlić je w TextViews. Ale gdy próbuję wyświetlić elementy w ListView nie wyświetla nic i daje mi się następujący komunikat w 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.Pominięto 60 klatek! Aplikacja może wykonywać zbyt wiele pracy nad głównym wątkiem.

tu jest mój główny kod:

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

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

wreszcie SearchResults.java:

public class SearchResults { 
    private String address = ""; 

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

    public String getAddress() { 
     return address; 
    } 
} 

Co mam zrobić źle? Czy masz pomysł na ten temat?

Dziękuję.

+1

To nie jest rozwiązanie, ale test, który może pomóc w ustaleniu, skąd pochodzi problem. To może być problem z pętlą, spróbuj użyć liczby podobnej do 1 zamiast 'records.count()' i sprawdź, czy nadal pojawia się błąd, po prostu spróbuj rozwiązać problem z każdym obszarem, który może być problemem. – Osman

+0

daj mi znać, co się dzieje ... lub możesz wydrukować długość rekordów, jeśli jest zbyt długa, spróbuj wykonać przetwarzanie na wątku wstecz, zanim wprowadzisz go do głównego wątku ... – Osman

+1

Dziękuję. nie wyświetla niczego, ale daje '06-05 21: 27: 32.367: I/dalvikvm-heap (23426): Rosną stertę (frag case) do 8.924MB dla 691216-bajtowej alokacji ' – Copernic

Odpowiedz

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

    } 
} 
} 

dodać tę linię przed onCreate całym swoim drugim globalnej 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) 

pasty to przez twoją oncreate metodę (zamień)

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

} 

i dodaj tę metodę do adaptera (MyCus tomnBaseAdapter class) kod

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

Zastanawiam się, dlaczego widok listy jest pusty. Pomijanie klatek nie jest wystarczające, aby wyświetlić pusty widok listy ... ponieważ nigdy nie było wywoływane "notifyDataSetChanged(); " na adapterze .. to dlatego, że musi wiedzieć, kiedy zmieniłeś dane w adapterze, aby mógł ponownie załadować (zaktualizować) ... Więc karakuri wskazuje na kilka rzeczy, które być może nie zdawałeś sobie sprawy ... – Osman

+0

Rozumiem teraz .. Dziękuję .. Ale mam problem, chodzi o typ powrotu doInBackground, nie mogę go zmienić na 'Arraylist' lub' List', aby zwrócić listę, powrót musi być ciągiem ... czy powinnam ? – Copernic

+0

Przepraszam, miałem na myśli, że jako kod pomysłu, pozwól mi go trochę zmodyfikować, żeby zobaczyć, czy mogę go uruchomić. – Osman

22

onPostExecute() dzieje się w głównym wątku UI. Wygląda na to, że nadal wykonujesz sporo pracy w tej metodzie, która powinna być wykonana poza wątkiem UI, tj. Przetwarzaniem odpowiedzi, iterowaniem obiektów JSON itd. Zrób to w doInBackground() i zwróć listę wyników, więc jedyne, co musi zrobićPostExecute, to przekazanie nowych elementów do adaptera listy.

Ponadto nie należy używać tej samej tablicy ArrayList, co karta adaptera. Jeśli z jakiegoś powodu adapter wykryje, że dane uległy zmianie bez wywołania notifyDataSetChanged(), prawdopodobnie ulegnie awarii (lub przynajmniej wyświetli dziwne zachowania). Utwórz nowy ArrayList w swoim AsyncTask, a następnie umieścić to w swoim Adapter i wywołać ją z onPostExecute:

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

Dziękuję, to było bardzo interesujące i pomocne, ale czy powrót doInBackground nie powinien być tylko ciągiem? – Copernic

+1

Zwrot może być dowolny, myślę, że powinieneś przyjrzeć się, jak działają metody w java, a dokładniej w klasie async, której używasz ... pozwól mi znaleźć pewne zasoby ... – Osman

+1

Zobacz tutaj 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/reference/android/os/AsyncTask.html – Osman