Skipped 60 frames! The application may be doing too much work on its main thread

user1079425 picture user1079425 · Jun 5, 2013 · Viewed 95.4k times · Source

I'm working on a App that should get a JSON response from a webservice and write every element in a listview, I have read that I should work with AsyncTask to get the HTTP Response and I did it and I could retrieve data from the webservice and display them in TextViews. But when I try to display elements in a listview it doesn't display anything and gives me the following message in the 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.

here's my main 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);
                }
            }   
        }   
    }
    }

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

and finally, SearchResults.java :

public class SearchResults {
    private String address = "";

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

    public String getAddress() {
        return address;
    }
}

So, what do I do wrong ? Do you have an idea about this ?

Thank you.

Answer

Karakuri picture Karakuri · Jun 5, 2013

onPostExecute() happens on the Main UI thread. It looks like you are still doing a fair amount of work in that method that should be done off the UI thread, i.e. processing the response, iterating over JSON objects, etc. Do that in doInBackground() and have that return a list of results, so the only thing onPostExecute needs to do is pass the new items to your list adapter.

Also, do not use the same ArrayList as the one your adapter holds. If for some reason the adapter discovers that the data has changed without you having called notifyDataSetChanged(), it will probably crash (or at least display odd behaviors). Create a new ArrayList in your AsyncTask, then put this in your Adapter and call it from onPostExecute:

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