Androidの基本:UIスレッドでコードを実行する


450

UIスレッドでコードを実行する観点から、以下の間に違いはありますか?

MainActivity.this.runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

または

MainActivity.this.myView.post(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

そして

private class BackgroundTask extends AsyncTask<String, Void, Bitmap> {
    protected void onPostExecute(Bitmap result) {
        Log.d("UI thread", "I am the UI thread");
    }
}

私の質問を明確にするために:私はこれらのコードがサービススレッド、通常はリスナーから呼び出されたと想定しています。また、AsynkTaskのdoInBackground()関数、または最初の2つのスニペットの前に呼び出される新しいTask(...)のいずれかで達成するための重い作業があると想定しました。とにかく、AsyncTaskのonPostExecute()がイベントキューの最後に配置されていますよね?
Luky

回答:


288

それらはどれもまったく同じではありませんが、すべて同じ効果があります。

第一と第二との間の差を使用することが起こる場合、すなわち上でコードを実行するときにアプリケーションのメインスレッド、最初のものは、( runOnUiThread())が実行されますRunnable直ちに。2つ目(post())は、Runnableすでにメインアプリケーションスレッドにいる場合でも、常にをイベントキューの最後に配置します。

3番目の方法は、のインスタンスを作成して実行すると仮定すると、最終的にに相当することを行う前にBackgroundTask、デフォルトのno-opを実行するためにスレッドプールからスレッドを取得するために多くの時間を浪費します。これは3つの中で最も効率が悪いです。使用して、あなたは実際の使用のためだけではなく、バックグラウンドスレッドでやるべき仕事を持っている場合。doInBackground()post()AsyncTaskonPostExecute()


27
AsyncTask.execute()いずれにしても、UIスレッドから呼び出す必要があることに注意してください。これにより、すべてのバックグラウンド作業を移動して適切にdoInBackground()使用しない限り、バックグラウンドスレッドからUIスレッドでコードを実行するだけのユースケースでは、このオプションは役に立たなくなりAsyncTaskます。
kabuko 2012年

@kabuko AsyncTaskUIスレッドから呼び出していることを確認するにはどうすればよいですか?
Neil Galiaskarov 2016

@NeilGaliaskarovこれは確かなオプションのように見えます:stackoverflow.com/a/7897562/1839500
Dick Lucas

1
@NeilGaliaskarovboolean isUiThread = (Looper.getMainLooper().getThread() == Thread.currentThread());
ban-

1
M使用以上のバージョンの@NeilGaliaskarovLooper.getMainLooper().isCurrentThread
Balu Sangem

251

私はHPPコメントからのものが好きです、それはパラメーターなしでどこでも使用できます:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

2
これはどれくらい効率的ですか?他のオプションと同じですか?
EmmanuelMess

59

を使用して4番目の方法があります Handler

new Handler().post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});

56
これには注意が必要です。非UIスレッドでハンドラーを作成すると、非UIスレッドにメッセージが投稿されるからです。ハンドラは、デフォルトでは、それが作成されたスレッドにメッセージを送信します。
lujop 2013年

107
メインUIスレッドで実行するにはdoを使用しますnew Handler(Looper.getMainLooper()).post(r)。これは、Looper.getMainLooper()mainへの静的呼び出しを行うのに適した方法postOnUiThread()ですがMainActivity、スコープ内のインスタンスが必要です。
HPP 2013年

1
@HPP私はこの方法を知りませんでした。アクティビティもビューもない場合に最適な方法です。よく働く!どうもありがとうございました!
スルフカイン2014

@lujop AsyncTaskのonPreExecuteコールバックメソッドの場合も同様です。
Sreekanth Karumanaghat

18

Pomberの答えは許容範囲ですが、私は新しいオブジェクトを繰り返し作成することはあまり好きではありません。最良のソリューションは、常にメモリの浪費を軽減しようとするものです。はい、自動ガベージコレクションがありますが、モバイルデバイスでのメモリの節約はベストプラクティスの範囲内です。以下のコードは、サービスのTextViewを更新します。

