AsyncTask,它必须采取这样的性能惩罚......?

Opp*_*cke 29 android android-asynctask

我正在开发一个小应用程序,它读取特定的html页面,重新格式化它们,然后在WebView中显示它们.如果我在GUI线程中运行我的代码,与简单地让WebView显示原始html页面相比,性能命中几乎可以忽略不计.但是,如果我是一个好孩子并且像我被告知的那样,我应该使用AsyncTask在后台运行代码,以免在我的代码完成工作的3-5秒内冻结GUI .问题是......如果我这样做,代码需要花费10倍以上的时间才能完成.页面需要60秒以上才能显示,这是不可接受的.

跟踪问题,TraceView向我显示我的AsyncTask(默认优先级)以大约10毫秒的块运行,大约每秒4次.我需要将我的线程优先级设置为MAX_PRIORITY以接近可接受的加载时间,但即使这样,它也比我在GUI线程中运行时长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/questions/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...
    }
}
Run Code Online (Sandbox Code Playgroud)

要在运行AsyncTask时在我的FilterX类中运行完全相同的代码,就像在GUI线程上运行一样,我剥离了所有ProgressBar的东西,然后我得到以下时间:

  • 以默认线程优先级加载页面30多秒
  • 在MAX_PRIORITY加载页面15秒以上
  • 在GUI线程中运行时加载页面的时间超过5秒

cla*_*ker 37

你不是唯一一个观察这种行为的人.因子10的减速可能是因为Android使用Linux cgroup(调度类)来获得优先级为BACKGROUND或更低优先级的线程.所有这些线程都必须占用10%的CPU时间.

好消息是您不必使用java.lang.Thread中的Thread优先级设置.您可以从android.os.Process中的定义为线程分配一个pthread(Linux线程)优先级.在那里,你不仅有Process.THREAD_PRIORITY_BACKGROUND,还有常量来调整优先级.

目前,Android使用后台线程cgroup用于优先级为THREAD_PRIORITY_BACKGROUND或更糟的所有线程,THREAD_PRIORITY_BACKGROUND为10,而THREAD_PRIORITY_DEFAULT为0且THREAD_PRIORITY_FOREGROUND为-2.

如果您选择THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE(又名9),您的线程将被提升出背景cgroup并具有10%的限制,同时不足以过于频繁地中断您的用户界面线程.

我相信有一些后台任务需要一些计算能力,但同时也不足以实际阻止UI(通过在单独的线程中消耗过多的CPU)而Android当前没有明确的优先级来分配给这些,所以在我看来,这是你可以分配给这样一个任务的最佳优先事项之一.

如果你可以使用HandlerThread,它很容易实现:

ht = new HandlerThread("thread name", THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE);
ht.start();
h  = new Handler(ht.getLooper()); 
Run Code Online (Sandbox Code Playgroud)

如果你想使用AsyncTask,你仍然可以

protected final YourResult doInBackground(YourInputs... yis) {
    Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE);
    ...
}
Run Code Online (Sandbox Code Playgroud)

但请注意,底层实现可能会为不同的任务,下一个AsyncTask或其他任务重用相同的Thread对象.但是,在doInBackground()返回后,Android似乎只是重置了优先级.

当然,如果您的UI确实消耗了CPU并且您希望同时为您的任务提供更多功能,那么将其从UI中删除,您可以设置另一个优先级,可能高达Process.THREAD_PRIORITY_FOREGROUND.

  • 弄乱你没有创建的线程的优先级是糟糕的形式.如果你想控制线程优先级,那很酷,但是不要使用`AsyncTask`,而是使用自己的`Thread`,或者使用`ExecutorService`来创建你创建的线程池.或者,如果你真的*想要`AsyncTask`语义,创建你自己的`ExecutorService`并使用`executeOnExecutor()`在API Level 11+上选择它,并在这些线程上设置你自己的线程优先级. (2认同)
  • @CommonsWare我没有找到除了HandlerThread之外的任何其他Thread-ish对象,它支持显式地设置`Process.THREAD_PRIORITY`,并且`Thread`s默认也神奇地具有后台prio.在我看来,我甚至无法确定Android是否会根据其需要改变Thread的优先级.因此,当我发现没有其他记录方式而不是"HandlerThread"时,我自由地将所有其他优先级操作方法视为同样非官方的.但你有一点意见. (2认同)

Rom*_*Guy 18

AsyncTask以较低的优先级运行,以帮助确保UI线程保持响应.