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

AsyncTask, должен ли он принять такой удар по производительности...?

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

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

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

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

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 http://stackoverflow.com/info/8706464/defaulthttpclient-to-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, как при запуске в потоке GUI, я лишил все материалы ProgressBar, а затем получаю следующие тайминги:

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

Ответ 1

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

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

В настоящее время Android использует общую цепочку потоков для всех потоков с приоритетом THREAD_PRIORITY_BACKGROUND или хуже, а THREAD_PRIORITY_BACKGROUND составляет 10, а THREAD_PRIORITY_DEFAULT - 0, а THREAD_PRIORITY_FOREGROUND - -2.

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

Я считаю, что есть фоновые задачи, которые требуют немного вычислительной мощности, но которые в то же время недостаточно важны, чтобы де-факто блокировать пользовательский интерфейс (потребляя слишком много CPU в отдельном потоке), и 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.

Ответ 2

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

Ответ 3

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

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

Ответ 4

u нужно вызвать final String str = fx.execute. вы не должны вызывать doinbackground непосредственно из потока ui.