2012-01-21 1 views
28

私は、特定のhtmlページを読み込み、それらのフォーマットを変更してWebViewで表示する小さなアプリケーションを開発しています。 GUIスレッドでコードを実行すると、WebViewに元のhtmlページを表示させるだけの場合と比べて、パフォーマンスの低下はごくわずかです。しかし、もし私がいい男の子で、私が言われたように言うと、AsyncTaskを使ってバックグラウンドでコードを実行し、GUIが3〜5秒間フリーズしないようにします。 。問題は...私がそうした場合、コードの終了までに10倍以上の時間がかかります。ページの表示に60秒以上かかりますが、これは容認できません。AsyncTask、そのようなパフォーマンスのペナルティヒットを取る必要があります...?

TraceViewは、AsyncTaskが(デフォルト優先度で)約10ミリ秒のチャンクで1秒あたり約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... 
    } 
} 

GUIスレッドで実行したときに、私はすべてのProgressBarものを剥奪、そして私としてAsyncTaskとして実行したときに私のFilterXクラスで正確同じコードを実行するにはMAX_PRIORITY 01でページをロードするために、デフォルトのスレッドの優先度で

  • 15秒以上のページをロードするために

    • 30+秒以下のタイミングを取得
    • 5+ GUIスレッドで実行したときにページをロードする秒
  • +3

    TraceViewは、非同期タスクを使用する際に、ほとんどの時間を費やしていると言いますか? 5秒から60秒になると、コードをリファクタリングしたときに何かがひどく間違っているように聞こえます。 (必要に応じて適切なスニペットを投稿してください) – smith324

    +0

    実際に「再要因」は起こっていません。 execute()の呼び出しを削除し、代わりにGUIスレッドからdoInBackground()を呼び出します。私は関連する部品を切り取って見せてくれるでしょう、その些細な事を示して、これが何か問題であることを非常に疑うでしょう。 – OppfinnarJocke

    +0

    あなたは私の最初の質問に答えたことはありません:TraceViewはあなたの時間をどこで使っていると言いますか? – smith324

    答えて

    36

    この現象を観察しているのはあなただけではありません。 factor 10による減速はおそらく、アンドロイドが優先度の高いスレッドのためのLinux cgroup(スケジューリングクラス)を使用した結果です。これらのスレッドはすべて、10%のCPU時間を必要とします。

    良いニュースは、java.lang.Threadのスレッド優先度設定を使用する必要がないということです。スレッドには、android.os.Processの定義からpthread(Linuxスレッド)優先度を割り当てることができます。 Process.THREAD_PRIORITY_BACKGROUNDだけでなく、優先度を少し調整するための定数もあります。

    現在、Androidは優先THREAD_PRIORITY_BACKGROUNDまたは悪化を持つすべてのスレッドのバックグラウンドスレッドのcgroupを使用し、そしてTHREAD_PRIORITY_DEFAULTが0でTHREAD_PRIORITY_FOREGROUNDが-2ながらTHREAD_PRIORITY_BACKGROUNDは10です。

    THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE(別名9)のスレッドは、ユーザインターフェイススレッドをあまりにも頻繁に中断するほど重要ではないものの、10%の制限でバックグラウンドcgroupから取り除かれます。

    私はある程度の計算能力を必要とするバックグラウンドタスクがあると思いますが、同時に(別のスレッドであまりにも多くのCPUを消費することによって)UIを実際にブロックするのには重要ではないと同時に、優先順位をこれらに割り当てることは、私の見解では、これがあなたがそのようなタスクに割り当てることができる最優先事項の1つです。あなたはそれを達成するのは簡単ですHandlerThread使用できる場合

    :あなたはAsyncTaskと一緒に行きたい場合は

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

    を、あなたはまだ

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

    んが、実装が再利用可能性があることに注意することができ異なるタスクのための同じThreadオブジェクト、次のAsyncTaskのためのもの、または何でも。しかし、Androidは単にdoInBackground()が返された後に優先度をリセットするようです。

    もちろん、あなたのUIが本当にCPUを消費し、同時にあなたのタスクのパワーをもっと上げたい場合はUIから取り除き、別の優先度を設定することもできます(Process.THREAD_PRIORITY_FOREGROUNDまで)。

    +2

    作成していないスレッドの優先順位が悪いのは悪いフォームです。スレッドの優先順位を制御したいのであればクールですが、 'AsyncTask'を使わず、独自の' Thread'をフォークしたり、作成したスレッドプールに 'ExecutorService'を使います。あるいは、あなたが本当に 'AsyncTask'セマンティクスを望むなら、あなた自身の' ExecutorService'を作成し、 'executeOnExecutor()'を使ってAPIレベル11+でそれをオプトインし、それらのスレッドで独自のスレッド優先度を設定してください。 – CommonsWare

    +2

    @CommonsWare 'Process.THREAD_PRIORITY'の明示的な設定をサポートする' HandlerThread'とは別のThread-ishオブジェクトが見つかりませんでした。また、 'Thread'sはデフォルトで背景prioを魔法のように持っています。私が見ているように、Androidのニーズに合わせてAndroidがスレッドの優先順位を変更しないようにすることさえできません。だから私は 'HandlerThread'以外の方法で文書化された方法を見つけなかったとき、私は他のすべての優先操作方法を同様に非公式であると見る自由を取った。しかし、そこにポイントがあります。 –

    +0

    私の謝罪、私はあなたの答えで何かを誤解していた。 'HandlerThread'は、標準Javaの' Thread'クラスから継承したものを除いて、優先順位とは何の関係もないことに注意してください。 – CommonsWare

    18

    AsyncTaskは、UIスレッドが応答残ることを確認することを助けるために低い優先度で実行されます。

    1

    パフォーマンスヒットにもかかわらず、を実行すると、がバックグラウンドでこれを実行します。いいプレイ、他はあなたとうまくやります。

    私はこれが何であるかわからないので、私は代替案を提案できません。私の最初の反応は、あなたが電話機でHTMLを再フォーマットしようとしているのは奇妙なことでした。 の電話番号であり、クワッドコアではありません。 Webサービスで再フォーマットし、その結果を電話で表示することは可能ですか?

    +0

    これは、フォーラムサーバーのコードです。タパスやその他のサポートはありません。私が考えることができる唯一の方法は、電話で再フォーマットすることです。それは私の***バグは、ブラウザ(BTWは、バックグラウンドで取得とレンダリングを行うようではない)のフォーラムを読むために、私は常に手動で各ページをズームし、スクロールする必要があるので、私のコードは、 (見出し、テーブルのほとんどのカラム、ほとんどの画像など)を表示し、WebViewでうまくフォーマットされたものを表示します。それは自分のためだけです。 Webサービスを使用しています...?私はかすかな手がかりを持っていません... – OppfinnarJocke

    0

    最終的なString str = fx.executeを呼び出す必要があります。あなたはUIスレッドから直接doinbackgroundを呼び出すべきではありません。

    関連する問題