Ausführen von Code im Hauptthread von einem anderen Thread


326

In einem Android-Dienst habe ich Threads für Hintergrundaufgaben erstellt.

Ich habe eine Situation, in der ein Thread bestimmte Aufgaben in der Nachrichtenwarteschlange des Hauptthreads veröffentlichen muss, z. B. a Runnable.

Gibt es einen Weg , um Handlervon dem Hauptthread und Post Message/ Runnableum es von meinem anderen Thread?

Vielen Dank,


2
Sie können auch einen benutzerdefinierten Rundfunkempfänger verwenden. Versuchen Sie meine Antwort hier: [Innerer Rundfunkempfänger] [1] [1]: stackoverflow.com/a/22541324/1881527
Melbourne Lopes

Es gibt viele Wege. Abgesehen von Davids Antwort und Dzeikeis Kommentar in seiner Antwort können Sie (3) einen Rundfunkempfänger verwenden oder (4) den Handler an Extras von Intent übergeben, die zum Starten des Dienstes verwendet wurden, und dann den Handler des Hauptthreads innerhalb des Dienstes mit getIntent abrufen ( ) .getExtras ().
Ashok Bijoy Debnath

2
@ sazzad-hissain-khan, Warum diese Frage aus dem Jahr 2012 mit den meisten Antworten in Java mit dem Kotlin-Tag versehen?
Tenfour04

Antworten:


617

HINWEIS: Diese Antwort hat so viel Aufmerksamkeit erhalten, dass ich sie aktualisieren muss. Seit die ursprüngliche Antwort veröffentlicht wurde, hat der Kommentar von @dzeikei fast genauso viel Aufmerksamkeit erhalten wie die ursprüngliche Antwort. Hier sind also 2 mögliche Lösungen:

1. Wenn Ihr Hintergrund-Thread einen Verweis auf ein ContextObjekt hat:

Stellen Sie sicher, dass Ihre Hintergrund-Worker-Threads Zugriff auf ein Kontextobjekt haben (dies kann der Anwendungskontext oder der Dienstkontext sein). Dann machen Sie dies einfach im Hintergrund-Worker-Thread:

// 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. Wenn Ihr Hintergrund-Thread kein ContextObjekt hat (oder benötigt)

(vorgeschlagen von @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
Danke David, es hat für mich geklappt. Wenn Sie mir Handler und impl handleMessage () erweitern könnten, würde dies den Hauptthread daran hindern, seine Nachrichten zu verarbeiten. Das ist nur eine Frage aus Neugier.
Ahmed

2
Nein. Wenn Sie eine Unterklasse Handler(oder eine Handler.CallbackSchnittstelle) verwenden, wird Ihre handleMessage()Methode NUR für Nachrichten aufgerufen, die mit Ihrem Handler veröffentlicht wurden. Der Hauptthread verwendet einen anderen Handler zum Posten / Verarbeiten von Nachrichten, sodass kein Konflikt besteht.
David Wasser

205
Ich glaube, Sie brauchen nicht einmal Kontext, wenn SieHandler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

6
Kleiner Punkt; Ihr Code geht nicht dahin, wo sich der ... gerade befindet. Es sollte seinnew Runnable(){@Override public void run() {....}};
Richard Tingle

1
@SagarDevanga Dies ist nicht der richtige Ort, um eine andere Frage zu stellen. Bitte posten Sie eine neue Frage, keinen Kommentar zu einer nicht verwandten Antwort. Auf diese Weise erhalten Sie eine bessere und schnellere Antwort.
David Wasser

138

Wie ein Kommentator unten richtig betonte, ist dies keine allgemeine Lösung für Dienste, sondern nur für Threads, die von Ihrer Aktivität aus gestartet wurden (ein Dienst kann ein solcher Thread sein, aber nicht alle sind dies). Lesen Sie zum komplizierten Thema der Kommunikation zwischen Service und Aktivität den gesamten Abschnitt "Services" des offiziellen Dokuments - es ist komplex, daher lohnt es sich, die Grundlagen zu verstehen: http://developer.android.com/guide/components/services.html #Benachrichtigungen

Die folgende Methode kann in einfachsten Fällen funktionieren.

Wenn ich Sie richtig verstehe, muss Code im GUI-Thread der Anwendung ausgeführt werden (ich kann mir nichts anderes vorstellen, das als "Haupt" -Thread bezeichnet wird). Hierfür gibt es eine Methode Activity:

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

Doc: http://developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang.Runnable%29

Hoffe das ist was du suchst.


20
OP sagt, er führt Threads in einem Service. Sie können nicht runOnUiThread()in einem verwenden Service. Diese Antwort ist irreführend und geht nicht auf die gestellte Frage ein.
David Wasser

31

Es gibt einen anderen einfachen Weg, wenn Sie keinen Zugriff auf den Kontext haben.

1). Erstellen Sie einen Handler aus dem Haupt-Looper:

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

2). Implementieren Sie eine ausführbare Schnittstelle:

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

3). Poste dein Runnable an den uiHandler:

uiHandler.post(runnable);

Das ist alles ;-) Viel Spaß mit Threads, aber vergiss nicht, sie zu synchronisieren.


29

Wenn Sie Code in einem Thread ausführen, z. B. eine Aktion verzögern, müssen Sie ihn runOnUiThreadaus dem Kontext aufrufen . Wenn sich Ihr Code beispielsweise innerhalb der MainActivityKlasse befindet, verwenden Sie Folgendes:

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

Wenn Ihre Methode entweder vom Hauptthread (UI-Thread) oder von anderen Threads aus aufgerufen werden kann, müssen Sie Folgendes überprüfen:

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

}

