android android-listview android-asynctask baseadapter

android - Saltamos 60 cuadros! La aplicación puede estar haciendo demasiado trabajo en su hilo principal



android-listview android-asynctask (2)

Estoy trabajando en una aplicación que debería obtener una respuesta JSON de un servicio web y escribir cada elemento en una vista de lista, he leído que debería trabajar con AsyncTask para obtener la respuesta HTTP y lo hice y pude recuperar datos del servicio web y mostrarlos en TextViews . Pero cuando trato de mostrar los elementos en una vista de lista, no se muestra nada y aparece el siguiente mensaje en el 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. 06-05 19:44:27.418: I/Choreographer(20731): Skipped 60 frames! The application may be doing too much work on its main thread.

aquí está mi código principal:

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

Mi lista de adaptadores:

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

y finalmente, SearchResults.java:

public class SearchResults { private String address = ""; public void setAddress(String address) { this.address = address; } public String getAddress() { return address; } }

Entonces, ¿qué hago mal? ¿Tienes una idea sobre esto?

Gracias.


onPostExecute() ocurre en el hilo de la interfaz de usuario principal. Parece que todavía está haciendo una buena cantidad de trabajo en ese método que debería hacerse fuera del hilo de la interfaz de usuario, es decir, procesar la respuesta, iterar sobre objetos JSON, etc. Hacer eso en doInBackground() y hacer que devuelva una lista de resultados , por lo que lo único que debe hacer en PostExecute es pasar los nuevos elementos a su adaptador de lista.

Además, no use la misma ArrayList que la que tiene su adaptador. Si por alguna razón el adaptador descubre que los datos han cambiado sin haber llamado a notifyDataSetChanged() , probablemente se bloqueará (o al menos mostrará comportamientos extraños). Cree una nueva ArrayList en su AsyncTask, luego ponga esto en su Adaptador y llámelo desde onPostExecute:

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


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

agregue esta línea antes de oncreate con todas sus otras var globales

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

pega esto sobre tu método oncreate (reemplázalo)

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

y agregue este método al código del adaptador (clase MyCustomnBaseAdapter)

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