別のスレッドからメインスレッドでコードを実行する


326

Androidサービスで、バックグラウンドタスクを実行するためのスレッドを作成しました。

スレッドがメインスレッドのメッセージキューに特定のタスクを投稿する必要がある状況があります。 Runnable

Handlerメインスレッドを取得して投稿する方法はありますかMessage/Runnable私の他のスレッドから、それには?

おかげで、


2
カスタムブロードキャストレシーバーを使用することもできます。ここで私の回答をお試しください。[インナーブロードキャストレシーバー] [1] [1]:stackoverflow.com/a/22541324/1881527
メルボルンロペス

多くの方法があります。Davidの回答と彼の回答でのdzeikeiのコメントは別として、(3)ブロードキャストレシーバーを使用するか、(4)サービスの開始に使用されるIntentのエクストラでハンドラーを渡し、getIntent()を使用してサービス内のメインスレッドのハンドラーを取得します).getExtras()。
Ashok Bijoy Debnath 14

2
@ sazzad-hissain-khan、なぜJavaでのほとんどの回答を2012年からこの質問にkotlinタグでタグ付けするのですか?
Tenfour04

回答:


617

注:この回答は非常に注目されているため、更新する必要があります。元の回答が投稿されてから、@ dzeikeiからのコメントは、元の回答とほぼ同じくらい注目されました。だからここに2つの可能な解決策があります:

1.バックグラウンドスレッドにContextオブジェクトへの参照がある場合:

バックグラウンドワーカースレッドがContextオブジェクト(アプリケーションコンテキストまたはサービスコンテキストのいずれか)にアクセスできることを確認してください。次に、これをバックグラウンドワーカースレッドで実行します。

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

2.バックグラウンドスレッドにContextオブジェクトがない(または必要でない)場合

(@dzeikeiが推奨):

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

1
Davidのおかげでうまくいきました。もし私がHandlerとimpl handleMessage()を拡張すると、メインスレッドがメッセージを処理できなくなる可能性があります。それは好奇心からの唯一の問題です..
アーメド

2
いいえ。サブクラス化Handler(またはHandler.Callbackインターフェースを使用)するhandleMessage()場合、メソッドはハンドラーを使用して投稿されたメッセージに対してのみ呼び出されます。メインスレッドはメッセージをポスト/処理するために別のハンドラーを使用しているため、競合は発生しません。
David Wasser

205
私はあなたが使用するなら、あなたは文脈さえ必要としないと思いますHandler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

6
マイナーポイント。あなたのコードは...現在のところに行きません。それはする必要がありますnew Runnable(){@Override public void run() {....}};
リチャード・チンクル

1
@SagarDevangaこれは別の質問をするのに適切な場所ではありません。無関係な回答へのコメントではなく、新しい質問を投稿してください。そうすれば、より良く、より速い応答が得られます。
David Wasser、2015年

138

以下のコメンターが正しく指摘しているように、これはサービスの一般的な解決策ではなく、アクティビティから起動されたスレッドに対してのみです(サービスはそのようなスレッドである場合がありますが、すべてではありません)。サービスとアクティビティの通信の複雑なトピックについては、公式ドキュメントの「サービス」セクション全体をお読みください。複雑なので、基本を理解するのにお金がかかります。http//developer.android.com/guide/components/services.html #Notifications

以下の方法は、単純なケースで機能する場合があります。

私があなたを正しく理解している場合、アプリケーションのGUIスレッドで実行されるいくつかのコードが必要です(「メイン」スレッドと呼ばれる他のものについては考えられません)。このためのメソッドがありActivityます:

someActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
           //Your code to run in GUI thread here
        }//public void run() {
});

ドキュメント:http : //developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang.Runnable%29

これがあなたが探しているものであることを願っています。


20
OPは、スレッドをで実行していると言いServiceます。あなたは使用することはできませんrunOnUiThread()Service。この回答は誤解を招くものであり、尋ねられた質問には対応していません。
David Wasser 2013年

31

コンテキストにアクセスできない場合は、別の簡単な方法があります。

1)。メインルーパーからハンドラーを作成します。

Handler uiHandler = new Handler(Looper.getMainLooper());

2)。Runnableインターフェースを実装します。

Runnable runnable = new Runnable() { // your code here }

3)。RunnableをuiHandlerにポストします。

uiHandler.post(runnable);

それだけです;-)スレッドを楽しんでください。ただし、スレッドを同期することを忘れないでください。


29

スレッドでコードを実行する場合、たとえばアクションを遅延させる場合runOnUiThreadは、コンテキストから呼び出す必要があります。たとえば、コードがMainActivityクラス内にある場合は、次のコードを使用します。

MainActivity.this.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        myAction();
    }
});

メソッドをメイン(UIスレッド)または他のスレッドから呼び出すことができる場合は、次のようなチェックが必要です。

