【问题标题】:Skipped 60 frames! The application may be doing too much work on its main thread跳过了 60 帧!应用程序可能在其主线程上做了太多工作
【发布时间】:2013-06-01 16:26:27
【问题描述】:

我正在开发一个应该从 web 服务获取 JSON 响应并将每个元素写入 listview 的应用程序,我已经读到我应该使用 AsyncTask获取 HTTP 响应,我做到了,我可以从 web 服务中检索数据并将它们显示在 TextViews 中。但是当我尝试在列表视图中显示元素时,它不会显示 anything 并在 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.

这是我的主要代码:

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

我的列表适配器:

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

最后是 SearchResults.java :

public class SearchResults {
    private String address = "";

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

    public String getAddress() {
        return address;
    }
}

那么,我做错了什么?你对此有什么想法吗?

谢谢。

【问题讨论】:

  • 这不是一个解决方案,而是一个测试,它可能有助于找出问题出在哪里。那个 for 循环可能是问题所在,请尝试使用 1 之类的数字而不是 records.count(),看看是否仍然出现错误,只需尝试排除可能出现问题的每个区域。
  • 让我知道发生了什么...或者您可以输出记录的长度,如果它太长,请尝试在将其带到主线程之前在后台线程上进行处理...跨度>
  • 谢谢。它不显示任何内容,但给了我06-05 21:27:32.367: I/dalvikvm-heap(23426): Grow heap (frag case) to 8.924MB for 691216-byte allocation
  • 很好,说明问题出在循环上,试试下面的答案
  • 好的,所以我意识到问题在于您将适配器设置在该循环中,因此您无法将其移动到后台线程,请给我一分钟将一些代码放在一起...

标签: android android-listview android-asynctask baseadapter


【解决方案1】:

onPostExecute() 发生在主 UI 线程上。看起来您仍在该方法中做大量工作,这些工作应该在 UI 线程之外完成,即处理响应、迭代 JSON 对象等。在 doInBackground() 中执行此操作并返回结果列表,因此 onPostExecute 唯一需要做的就是将新项目传递给您的列表适配器。

另外,不要使用与适配器所拥有的相同的 ArrayList。如果由于某种原因适配器发现数据已更改而您没有调用notifyDataSetChanged(),它可能会崩溃(或至少显示奇怪的行为)。在您的 AsyncTask 中创建一个新的 ArrayList,然后将其放入您的 Adapter 并从 onPostExecute 调用它:

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

【讨论】:

【解决方案2】:
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);

    }   
}
}

在 oncreate 之前添加这一行以及所有其他全局变量

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

将此粘贴​​到您的 oncreate 方法上(替换它)

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

}

并将此方法添加到适配器(MyCustomnBaseAdapter 类)代码中

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

【讨论】:

  • 我一直想知道为什么您的列表视图是空的。跳帧不足以导致列表视图为空...这是因为您从未调用过 'notifyDataSetChanged();'在适配器上.. 那是因为它需要知道您何时更改了适配器中的数据,以便它可以重新加载(更新)....所以 karakuri 指出了一些您可能没有意识到的事情...
  • 我现在明白了.. 谢谢.. 但是我遇到了一个问题,关于 doInBackground 的返回类型,我无法将其更改为 ArraylistList 以返回列表,返回必须是一个字符串。我该怎么办?
  • 对不起,我只是说作为想法代码,让我稍微修改一下,看看能不能让它工作
  • 很酷,我设置了AsyncTask&lt;String, Void, ArrayList&lt;SearchResults&gt;&gt;.. 但现在我得到了:I/dalvikvm-heap(1273): Grow heap (frag case) to 8.925MB for 691216-byte allocation 它没有显示任何结果.. 我读到我的程序午餐@987654329 @那么我该如何解决呢?谢谢你:)
  • 没关系,这只是意味着您的应用程序的大小正在增长,这是可以理解的。您知道该循环是如何阻塞主线程的,因为它太大了,那是因为您正在下载大量数据,这导致 android 增加了为您的应用程序分配的内存大小,这并不意味着内存不足,只是您的应用程序的大小正在增长,这很好并且可以理解。现在要解决您在列表视图中看不到任何内容的事实,请确保您在某个时间点调用notifyDataSetChanged();,如果您按照我的大纲,它应该在 setListItems 方法中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
  • 1970-01-01
  • 2021-12-31
相关资源
最近更新 更多