AsyncTask, он должен принять такой штраф за производительность ...? - PullRequest
29 голосов
/ 21 января 2012

Я занимаюсь разработкой небольшого приложения, которое читает на определенных html-страницах, переформатирует их, а затем показывает их в WebView.Если я запускаю свой код в потоке графического интерфейса, снижение производительности практически ничтожно по сравнению с простым разрешением WebView отображать оригинальную html-страницу.Но если я хороший мальчик и делаю то, что мне говорят, я должен использовать AsyncTask для запуска кода в фоновом режиме, чтобы не зависать в течение этих 3-5 секунд, мой код выполняет свою работу,Проблема в том, что ... если я это сделаю, выполнение кода займет более 10 раз.Страница занимает более 60 секунд, что недопустимо.

Отслеживая проблему, TraceView показывает мне, что мой AsyncTask (с приоритетом по умолчанию) выполняется примерно за 10 мс, примерно 4 раза в секунду.Мне нужно установить приоритет потока в MAX_PRIORITY, чтобы приблизиться к приемлемому времени загрузки, но даже тогда это занимает в 3-4 раза больше времени, чем при запуске в потоке GUI.

Я что-то не так делаю илиэто так, как это работает?И должен ли он работать таким образом ...?

Вот компилируемый код в соответствии с запросом:

package my.ownpackage.athome;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.StrictMode;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class AndroidTestActivity extends Activity
{   
    WebView webview;
    //...

    private class HelloWebViewClient extends WebViewClient 
    {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) 
        {
            AndroidTestActivity.this.fetch(view, url);
            return true;
        }
    }

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // To allow to connect to the web and pull down html-files, reset strict mode
        // see /4363195/defaulthttpclient-dlya-androidhttpclient
        if (android.os.Build.VERSION.SDK_INT > 9) 
        {
            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
        }

        // webview init etc...

        fetch(webview, "http://www.example.com");   
    }

    // This one calls either the AsyncTask or does it all manually in the GUI thread
    public void fetch(WebView view, String url)
    {
        //** Use these when run as AsyncTask in background - SLOW! 
        //** Takes 30+ seconds at default thread priority, with MAX_PRIORITY 15+ seconds
        // AsyncTask<Void, String, String> fx = new FilterX(url, view, this);   
        // fx.execute();    // running as AsyncTask takes roughly ten times longer than just plain load!    

        //** Use these when not running as AsyncTask - FAST! takes ~5 seconds
        FilterX fx = new FilterX(url, view, this);
        fx.onPreExecute();
        final String str = fx.doInBackground();
        fx.onPostExecute(str);
    }
}

class FilterX extends AsyncTask<Void, String, String>
{
    WebView the_view = null;
    // other stuff...

    FilterX(final String url, final WebView view, final Activity activity)
    {
        the_view = view;
        // other initialization
        // same code in both cases
    }

    protected void onPreExecute()
    {
        // same code in both cases
    }

    protected String doInBackground(Void... v)
    {
        // same in both cases...

        return new String();    // just to make it compile
    }

    protected void onPostExecute(final String string)
    {
        the_view.loadUrl(string);
        // same in both cases...
    }
}

Для запуска точно тот же код в моем классе FilterX при запуске какAsyncTask, как при запуске в потоке графического интерфейса, я удалил все вещи ProgressBar, и затем я получил следующие моменты времени:

  • 30 + секунд для загрузки страницы с приоритетом потока по умолчанию
  • 15+ секунды для загрузки страницы в MAX_PRIORITY
  • 5 + секунды для загрузки страницы при запуске в потоке графического интерфейса

Ответы [ 4 ]

37 голосов
/ 08 января 2013

Вы не единственный, кто наблюдает за этим поведением.Замедление в 10 раз, вероятно, является результатом того, что Android использует cgroup Linux (класс планирования) для потоков с приоритетом BACKGROUND или ниже.Все эти потоки должны жить с 10% процессорного времени в целом.

Хорошая новость заключается в том, что вам не нужно жить с настройками приоритетов потоков из java.lang.Thread.Вы можете назначить вашему потоку приоритет pthread (потока Linux) из определений в android.os.Process.Там у вас есть не только Process.THREAD_PRIORITY_BACKGROUND, но также и константы, позволяющие немного отрегулировать приоритет.

В настоящее время Android использует группу фоновых потоков для всех потоков с приоритетом THREAD_PRIORITY_BACKGROUND или хуже, а THREAD_PRIORITY_BACKGROUND_PRI_DEFITY_PRA 10 равен THERAD0 и THREAD_PRIORITY_FOREGROUND равно -2.

Если вы выберете THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE (он же 9), ваш поток будет удален из фоновой группы с ограничением в 10%, хотя это не настолько важно, чтобы прерывать интерфейс пользователя.слишком часто.

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

Если вы можете использоватьHandlerThread, которого легко достичь:

ht = new HandlerThread("thread name", THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE);
ht.start();
h  = new Handler(ht.getLooper()); 

Если вы хотите использовать AsyncTask, вы все равно можете сделать

protected final YourResult doInBackground(YourInputs... yis) {
    Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE);
    ...
}

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

Конечно, если ваш пользовательский интерфейс действительно потребляет процессор, и вы хотите в то же время больше энергии для своей задачи, убирая его из пользовательского интерфейса, вы можете установить другой приоритет, возможно вплоть до Process.THREAD_PRIORITY_FOREGROUND.

18 голосов
/ 21 января 2012

AsyncTask работает с более низким приоритетом, чтобы убедиться, что поток пользовательского интерфейса останется отзывчивым.

1 голос
/ 22 января 2012

Несмотря на снижение производительности, вы делаете хотите сделать это в фоновом режиме.Играйте хорошо, а другие будут играть с вами хорошо.

Поскольку я не знаю, для чего это нужно, я не могу предложить альтернативу.Моей первой реакцией было то, что странно, что вы пытаетесь переформатировать HTML на телефонном устройстве.Это телефон , а не четырехъядерный с кучей оперативной памяти.Можно ли сделать переформатирование в веб-сервисе и отобразить результат на телефоне?

0 голосов
/ 19 ноября 2014

вам нужно вызвать final String str = fx.execute. Вы не должны вызывать doinbackground напрямую из потока пользовательского интерфейса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...