public void myMethod() {
   if( Looper.myLooper() == Looper.getMainLooper() ) {
       myAction();
   }
   else {

}

7
OPは、スレッドをで実行していると言いServiceます。あなたは使用することはできませんrunOnUiThread()Service
David Wasser 2013年

@DavidWasserそれはどこかに文書化されていますか?メソッドのドキュメントはそれについて何も言及していません。developer.android.com/reference/android/app/…
グレッグ・ブラウン

1
@GregBrown は、Activityドキュメントへのリンクで示したようにrunOnUiThread()、のメソッドですActivity。の方法ではないServiceため、使用できません。それより何がはっきりしているのでしょうか?
David Wasser

@DavidWasser十分に公正です。なぜ今私がその質問をしたのか覚えていません(ほぼ1年前に投稿されました)。
グレッグブラウン

24

圧縮コードブロックは次のとおりです。

   new Handler(Looper.getMainLooper()).post(new Runnable() {
       @Override
       public void run() {
           // things to do on the main thread
       }
   });

これには、アクティビティ参照またはアプリケーション参照の受け渡しは含まれません。

コトリン相当:

    Handler(Looper.getMainLooper()).post(Runnable {
        // things to do on the main thread
    })

20

Kotlinバージョン

あなたが活動しているときに、使用してください

runOnUiThread {
    //code that runs in main
}

ときあなたはアクティビティコンテキストを持って、mContextは、使用します

mContext.runOnUiThread {
    //code that runs in main
}

あなたはどこかにいるときはありませんコンテキスト利用でき、その後、使用

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}

Kotlinのバージョンがわからないが、中括弧を追加する必要があった:runOnUiThread(){...}
Wex

5

特にコンテキストがない場合、RxAndroidを使用している場合、最も簡単な方法は次のとおりです。

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}


4

私が考えることができる1つの方法はこれです:

1)UIをサービスにバインドします。
2)Binderあなたを登録するによって以下のようなメソッドを公開しますHandler

public void registerHandler(Handler handler) {
    mHandler = handler;
}

3)UIスレッドで、サービスにバインドした後、上記のメソッドを呼び出します。

mBinder.registerHandler(new Handler());

4)サービスのスレッドのハンドラーを使用してタスクを投稿します。

mHandler.post(runnable);

3

HandlerThread Androidの通常のJavaスレッドよりも優れたオプションです。

  1. HandlerThreadを作成して開始する
  2. HandlerThreadからルーパーを使用してハンドラーを作成します。requestHandler
  3. post上のRunnableタスクrequestHandler

からのUIスレッドとの通信 HandlerThread

  1. Handlerwith Looperfor main thread: responseHandlerおよびオーバーライドhandleMessageメソッドを作成します
  2. 内部Runnable(他のスレッドのタスクHandlerThreadこの場合)、呼び出しsendMessageresponseHandler
  3. このsendMessage結果、が呼び出さhandleMessageresponseHandlerます。
  4. から属性を取得してMessage処理し、UIを更新する

TextViewWebサービスから受信したデータで更新します。Webサービスは非UIスレッドで呼び出す必要があるHandlerThreadため、ネットワーク操作用に作成されます。Webサービスからコンテンツを取得したら、メインスレッド(UIスレッド)ハンドラーにメッセージを送信します。Handler。これにより、メッセージが処理され、UIが更新されます。

サンプルコード:

HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        txtView.setText((String) msg.obj);
    }
};

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Runnable", "Before IO call");
            URL page = new URL("http://www.your_web_site.com/fetchData.jsp");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ((line = buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Runnable", "After IO call:"+ text.toString());
            Message msg = new Message();
            msg.obj = text.toString();
            responseHandler.sendMessage(msg);


        } catch (Exception err) {
            err.printStackTrace();
        }
    }
};
requestHandler.post(myRunnable);

役立つ記事:

ハンドラースレッドと、なぜあなたは自分のAndroidアプリでそれらを使用する必要がありますか

android-looper-handler-handlerthread-i


2

これは古い質問であることは知っていますが、KotlinとJavaの両方で使用しているメインスレッドのワンライナーに出くわしました。これはサービスに最適なソリューションではないかもしれませんが、フラグメント内のUIを変更するものを呼び出す場合、これは非常に単純で明白です。

Java(8):

 getActivity().runOnUiThread(()->{
      //your main thread code
 });

コトリン:

this.runOnUiThread {
     //your main thread code
}

1

この方法に従ってください。この方法を使用すると、単にバックグラウンドスレッドからUIを更新できます。runOnUiThreadはmain(UI)スレッドで動作します。このコードスニペットは、特に初心者にとってはそれほど複雑ではなく簡単です。

AsyncTask.execute(new Runnable() {
            @Override
            public void run() {

            //code you want to run on the background
            someCode();

           //the code you want to run on main thread
 MainActivity.this.runOnUiThread(new Runnable() {

                    public void run() {

/*the code you want to run after the background operation otherwise they will executed earlier and give you an error*/
                        executeAfterOperation();

                   }
                });
            }
        });

サービスの場合

oncreateでハンドラを作成する

 handler = new Handler();

次に、このように使用します

 private void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }

このコードスニペットをありがとうございます。このコードスニペットは、制限された即時のヘルプを提供する可能性があります。適切な説明が大幅に長期的な価値を向上させるだろう示すことによって、なぜこれが問題に良い解決策であり、他の、同様の質問を将来の読者にそれがより便利になるだろう。回答を編集して、仮定を含めて説明を追加してください。
iBug 2018


1

したがって、最も便利なのは次のようなことです。

import android.os.AsyncTask
import android.os.Handler
import android.os.Looper

object Dispatch {
    fun asyncOnBackground(call: ()->Unit) {
        AsyncTask.execute {
            call()
        }
    }

    fun asyncOnMain(call: ()->Unit) {
        Handler(Looper.getMainLooper()).post {
            call()
        }
    }
}

以降:

Dispatch.asyncOnBackground {
    val value = ...// super processing
    Dispatch.asyncOnMain { completion(value)}
}

0
public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

これは問題なくサービスクラスでも機能します。


このコードは質問に答えるかもしれませんが、他のユーザーにとっての答えの価値を高めるため、いくつかの説明文を追加することを検討するかもしれません!
MBT、

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.