Подтвердить что ты не робот

Пропустили 60 кадров! Приложение может делать слишком много работы над своей основной нитью

Я работаю над приложением, которое должно получить ответ JSON от веб-службы и написать каждый элемент в listview, я прочитал, что должен работать с AsyncTask чтобы получить HTTP-ответ, и я сделал это, и я смог получить данные из веб-службы и отобразить их в TextViews. Но когда я пытаюсь отображать элементы в списке, он не отображает ничего и дает мне следующее сообщение в 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;
    }
}

Итак, что я делаю неправильно? У вас есть идея об этом?

Спасибо.

4b9b3361

Ответ 1

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 со всем другим глобальным 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)

вставьте это в свой метод 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();
}

Ответ 2

onPostExecute() происходит в потоке основного интерфейса. Похоже, что вы все еще выполняете достаточную работу в этом методе, который должен выполняться из потока пользовательского интерфейса, то есть обрабатывать ответ, итерации над объектами JSON и т.д. Сделайте это в doInBackground() и верните список результатов, поэтому единственное, что нужно сделать onPostExecute, - это передать новые элементы в ваш адаптер списка.

Кроме того, не используйте тот же ArrayList, что и ваш адаптер. Если по какой-то причине адаптер обнаруживает, что данные изменились без вызова notifyDataSetChanged(), это, вероятно, приведет к сбою (или, по крайней мере, отображению нечетного поведения). Создайте новый ArrayList в своей AsyncTask, затем поместите его в свой адаптер и вызовите его из onPostExecute:

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

Ответ 3

взято из: Android UI: исправление пропущенных кадров

Любой, кто начинает разработку приложения для Android, видит это сообщение на logcat "Хореограф (abc): пропущенные xx-фреймы! Приложение может делать слишком много работы над своим основным потоком". Итак, что это на самом деле означает, почему вы должны беспокоиться и как его решать.

Это означает, что ваш код занимает много времени, и из-за него пропускаются кадры, возможно, из-за некоторой тяжелой обработки, которую вы делаете в основе вашего приложения или доступа к БД или любой другой вещи, которая вызывает поток остановитесь некоторое время. Вот более подробное объяснение:

Хореограф позволяет приложениям подключаться к vsync и правильно использовать время для повышения производительности.

Анимация Android-просмотра внутренне использует хореографа с той же целью: правильно настроить анимацию и, возможно, улучшить производительность.

Так как хореографу рассказывают о каждом событии vsync, я могу сказать, что один из Runnables, переданный Choreographer.post * apis, не заканчивается за одно время, заставляя кадры пропускаться.

По моему пониманию, хореограф может обнаруживать проскальзывание кадра. Он не может сказать, почему это происходит.

Сообщение "Приложение может слишком много работать над основным потоком". может ввести в заблуждение.

источник: Значение сообщений хореографа в Logcat

Почему вы должны быть обеспокоены

Когда это сообщение появляется в эмуляторе Android, а количество пропущенных кадров довольно мало (<100), вы можете сделать безопасную ставку медленного эмулятора - что происходит почти все время. Но если количество кадров пропущено и большое и в порядке 300+, то могут возникнуть серьезные проблемы с вашим кодом. Устройства Android поставляются в широком спектре оборудования, в отличие от устройств ios и windows. ОЗУ и процессор различаются, и если вы хотите иметь разумную производительность и пользовательский интерфейс на всех устройствах, вам необходимо исправить это. Когда кадры пропускаются, пользовательский интерфейс медленный и медленный, что не является желательным для пользователя.

Как это исправить

Для этого требуется определить узлы, где есть или возможно может произойти длительная обработка. Лучший способ - сделать всю обработку независимо от того, насколько она маленькая или большая в потоке отдельно от основного потока пользовательского интерфейса. Так что будь доступ к форме данных SQLite Database или выполнению некоторых хардкорных математических упражнений или просто сортировке массива - сделайте это в другом потоке

Теперь здесь есть уловка. Вы создадите новый поток для выполнения этих операций, и когда вы запустите свое приложение, он столкнется с сообщением: "Только исходный поток, создавший иерархию представлений, может коснуться его представлений". Вам нужно знать, что пользовательский интерфейс в андроиде может быть изменен только основным потоком или потоком пользовательского интерфейса. Любой другой поток, который пытается это сделать, терпит неудачу и сбой этой ошибки. Что вам нужно сделать, так это создать новый Runnable внутри runOnUiThread и внутри этой runnable вы должны выполнить все операции с пользовательским интерфейсом. Найдите пример здесь.

Итак, у нас есть Thread и Runnable для обработки данных из основного потока, что еще? В андроиде есть AsyncTask, который позволяет выполнять длительные процессы в потоке пользовательского интерфейса. Это наиболее полезно, когда приложения управляются данными или управляются веб-api или используют сложные пользовательские интерфейсы, подобные тем, которые создаются с использованием Canvas. Сила AsyncTask - это то, что позволяет делать что-то в фоновом режиме, и как только вы закончите обработку, вы можете просто выполнить необходимые действия в пользовательском интерфейсе без какого-либо эффекта задержки. Это возможно, потому что AsyncTask вытекает из потока пользовательских интерфейсов Activitys - все операции, которые вы выполняете в пользовательском интерфейсе через AsyncTask, выполняются, это другой поток из основного потока пользовательского интерфейса, отсутствие помех для взаимодействия с пользователем.

Итак, это то, что вам нужно знать для создания плавных приложений для Android, и насколько я знаю, каждый новичок получает это сообщение на своей консоли.

Я процитировал эту страницу. Очень описательный ответ.