Gibt es im Hinblick auf die Ausführung von Code im UI-Thread einen Unterschied zwischen:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
oder
MainActivity.this.myView.post(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
und
private class BackgroundTask extends AsyncTask<String, Void, Bitmap> {
protected void onPostExecute(Bitmap result) {
Log.d("UI thread", "I am the UI thread");
}
}
Antworten:
Keines davon ist genau gleich, obwohl alle den gleichen Nettoeffekt haben.
Der Unterschied zwischen der ersten und der zweite ist , dass , wenn Sie sich gerade auf dem Hauptanwendungsthread , wenn der Code ausgeführt wird , der erste (
runOnUiThread()
) die ausgeführt wirdRunnable
sofort. Mit der zweiten (post()
) steht dieRunnable
Warteschlange immer am Ende der Ereigniswarteschlange, auch wenn Sie sich bereits im Hauptanwendungsthread befinden.Die dritte, vorausgesetzt, Sie erstellen und führen eine Instanz von aus
BackgroundTask
, verschwendet viel Zeit damit, einen Thread aus dem Thread-Pool zu holen, um ein Standard-No-Op auszuführendoInBackground()
, bevor Sie schließlich das tun, was a entsprichtpost()
. Dies ist bei weitem die am wenigsten effiziente der drei. VerwendenAsyncTask
Sie diese Option, wenn Sie tatsächlich in einem Hintergrund-Thread arbeiten müssen, nicht nur für die Verwendung vononPostExecute()
.quelle
AsyncTask.execute()
Sie auch, dass Sie ohnehin vom UI-Thread aus aufrufen müssen, wodurch diese Option für den Anwendungsfall unbrauchbar wird, Code auf dem UI-Thread einfach aus einem Hintergrund-Thread auszuführen, es sei denn, Sie verschieben Ihre gesamte Hintergrundarbeit in den UI-ThreaddoInBackground()
und verwenden ihnAsyncTask
ordnungsgemäß.AsyncTask
vom UI-Thread aus anrufe ?boolean isUiThread = (Looper.getMainLooper().getThread() == Thread.currentThread());
Looper.getMainLooper().isCurrentThread
Ich mag den von HPP Kommentar , er kann überall ohne Parameter verwendet werden:
quelle
Es gibt einen vierten Weg mit
Handler
quelle
new Handler(Looper.getMainLooper()).post(r)
ist die bevorzugte Methode, umLooper.getMainLooper()
einen statischen Aufruf von main durchzuführen , währendpostOnUiThread()
eine Instanz vonMainActivity
in scope vorhanden sein muss.Die Antwort von Pomber ist akzeptabel, aber ich bin kein großer Fan davon, immer wieder neue Objekte zu erstellen. Die besten Lösungen sind immer diejenigen, die versuchen, das Gedächtnisproblem zu verringern. Ja, es gibt eine automatische Speicherbereinigung, aber die Speichererhaltung auf einem mobilen Gerät fällt unter die Grenzen der Best Practice. Der folgende Code aktualisiert eine Textansicht in einem Dienst.
Es kann von überall wie folgt verwendet werden:
quelle
The best solutions are always the ones that try to mitigate memory hog
. Es gibt viele andere Kriterien fürbest
, und dies hat einen leichten Geruch vonpremature optimization
. Das heißt, wenn Sie nicht wissen, dass Sie es so nennen, dass die Anzahl der erstellten Objekte ein Problem darstellt (im Vergleich zu den zehntausend anderen Methoden, mit denen Ihre App wahrscheinlich Müll erzeugt), ist es möglicherweisebest
am einfachsten zu schreiben (am einfachsten zu verstehen) ) Code und fahren Sie mit einer anderen Aufgabe fort.textViewUpdaterHandler
sollte besser so etwas wieuiHandler
oder genannt werdenmainHandler
, da es im Allgemeinen für jeden Beitrag zum Haupt-UI-Thread nützlich ist. Es ist überhaupt nicht an Ihre TextViewUpdater-Klasse gebunden. Ich würde es vom Rest dieses Codes entfernen und klarstellen, dass es an anderer Stelle verwendet werden kann ... Der Rest des Codes ist zweifelhaft, da Sie, um zu vermeiden, dass ein Objekt dynamisch erstellt wird, einen einzelnen Aufruf unterbrechen zwei SchrittesetText
undpost
, die auf einem langlebigen Objekt beruhen, das Sie als temporäres verwenden. Unnötige Komplexität und nicht threadsicher. Nicht leicht zu pflegen.uiHandler
undtextViewUpdater
dann Ihre Klasse zu verbessern,public void setText(String txt, Handler uiHandler)
indem Sie zur Methodenzeile wechseln und diese hinzufügen.uiHandler.post(this);
Dann kann der Aufrufer in einem Schritt Folgendes tun :textViewUpdater.setText("Hello", uiHandler);
. Wenn die Methode in Zukunft threadsicher sein muss, kann sie ihre Anweisungen in eine Sperre einschließenuiHandler
, und der Aufrufer bleibt unverändert.Ab Android P können Sie verwenden
getMainExecutor()
:Aus den Android-Entwicklerdokumenten :
Aus dem CommonsBlog :
quelle
Wenn Sie in Fragment verwenden müssen, sollten Sie verwenden
anstatt
Weil es in einigen Situationen wie dem Pager-Fragment eine Nullzeiger-Ausnahme gibt
quelle
Hallo Leute, dies ist eine grundlegende Frage, die ich weg erzähle
Verwenden Sie Handler
quelle
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
wenn es nicht vom UI-Thread aufgerufen wird.