2012-01-21 24 views
28

Tôi đang phát triển một ứng dụng nhỏ đọc trong các trang html cụ thể, định dạng lại chúng và sau đó hiển thị chúng trong một WebView. Nếu tôi chạy mã của tôi trong chủ đề GUI, hiệu suất đạt gần như không đáng kể so với việc chỉ cho phép WebView hiển thị trang html gốc. Nhưng nếu tôi là một cậu bé tốt và làm như tôi đã nói, tôi phải sử dụng một AsyncTask để chạy mã trong nền để không đóng băng GUI trong 3-5 giây mã của tôi thực hiện công việc của mình . Vấn đề là ... nếu tôi làm như vậy, mã sẽ mất hơn 10 lần để hoàn thành. Một trang mất hơn 60 giây để hiển thị, điều này là không thể chấp nhận được.AsyncTask, phải mất một cú đánh phạt hiệu suất như vậy ...?

Theo dõi vấn đề, TraceView cho tôi thấy AsyncTask của tôi là (ưu tiên mặc định) chạy trong khoảng 10 ms khối, khoảng 4 lần mỗi giây. Tôi cần đặt mức độ ưu tiên của luồng thành MAX_PRIORITY để gần với thời gian tải có thể chấp nhận được, nhưng thậm chí sau đó phải mất 3-4 lần so với khi tôi chạy trong luồng GUI.

Tôi có làm điều gì sai, hay đây chỉ là cách nó hoạt động? Và nó phải hoạt động theo cách này ...?

Đây là mã biên dịch được theo yêu cầu:

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

Để chạy chính xác cùng mã trong lớp FilterX của tôi khi chạy như AsyncTask như khi chạy trên thread GUI, tôi tước tất cả những thứ ProgressBar, và sau đó tôi có được timings sau:

  • 30+ giây để tải một trang ở mức ưu tiên chủ đề mặc định
  • 15+ giây để tải một trang tại MAX_PRIORITY
  • 5+ giây để tải trang khi chạy trong chủ đề GUI
+3

TraceView nói bạn đang dành phần lớn thời gian của mình khi sử dụng tác vụ không đồng bộ? Đi từ 5 giây đến 60 làm cho nó nghe như một cái gì đó đã đi sai lầm khủng khiếp khi bạn refactored mã của bạn. (đăng các đoạn mã có liên quan nếu cần) – smith324

+0

Không thực sự "tái bao thanh toán" xảy ra. Tôi chỉ cần loại bỏ lệnh gọi execute() và thay vào đó gọi doInBackground() từ luồng GUI. Tôi sẽ đọc các phần liên quan và cho bạn thấy, những điều nhỏ nhặt của nó và rất nghi ngờ rằng đây là vấn đề. – OppfinnarJocke

+0

Bạn chưa bao giờ trả lời câu hỏi đầu tiên của tôi: TraceView nói bạn đang sử dụng thời gian của bạn ở đâu? – smith324

Trả lời

36

Bạn không phải là người duy nhất quan sát hành vi này. Sự chậm lại của yếu tố 10 có lẽ là kết quả của Android bằng cách sử dụng một cgroup Linux (lớp lập lịch trình) cho các chủ đề của BACKGROUND ưu tiên hoặc dưới đây. Tất cả các chủ đề này phải sống với thời gian CPU 10% hoàn toàn.

Tin vui là bạn không phải sống với cài đặt ưu tiên của Chủ đề từ java.lang.Thread. Bạn có thể gán cho Thread của bạn một ưu tiên pthread (Linux thread) từ các định nghĩa trong android.os.Process. Ở đó, bạn không chỉ có Process.THREAD_PRIORITY_BACKGROUND, mà còn hằng số để điều chỉnh mức ưu tiên một chút.

Hiện tại, Android sử dụng cgroup chuỗi nền cho tất cả các chuỗi có mức độ ưu tiên THREAD_PRIORITY_BACKGROUND trở xuống và THREAD_PRIORITY_BACKGROUND là 10 trong khi THREAD_PRIORITY_DEFAULT là 0 và THREAD_PRIORITY_FOREGROUND là -2.

Nếu bạn tham dự THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE (còn gọi là 9), chuỗi của bạn sẽ được loại bỏ khỏi nhóm nền với giới hạn 10%, trong khi không đủ quan trọng để làm gián đoạn chuỗi giao diện người dùng của bạn quá thường xuyên.

Tôi tin rằng có các tác vụ nền cần một chút công suất tính toán nhưng đồng thời không đủ quan trọng để chặn giao diện người dùng (bằng cách tiêu thụ quá nhiều CPU trong một chuỗi riêng biệt) và Android hiện không rõ ràng ưu tiên để gán cho những điều này, vì vậy theo quan điểm của tôi, đây là một trong những ưu tiên tốt nhất mà bạn có thể gán cho một nhiệm vụ như vậy.