7
OP sagt, er führt Threads in einem Service. Sie können nicht runOnUiThread()in einem verwenden Service.
David Wasser

@ DavidWasser Ist das irgendwo dokumentiert? Die Methodendokumente erwähnen nichts darüber. developer.android.com/reference/android/app/…
Greg Brown

1
@ GregBrown Wie Sie durch Ihren Link zur ActivityDokumentation angegeben haben, runOnUiThread()ist eine Methode von Activity. Es ist keine Methode von Service, daher können Sie es nicht verwenden. Was könnte klarer sein als das?
David Wasser

@ DavidWasser Fair genug. Ich erinnere mich nicht einmal, warum ich diese Frage jetzt gestellt habe (sie wurde vor fast einem Jahr veröffentlicht).
Greg Brown

24

Ein komprimierter Codeblock lautet wie folgt:

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

Dies beinhaltet nicht die Weitergabe der Aktivitätsreferenz oder der Anwendungsreferenz.

Kotlin-Äquivalent:

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

20

Kotlin-Versionen

Wenn Sie an einer Aktivität teilnehmen , verwenden Sie

runOnUiThread {
    //code that runs in main
}

Wenn Sie einen Aktivitätskontext haben , verwenden Sie mContext

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

Wenn Sie sich an einem Ort befinden, an dem kein Kontext verfügbar ist , verwenden Sie

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

Ich bin mir nicht sicher, um welche Version von Kotlin es sich handelt, aber ich musste geschweifte Klammern hinzufügen:runOnUiThread(){...}
Wex

5

Der einfachste Weg, insbesondere wenn Sie keinen Kontext haben, wenn Sie RxAndroid verwenden, können Sie Folgendes tun:

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

5

Genauerer Kotlin-Code mit dem Handler:

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }

4

Eine Methode, die ich mir vorstellen kann, ist folgende:

1) Lassen Sie die Benutzeroberfläche an den Dienst binden.
2) Stellen Sie eine Methode wie die folgende bereit, mit der Sie Folgendes Binderregistrieren Handler:

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

3) Rufen Sie im UI-Thread die obige Methode auf, nachdem Sie an den Dienst gebunden haben:

mBinder.registerHandler(new Handler());

4) Verwenden Sie den Handler im Thread des Dienstes, um Ihre Aufgabe zu veröffentlichen:

mHandler.post(runnable);

3

HandlerThread ist eine bessere Option für normale Java-Threads in Android.

  1. Erstellen Sie einen HandlerThread und starten Sie ihn
  2. Erstellen Sie einen Handler mit Looper aus HandlerThread:requestHandler
  3. posteine RunnableAufgabe aufrequestHandler

Kommunikation mit UI-Thread von HandlerThread

  1. Erstellen Sie eine Methode Handlermit Looperfor for main thread: responseHandlerund überschreiben Sie siehandleMessage
  2. Rufen Sie innerhalb der RunnableAufgabe eines anderen Threads ( HandlerThreadin diesem Fall) sendMessageaufresponseHandler
  3. Dieser sendMessageErgebnisaufruf von handleMessagein responseHandler.
  4. Holen Sie sich Attribute aus dem Messageund verarbeiten Sie es, aktualisieren Sie die Benutzeroberfläche

Beispiel : Aktualisierung TextViewmit Daten, die von einem Webdienst empfangen wurden. Da der Webdienst für einen Nicht-UI-Thread aufgerufen werden sollte, der HandlerThreadfür den Netzwerkbetrieb erstellt wurde. Sobald Sie den Inhalt vom Webdienst erhalten haben, senden Sie eine Nachricht an Ihren Handler für den Hauptthread (UI-Thread)Handler behandelt die Nachricht und aktualisiert die Benutzeroberfläche.

Beispielcode:

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);

Nützliche Artikel:

Handlerthreads-und-warum-Sie-sollten-sie-in-Ihren-Android-Apps verwenden

android-looper-handler-handlerthread-i


2

Ich weiß, dass dies eine alte Frage ist, aber ich bin auf einen Einzeiler mit Hauptthread gestoßen, den ich sowohl in Kotlin als auch in Java verwende. Dies ist möglicherweise nicht die beste Lösung für einen Dienst, aber für den Aufruf von etwas, das die Benutzeroberfläche innerhalb eines Fragments verändert, ist dies äußerst einfach und offensichtlich.

Java (8):

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

Kotlin:

this.runOnUiThread {
     //your main thread code
}

1

Folgen Sie dieser Methode. Auf diese Weise können Sie die Benutzeroberfläche einfach über einen Hintergrund-Thread aktualisieren. runOnUiThread arbeitet am Hauptthread (UI). Ich denke, dieses Code-Snippet ist weniger komplex und einfach, besonders für Anfänger.

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();

                   }
                });
            }
        });

im Falle einer Dienstleistung

Erstellen Sie einen Handler in oncreate

 handler = new Handler();

dann benutze es so

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

Vielen Dank für dieses Code-Snippet, das möglicherweise nur begrenzte, sofortige Hilfe bietet. Eine richtige Erklärung würde ihren langfristigen Wert erheblich verbessern, indem sie zeigt, warum dies eine gute Lösung für das Problem ist, und es für zukünftige Leser mit anderen, ähnlichen Fragen nützlicher machen. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, einschließlich der von Ihnen getroffenen Annahmen.
iBug


1

Am praktischsten ist es also, Folgendes zu tun:

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()
        }
    }
}

Und danach:

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
        }
    });
}

Dies kann auch in einer Serviceklasse ohne Probleme funktionieren.


Obwohl dieser Code möglicherweise die Frage beantwortet, sollten Sie dennoch einige erklärende Sätze hinzufügen, da dies den Wert Ihrer Antwort für andere Benutzer erhöht!
MBT

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.