AsyncTask,它必须承受这样的性能损失吗......? [英] AsyncTask, must it take such a performance penalty hit...?

查看:12
本文介绍了AsyncTask,它必须承受这样的性能损失吗......?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm developing a small app that reads in specific html-pages, re-formats them and then shows them in a WebView. If I run my code in the GUI thread, the performance hit is close to negligible compared to simply letting the WebView show the original html-page. But if I'm a good boy and do like I'm told, I'm supposed to use an AsyncTask to run the code in the background so as not to freeze up the GUI during those 3-5 seconds my code does its job. Problem is... if I do so, the code takes more than 10 times as long to finish. A page takes 60+ seconds to show, which is unacceptable.

跟踪问题后,TraceView 向我显示我的 AsyncTask(在默认优先级下)以大约 10 毫秒的块运行,大约每秒 4 次.我需要将线程优先级设置为 MAX_PRIORITY 以接近可接受的加载时间,但即便如此,它也需要比在 GUI 线程中运行时长 3-4 倍.

Tracking down the problem, TraceView shows me that my AsyncTask is (at default priority) run in roughly 10 ms chunks, around 4 times per second. I need to set my thread priority to MAX_PRIORITY to get close to acceptable loading times, but even then it takes 3-4 times longer than when I run in the GUI thread.

我做错了什么,还是这就是它的工作方式?它必须以这种方式工作吗...?

Am I doing something wrong, or is this just the way it works? And must it work this way...?

这是要求的可编译代码:

Here's compilable code as requested:

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

为了在作为 AsyncTask 运行时在我的 FilterX 类中完全运行与在 GUI 线程上运行时相同的代码,我剥离了所有 ProgressBar 内容,然后我得到了以下时间:

To run exactly the same code in my FilterX class when run as AsyncTask as when run on the GUI thread, I stripped all ProgressBar stuff, and then I get the following timings:

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

推荐答案

您不是唯一观察到这种行为的人.速度下降 10 倍可能是因为 Android 使用 Linux cgroup(调度类)来处理优先级为背景或以下的线程.所有这些线程必须总共占用 10% 的 CPU 时间.

You're not the only one observing this behaviour. The slowdown by factor 10 is probably a result of Android using a Linux cgroup (scheduling class) for threads of priority BACKGROUND or below. All these threads have to live with 10% CPU time altogether.

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

The good news is you don't have to live with the Thread priority settings from java.lang.Thread. You can assign your Thread a pthread (Linux thread) priority from the definitions in android.os.Process. There, you not only have Process.THREAD_PRIORITY_BACKGROUND, but also constants to adjust the priority a bit.

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

Currently, Android uses the background thread cgroup for all threads with priority THREAD_PRIORITY_BACKGROUND or worse, and THREAD_PRIORITY_BACKGROUND is 10 while THREAD_PRIORITY_DEFAULT is 0 and THREAD_PRIORITY_FOREGROUND is -2.

如果您选择 THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE(又名 9),您的线程将被提升到具有 10% 限制的后台 cgroup 之外,但其重要性不足以经常中断您的用户界面线程.

If you go for THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE (aka 9) your thread will be lifted out of the background cgroup with the 10% limitation, while not being important enough to interrupt your User Interface threads too often.

我相信有一些后台任务需要一点计算能力,但同时又不重要到事实上阻塞 UI(通过在单独的线程中消耗太多 CPU),Android 目前没有明显的优先级分配给这些,所以在我看来,这是您可以分配给此类任务的最佳优先级之一.

I believe there are background tasks which need a bit of computational power but which are at the same time not important enough to de facto block the UI (by consuming too much CPU in a separate thread) and Android currently has no obvious priority to assign to these, so in my view, this is one of the best priorities you can assign to such a task.

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

If you can use a HandlerThread it's easy to achieve:

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

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

If you want to go with AsyncTask, you can still do

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

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

but be aware that the underlying implementation may reuse the same Thread object for different tasks, for the next AsyncTask, or whatever. It seems that Android simply resets the priority after doInBackground() returns, though.

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

Of course, if your UI really consumes CPU and you want more power for your task at the same time, taking it away from the UI, you can set another priority, maybe up to Process.THREAD_PRIORITY_FOREGROUND.

这篇关于AsyncTask,它必须承受这样的性能损失吗......?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