Nếu bạn có thể sử dụng một HandlerThread thật dễ dàng để đạt được:

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

Nếu bạn muốn đi với AsyncTask, bạn vẫn có thể làm

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

nhưng lưu ý rằng việc thực hiện cơ bản có thể tái sử dụng cùng một đối tượng Thread cho các nhiệm vụ khác nhau, cho AsyncTask tiếp theo, hoặc bất cứ thứ gì. Có vẻ như Android chỉ cần đặt lại mức độ ưu tiên sau khi doInBackground() trả về. Tất nhiên, nếu giao diện người dùng của bạn thực sự tiêu thụ CPU và bạn muốn nhiều quyền lực hơn cho nhiệm vụ của mình cùng lúc, lấy nó ra khỏi giao diện người dùng, bạn có thể đặt ưu tiên khác, có thể lên đến Process.THREAD_PRIORITY_FOREGROUND.

+2

Lộn xộn với mức độ ưu tiên của một chuỗi mà bạn không tạo ra là biểu mẫu xấu. Nếu bạn muốn kiểm soát các ưu tiên luồng, điều đó thật tuyệt, nhưng sau đó không sử dụng 'AsyncTask', nhưng hãy nĩa riêng' Thread' của bạn, hoặc sử dụng một 'ExecutorService' cho một nhóm luồng mà bạn tạo ra.Hoặc, nếu bạn * thực sự * muốn ngữ nghĩa 'AsyncTask', hãy tạo' ExecutorService' của riêng bạn và sử dụng 'executeOnExecutor()' để chọn nó trên API Level 11+, và đặt ưu tiên luồng của riêng bạn trên các luồng đó. – CommonsWare

+2

@CommonsWare Tôi đã không tìm thấy bất kỳ đối tượng Thread-ish nào khác ngoài 'HandlerThread' hỗ trợ thiết lập' Process.THREAD_PRIORITY' một cách rõ ràng, và 'Thread' cũng kỳ diệu có background prio theo mặc định. Như tôi thấy, tôi thậm chí không thể chắc chắn rằng Android sẽ không thay đổi ưu tiên của Chủ đề trên đường đi phù hợp với nhu cầu của nó. Vì vậy, khi tôi không tìm thấy cách nào khác hơn là viết cho 'HandlerThread', tôi đã tự do để xem tất cả các phương pháp thao tác ưu tiên khác như nhau không chính thức. Nhưng bạn có một điểm ở đó. –

+0

Lời xin lỗi của tôi, tôi đã hiểu sai điều gì đó trong câu trả lời của bạn. Mặc dù lưu ý rằng 'HandlerThread' không liên quan gì đến ưu tiên, ngoại trừ những gì nó thừa kế từ lớp Java' Thread' chuẩn. – CommonsWare

18

AsyncTask chạy ở mức ưu tiên thấp hơn để giúp đảm bảo chuỗi giao diện người dùng vẫn đáp ứng.

1

Mặc dù hiệu suất đạt được, bạn làm muốn thực hiện việc này dưới nền. Chơi đẹp, và những người khác sẽ chơi tốt với bạn.

Vì tôi không biết điều này là gì, tôi không thể đề xuất phương án thay thế. Phản ứng đầu tiên của tôi là kỳ lạ khi bạn đang cố định dạng lại HTML trên thiết bị điện thoại. Đó là điện thoại , không phải là lõi tứ với RAM. Có thể thực hiện định dạng lại trong dịch vụ web và hiển thị kết quả trên điện thoại không?

+0

Vâng ... đây là mã từ một máy chủ diễn đàn, không có tapatalk hoặc bất kỳ hỗ trợ nào khác như vậy, do đó, cách duy nhất tôi có thể nghĩ đến là thực hiện định dạng lại trên điện thoại. Nó gây lỗi cho tôi để đọc diễn đàn đó trong một trình duyệt (mà BTW dường như không tìm nạp và hiển thị trong nền), tôi liên tục phải phóng to và cuộn từng trang theo cách thủ công, vì vậy mã của tôi không bị xóa những thứ cần thiết (tiêu đề, hầu hết các cột trong bảng, hầu hết hình ảnh, v.v.) và sau đó hiển thị được định dạng tốt trong WebView của tôi. Nó chỉ dành cho bản thân tôi. Sử dụng dịch vụ web ...? Tôi không có đầu mối mờ nhạt ... – OppfinnarJocke

0

u cần gọi chuỗi cuối cùng str = fx.execute. bạn không nên gọi trực tiếp doinbackground từ chuỗi ui.

Các vấn đề liên quan