TextViewUpdater textViewUpdater = new TextViewUpdater();
Handler textViewUpdaterHandler = new Handler(Looper.getMainLooper());
private class TextViewUpdater implements Runnable{
    private String txt;
    @Override
    public void run() {
        searchResultTextView.setText(txt);
    }
    public void setText(String txt){
        this.txt = txt;
    }

}

次のような場所から使用できます。

textViewUpdater.setText("Hello");
        textViewUpdaterHandler.post(textViewUpdater);

7
GCとオブジェクトの作成について言及する場合は+1。しかし、私は必ずしも同意しませんThe best solutions are always the ones that try to mitigate memory hog。には他にも多くの基準がbestあり、これにはわずかな匂いがありpremature optimizationます。つまり、作成されたオブジェクトの数が問題になるほど十分に呼び出していることがわかっている場合を除いて(おそらくアプリがガベージを作成している他の1万の方法と比較して)、best最も単純な(理解しやすい) )コードを作成し、他のタスクに進みます。
ToolmakerSteve 2015年

2
ちなみに、メインのUIスレッドへの投稿には一般的に役立つため、orやのtextViewUpdaterHandlerような名前を付ける方が適切です。TextViewUpdaterクラスにまったく関連付けられていません。私はそれをそのコードの残りの部分から遠ざけ、他の場所で使用できることを明確にします...残りのコードは疑わしいです。なぜなら、1つのオブジェクトを動的に作成しないようにするには、単一の呼び出しになる可能性のあるものを2つのステップと、それは一時的に使用する長命のオブジェクトに依存しています。不必要な複雑さ、そしてスレッドセーフではありません。メンテナンスが容易ではありません。uiHandlermainHandlersetTextpost
ToolmakerSteve 2015年

あなたがいる場合、本当にこれは、それは、キャッシングの価値があるように何度も呼び出される状況持っているuiHandlertextViewUpdater、その後に変更することで、あなたのクラスを改善public void setText(String txt, Handler uiHandler)し、メソッドの行を追加uiHandler.post(this); :その後、発信者は1つのステップで行うことができますが textViewUpdater.setText("Hello", uiHandler);。その後、将来、スレッドセーフにする必要がある場合、メソッドはそのステートメントをロックオン内にラップできuiHandler、呼び出し元は変更されません。
ToolmakerSteve 2015年

Runnableは一度しか実行できないと確信しています。これはあなたの考えを完全に壊します。それは全体的にいいことです。
Nativ

@Nativいいえ、Runnableは複数回実行できます。スレッドはできません。
Zbyszek 2016年

8

Android P以降では次のものが使用できますgetMainExecutor()

getMainExecutor().execute(new Runnable() {
  @Override public void run() {
    // Code will run on the main thread
  }
});

Androidの開発者向けドキュメント

このコンテキストに関連付けられたメインスレッドでエンキューされたタスクを実行するエグゼキュータを返します。これは、アプリケーションコンポーネント(アクティビティ、サービスなど)への呼び出しをディスパッチするために使用されるスレッドです。

CommonsBlogから:

コンテキストでgetMainExecutor()を呼び出して、メインアプリケーションスレッドでジョブを実行するエグゼキューターを取得できます。Looperとカスタムエグゼキューター実装を使用してこれを達成する他の方法がありますが、これはより簡単です。


7

フラグメントで使用する必要がある場合は、使用する必要があります

private Context context;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        this.context = context;
    }


    ((MainActivity)context).runOnUiThread(new Runnable() {
        public void run() {
            Log.d("UI thread", "I am the UI thread");
        }
    });

の代わりに

getActivity().runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

ページャーフラグメントのような状況ではnullポインター例外が発生するため


1

こんにちは、これは基本的な質問です

ハンドラーを使用する

new Handler().post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});

runOnUiThreadではなく汎用ハンドラを使用することにした理由はありますか?
ThePartyTurtle

これはjava.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()、UIスレッドから呼び出されなかった場合にを提供します。
Roc Boronat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.