Android
Handler
Tutorial und Beispiele
Ein Handler
ist eine Threading
-Klasse, die im android.os
-Paket definiert ist und über die wir Message
- und Runnable
-Objekte senden und verarbeiten können, die mit der MessageQueue
eines Threads
verbunden sind.
Man beginnt mit der Erstellung einer Handler
Instanz. Dann wird diese Instanz mit einem einzelnen Thread
sowie der MessageQueue
dieses Threads
verbunden. Der Handler
, den Sie gerade erstellt haben, wird dann an den Thread
oder die Nachrichtenwarteschlange des Threads
gebunden, der ihn erstellt hat. Daher kann er dann Nachrichten und runnables
an diese Nachrichtenwarteschlange liefern und sie ausführen, wenn sie aus der Nachrichtenwarteschlange kommen.
Verwendungen von Handler
Der Handler
hat zwei Hauptverwendungen
- Einplanung von Nachrichten und
runnables
, die in der Zukunft ausgeführt werden müssen. - Enqueueing von Aktionen, die im Hintergrund ausgeführt werden müssen
Thread
.
Handler
und Looper
Handler" ist eine Klasse, die grundlegend dafür ist, wie wir in Android auf Infrastrukturebene Threading
betreiben. Sie arbeitet Hand in Hand mit dem Looper
. Zusammen untermauern sie alles, was der Haupt-Thread tut - einschließlich des Aufrufs der Lebenszyklus-Methoden von "Activity".
Looper" kümmert sich um das Dispatching der Arbeit in seiner Nachrichtenschleife "Thread". Auf der anderen Seite wird Handler
zwei Aufgaben erfüllen:
- Erstens wird er eine Schnittstelle bereitstellen, um Nachrichten an seine
Looper
Warteschlange.- zweitens wird er den Callback für die Verarbeitung dieser Nachrichten implementieren, wenn sie vom
Looper
versandt werden.
- zweitens wird er den Callback für die Verarbeitung dieser Nachrichten implementieren, wenn sie vom
Jeder Handler
wird an einen einzigen Looper
gebunden und damit an einen Thread
und dessen Looper
MessageQueue.
Wir sagten, dass Handler
eine Schnittstelle bietet, um Arbeit an Looper
-Threads zu übergeben. Abgesehen davon, definiert Handler
auch den Code, der die übermittelten Nachrichten verarbeitet.
Im folgenden Code zum Beispiel wird die Klasse MyHandler
die Methode handleMessage()
des Handlers
außer Kraft setzen.
Hier schreiben wir dann unseren Code für die Nachrichtenverarbeitung:
public class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// your message handling code here
}
}
Nehmen wir an, wir haben einen Thread
namens MyThread
erstellt. Wir können dann unseren Handler
innerhalb unseres MyThread
Threads
über den Standard
Konstrukteur
Handler()`.
Daher wird myHandler
an den Looper
des aktuellen Threads
angehängt, anstatt an den Looper
des Hauptthreads`:
public class MyThread extends Thread{
private Handler myHandler;
@Override
public void run() {
Looper.prepare();
myHandler = new MyHandler();
Looper.loop();
}
public Handler getHandler(){
return myHandler;
}
}
Dann wird der Looper
Thread
, sobald er gestartet ist, in der loop()
Methode der Looper
Klasse darauf warten, dass Nachrichten zu seiner Warteschlange hinzugefügt werden.
Dann kann ein anderer Thread
eine Nachricht zu dieser Warteschlange hinzufügen. Er kann das mit der Methode submit()
tun.
Wenn das passiert, wird der wartende Thread
die Nachricht an unser Ziel MyHandler
senden, indem er die handleMessage()
Methode des Handlers
aufruft.
Die Handler
Instanz/Objekt erlaubt es uns, Nachrichten an die Handler
Klasse von jedem Thread
zu senden und als Folge davon werden sie immer an den Looper
Thread
gesendet und vom richtigen Handler
behandelt,
Handler
Schnelle Beispiele
Schauen wir uns schnelle Beispiele, Schnipsel und HowTos für Handler
an.
1. Wie man eine Splash-Aktivität über Handler
zeigt
Unser Ziel ist es, eine Splash-Aktivität über Handler
anzuzeigen.
Nehmen wir an, Sie haben dieses Layout als unsere activity_splash
. Es hat eine Bildansicht und mehrere Textansichten.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android_id="@+id/activity_splash"
android_layout_width="match_parent"
android_layout_height="match_parent"
tools_context="com.pchef.cc.personalchef.Splash">
<ImageView
android_layout_width="match_parent"
android_layout_height="match_parent"
android_id="@+id/s_img"
android_alpha="0.7"
android_src="@drawable/splash"
android_scaleType="centerCrop"/>
<LinearLayout
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_orientation="vertical"
android_layout_centerInParent="true">
<TextView
android_layout_width="200dp"
android_layout_height="wrap_content"
android_text="Personal Chef"
android_layout_gravity="center"
android_gravity="center"
android_textColor="#fff"
android_fontFamily="cursive"
android_textStyle="bold|italic"
android_layout_marginBottom="50dp"
android_textSize="60dp"/>
<TextView
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_gravity="center"
android_textColor="#fff"
android_textSize="22dp"
android_fontFamily="sans-serif-condensed"
android_id="@+id/tv1"
android_text="Dont know what to cook ?"
/>
<TextView
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_id="@+id/tv2"
android_gravity="center"
android_layout_marginLeft="20dp"
android_layout_marginRight="20dp"
android_textSize="18dp"
android_textColor="#fff"
android_fontFamily="sans-serif-condensed"
android_text="Our Personal Chef Can help you"
android_layout_marginTop="14dp" />
</LinearLayout>
</RelativeLayout>
Dann können wir unsere SpashActivity
erstellen. Sie leitet sich von der AppCompatActivity ab. Wir machen mehrere Importe, darunter den Handler
selbst sowie Glide, eine Imageloader-Bibliothek.
import android.content.Intent;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.Spinner;
import com.bumptech.glide.Glide;
import java.util.concurrent.TimeUnit;
public class Splash extends AppCompatActivity {...}
Wir werden eine ImageView als Hintergrund haben:
ImageView background;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
background = (ImageView) findViewById(R.id.s_img);
Wir werden Glide verwenden, um das Bild in eine ImageView zu laden:
Glide.with(this)
.load(R.drawable.splash)
.into(background);
Zum Schluss instanziieren wir unseren handler
:
final Handler handler = new Handler();
Dann rufen wir die Methode postDelayed()
auf und übergeben ein Runnable
, in dem wir die Methode run()
implementieren.
Innerhalb der run()
-Methode erledigen wir unsere Hintergrundaufgaben. Beachten Sie, dass wir auch die Verzögerung in Millisekunden übergeben.
handler.postDelayed(new Runnable() {
@Override
public void run() {
//Do something after delay
finish();
startActivity(new Intent(Splash.this, Home.class));
}
}, 3000);
}
}
3. Wie man einen Handler
benutzt, um WebView zu aktualisieren
In diesem kurzen Beispiel wollen wir die postDelayed()
-Methode von Handler
zum Aktualisieren eines Webviews verwenden. Sie aktualisieren eine Webansicht mit der reload()
Methode der Webansicht. Wir übergeben die Verzögerungszeit in Millisekunden.
void refreshWebPage(){
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
LightningView webview = getCurrentWebView();
if (webview != null) {
webview.reload();
}
}
}, 200);
}
4. Wie man einen Handler
zum Laden von Animationen verwendet
Android ist ein animationsreiches Framework. Die Möglichkeit, Animationen zu verwenden, wurde durch das Vorhandensein eines Hnadlers vereinfacht. Schauen wir uns ein Beispiel-Snippet an, das uns zeigt, wie man eine Animation mit einem Handler
laden kann.
Wir übergeben die zu animierende Ansicht, die Ressource der Animation, die Verzögerungszeit und das Context-Objekt. Wir werden die Handler
Methode postDelayed
verwenden. Dort übergeben wir unsere anonyme Klasse Runnable
sowie die Verzögerungszeit.
public static void animationIn(final View view, final int animation, int delayTime, final Context context) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
Animation inAnimation = AnimationUtils.loadAnimation(
context.getApplicationContext(), animation);
view.setAnimation(inAnimation);
view.setVisibility(View.VISIBLE);
}
}, delayTime);
}
Das war Animation in, nun haben wir auch Animation out:
public static void animationOut(final View view, final int animation, int delayTime, final boolean isViewGone, final Context context) {
view.setVisibility(View.VISIBLE);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
Animation outAnimation = AnimationUtils.loadAnimation(
context.getApplicationContext(), animation);
view.setAnimation(outAnimation);
if (isViewGone)
view.setVisibility(View.GONE);
else
view.setVisibility(View.INVISIBLE);
}
}, delayTime);
}
Handler
post()
Handler
hat eine Methode namens post()
:
post(Runnable r)
Wie Sie sehen können, nimmt diese Methode ein runnable Objekt. Die Methode post()
bewirkt, dass das Runnable
r der Nachrichtenwarteschlange hinzugefügt wird.
Schauen wir uns einige Beispiele aus der realen Welt für diese Methode an:
1. Verwenden Sie die post()
Methode von Handler
, um eine Tastatur zu öffnen
Hier wird beschrieben, wie man die post()
-Methode des Handlers
verwendet, um eine Tastatur zu öffnen.
public void openIME(final EditText v) {
final boolean focus = v.requestFocus();
if (v.hasFocus()) {
final Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
InputMethodManager mgr = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
boolean result = mgr.showSoftInput(v, InputMethodManager.SHOW_FORCED);
log.debug("openIME " + focus + " " + result);
}
});
}
}
2. Handler
's post()
-Methode mit Executor
Ein Executor
ist ein Objekt, das eingereichte Runnable
Aufgaben ausführt.
public DownloadStatusDeliveryImpl(final Handler handler) {
this.mDownloadStatusPoster = new Executor() {
public void execute(Runnable command) {
handler.post(command);
}
};
}
3. Full Handler
Wiederverwendbare Utility-Klasse
Hier ist ein Beispiel für eine Utility-Klasse, die die Verwendung von Handler
weiter erforscht:
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
public final class HandlerUtils {
private HandlerUtils() {
}
public static void uiPost(Runnable action) {
if (action == null) {
return;
}
uiHandler().post(action);
}
public static void uiPostDelayed(Runnable action, long delayMillis) {
if (action == null) {
return;
}
uiHandler().postDelayed(action, delayMillis);
}
public static void uiRemoveCallbacks(Runnable action) {
uiHandler().removeCallbacks(action);
}
public static void threadPost(Runnable action) {
if (action == null) {
return;
}
threadHandler().post(action);
}
public static void threadPostDelayed(Runnable action, long delayMillis) {
if (action == null) {
return;
}
threadHandler().postDelayed(action, delayMillis);
}
public static void threadRemoveCallbacks(Runnable action) {
threadHandler().removeCallbacks(action);
}
private static Handler uiHandler() {
return Holder.handler;
}
private interface Holder {
Handler handler = new Handler(Looper.getMainLooper());
}
private static Handler sThreadHandler;
private static synchronized Handler threadHandler() {
if (sThreadHandler == null) {
HandlerThread thread = new HandlerThread("HandlerUtils.sThreadHandler");
thread.start();
sThreadHandler = new Handler(thread.getLooper());
}
return sThreadHandler;
}
}
4. Wie man eine angegebene Runnable
auf dem Hauptthread ausführt
Wir übergeben die Aktion, die auf dem UI Thread
ausgeführt werden soll.
public final class HandlerUtils {
private static Handler handler = new Handler(Looper.getMainLooper());
public static void runOnUiThread(Runnable action) {
if (Looper.myLooper() == Looper.getMainLooper()) {
action.run();
}
else {
handler.post(action);
}
}
}
Schauen wir uns einige vollständige Beispiele an.
Beispiel 1: Verschiedene Handler
Beispiele
In diesem Beispiel werden verschiedene praktische Anwendungsszenarien untersucht, in die man bei der Verwendung der Klasse android.os.Handler
geraten kann.
Schritt 1: Java- oder Kotlin-Projekt erstellen
Erstellen Sie ein Java-Projekt in android studio. Sie können auch ein Kotlin-Projekt erstellen und den Code-Konverter verwenden, um von Java nach Kotlin zu konvertieren.
Schritt 2: Abhängigkeiten
Für dieses Projekt werden keine Abhängigkeiten benötigt.
Schritt 3: Berechtigungen
Für dieses Projekt werden keine Berechtigungen benötigt.
Schritt 4: Layout gestalten
Hier fügen Sie eine Reihe von Textansichten zu Ihrem Hauptaktivitätslayout hinzu. Fügen Sie auch eine Schaltfläche hinzu. Sie können sie in einem vertikalen linearen Layout anordnen:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.lomza.examples.handlers.MainActivity">
<TextView
android:id="@+id/tv_01"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_02"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_03"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_04"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_05"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_06"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button_07"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Post with a view Handler"/>
<TextView
android:id="@+id/tv_07"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Schritt 5: Code schreiben
Wir haben dann unseren Code geschrieben. Sie beginnen mit der Erweiterung der AppCompatActivity
, um unsere Hauptaktivität zu erstellen.
Die Hauptaktivität mit Methoden zur Demonstration der Verwendung von Handlern, Runnables und Messages:
public class MainActivity extends AppCompatActivity {
private TextView tv01;
private TextView tv02;
private TextView tv03;
private TextView tv04;
private TextView tv05;
private TextView tv06;
private TextView tv07;
private Button button07;
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
private Handler currentThreadHandler;
private Handler decorViewHandler;
private final ChangeTextHandler customHandler = new ChangeTextHandler(this);
private final Handler notLeakyHandler = new Handler();
private final Runnable notLeakyRunnable = new ChangeTextRunnable(this, "Hi from leak-safe Runnable!");
private static final String TAG = "[Handlers]";
private static final String BUNDLE_KEY = "greeting";
Zuerst kann man mit dem folgenden Code eine Aufgabe mit einem gewöhnlichen thread
posten:
private void postTaskWithOrdinaryThread() {
Runnable notForHandlerTask = new Runnable() {
@Override
public void run() {
// as written here - https://developer.android.com/guide/components/processes-and-threads.html#Threads,
// do NOT access the Android UI toolkit from outside the UI thread, as sth unexpected may happen
// for instance, you might get android.view.ViewRootImpl$CalledFromWrongThreadException
tv01.setText("Hi from Thread(runnable)!");
// if you call thread.run(), this would be TRUE, as no new Thread would be created
// read the explanation here - http://stackoverflow.com/a/35264580/655275
Log.d(TAG, "[postTaskWithOrdinaryThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
Thread thread = new Thread(notForHandlerTask);
thread.start();
}
Und hier ist, wie man eine Aufgabe mit handler
auf den Haupt- thread
posten kann:
@UiThread
private void postTaskWithHandlerOnMainThread() {
Runnable mainThreadTask = new Runnable() {
@Override
public void run() {
// since we use Looper.getMainLooper(), we can safely update the UI from here
tv02.setText("Hi from Handler(Looper.getMainLooper()) post!");
Log.d(TAG, "[postTaskWithHandlerOnMainThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
mainThreadHandler.post(mainThreadTask);
}
Hier ist, wie Sie eine Aufgabe mit handler
auf den konkurrierenden Thread
posten können:
private void postTaskWithHandlerOnCurrentThread() {
currentThreadHandler = new Handler();
Runnable currentThreadTask = new Runnable() {
@Override
public void run() {
// since we use current thread (and from onCreate(), it's the UI thread), we can safely update the UI from here
tv03.setText("Hi from Handler() post!");
Log.d(TAG, "[postTaskWithHandlerOnCurrentThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
currentThreadHandler.post(currentThreadTask);
}
Und hier ist, wie Sie Aufgabe innerhalb des Hintergrund thread
posten können:
private void postTaskInsideBackgroundTask() {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
// pretend to do something "background-y"
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
tv04.setText("Hi from a Handler inside of a background Thread!");
}
});
}
});
backgroundThread.start();
}
Hier sehen Sie, wie Sie die Annotation @UiThread
verwenden können, um eine Aufgabe mit diesem Fenster und Textview zu posten:
@UiThread
private void postTaskWithThisWindowAndTextViewHandlers() {
// this line will return null from onCreate() (and even if called from onResume()) and cause NPE when trying to post();
// this is because the handler isn't attached to the view if it's not fully visible
decorViewHandler = getWindow().getDecorView().getHandler();
Runnable decorViewTask = new Runnable() {
@Override
public void run() {
// View's post() uses UI handler internally
tv07.post(new Runnable() {
@Override
public void run() {
tv07.setText("Hi from getWindow().getDecorView().getHandler() > TextView.post()!");
Log.d(TAG, "[postTaskWithThisWindowAndTextViewHandlers] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
});
}
};
decorViewHandler.post(decorViewTask);
}
Und hier sehen Sie, wie Sie eine Aufgabe mit handler
im Hintergrund thread
posten können:
private void postTaskWithHandlerOnBackgroundThread() {
final Runnable pretendsToBeSomeOtherTask = new Runnable() {
@Override
public void run() {
Log.d(TAG, "[postTaskWithHandlerOnBackgroundThread] Is there a looper? " + (Looper.myLooper() != null));
// you'll get java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
// this is because Handlers need to have a Looper associated with them; when the task was run on the main thread,
// main thread looper was used, but if we call this from the background thread, there is NO looper to use
// read more - https://developer.android.com/reference/android/os/Looper.html
postTaskWithHandlerOnCurrentThread();
}
};
final Thread thread = new Thread(pretendsToBeSomeOtherTask);
thread.start();
}
Hier sehen Sie, wie Sie eine Aufgabe mit einem nicht undichten Handler
und Runnable
posten können:
private void postTaskWithNotLeakyHandlerAndRunnable() {
// in order to eliminate leaks, both Handler and Runnable should be static
// static inner classes do not hold an implicit reference to the outer class
// it seems like a lot of useless work, but it's the most accurate and bug-free way
// read more - http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
notLeakyHandler.postDelayed(notLeakyRunnable, 500);
}
Hier ist der vollständige Code;
MainActivity.java
package com.lomza.examples.handlers;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
/**
* Main activity with methods to demonstrates the usage of Handlers, Runnables, and Messages :)
*
* @author Antonina Tkachuk
*/
public class MainActivity extends AppCompatActivity {
private TextView tv01;
private TextView tv02;
private TextView tv03;
private TextView tv04;
private TextView tv05;
private TextView tv06;
private TextView tv07;
private Button button07;
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
private Handler currentThreadHandler;
private Handler decorViewHandler;
private final ChangeTextHandler customHandler = new ChangeTextHandler(this);
private final Handler notLeakyHandler = new Handler();
private final Runnable notLeakyRunnable = new ChangeTextRunnable(this, "Hi from leak-safe Runnable!");
private static final String TAG = "[Handlers]";
private static final String BUNDLE_KEY = "greeting";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
@Override
protected void onStart() {
super.onStart();
postTaskWithOrdinaryThread();
postTaskWithHandlerOnMainThread();
postTaskWithHandlerOnCurrentThread();
postTaskInsideBackgroundTask();
//postTaskWithHandlerOnBackgroundThread();
postTaskWithNotLeakyHandlerAndRunnable();
sendMessageToChangeTextHandler();
}
@Override
protected void onStop() {
super.onStop();
// when posting Runnables or Messages, always remember to call removeCallbacks() or removeMessages()
// or removeCallbacksAndMessages() for both.
// this ensures that all pending tasks don't execute in vain; for instance, the user has left our activity
// and he doesn't really care if some job is finished or not, so it's our responsibility to cancel it
// pass null to remove ALL callbacks and messages
mainThreadHandler.removeCallbacks(null);
currentThreadHandler.removeCallbacks(null);
if (decorViewHandler != null)
decorViewHandler.removeCallbacks(null);
customHandler.removeCallbacksAndMessages(null);
notLeakyHandler.removeCallbacks(notLeakyRunnable);
}
private void initView() {
tv01 = (TextView) findViewById(R.id.tv_01);
tv02 = (TextView) findViewById(R.id.tv_02);
tv03 = (TextView) findViewById(R.id.tv_03);
tv04 = (TextView) findViewById(R.id.tv_04);
tv05 = (TextView) findViewById(R.id.tv_05);
tv06 = (TextView) findViewById(R.id.tv_06);
tv07 = (TextView) findViewById(R.id.tv_07);
button07 = (Button) findViewById(R.id.button_07);
button07.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
postTaskWithThisWindowAndTextViewHandlers();
}
});
}
private void postTaskWithOrdinaryThread() {
Runnable notForHandlerTask = new Runnable() {
@Override
public void run() {
// as written here - https://developer.android.com/guide/components/processes-and-threads.html#Threads,
// do NOT access the Android UI toolkit from outside the UI thread, as sth unexpected may happen
// for instance, you might get android.view.ViewRootImpl$CalledFromWrongThreadException
tv01.setText("Hi from Thread(runnable)!");
// if you call thread.run(), this would be TRUE, as no new Thread would be created
// read the explanation here - http://stackoverflow.com/a/35264580/655275
Log.d(TAG, "[postTaskWithOrdinaryThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
Thread thread = new Thread(notForHandlerTask);
thread.start();
}
@UiThread
private void postTaskWithHandlerOnMainThread() {
Runnable mainThreadTask = new Runnable() {
@Override
public void run() {
// since we use Looper.getMainLooper(), we can safely update the UI from here
tv02.setText("Hi from Handler(Looper.getMainLooper()) post!");
Log.d(TAG, "[postTaskWithHandlerOnMainThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
mainThreadHandler.post(mainThreadTask);
}
private void postTaskWithHandlerOnCurrentThread() {
currentThreadHandler = new Handler();
Runnable currentThreadTask = new Runnable() {
@Override
public void run() {
// since we use current thread (and from onCreate(), it's the UI thread), we can safely update the UI from here
tv03.setText("Hi from Handler() post!");
Log.d(TAG, "[postTaskWithHandlerOnCurrentThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
currentThreadHandler.post(currentThreadTask);
}
private void postTaskInsideBackgroundTask() {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
// pretend to do something "background-y"
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
tv04.setText("Hi from a Handler inside of a background Thread!");
}
});
}
});
backgroundThread.start();
}
@UiThread
private void postTaskWithThisWindowAndTextViewHandlers() {
// this line will return null from onCreate() (and even if called from onResume()) and cause NPE when trying to post();
// this is because the handler isn't attached to the view if it's not fully visible
decorViewHandler = getWindow().getDecorView().getHandler();
Runnable decorViewTask = new Runnable() {
@Override
public void run() {
// View's post() uses UI handler internally
tv07.post(new Runnable() {
@Override
public void run() {
tv07.setText("Hi from getWindow().getDecorView().getHandler() > TextView.post()!");
Log.d(TAG, "[postTaskWithThisWindowAndTextViewHandlers] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
});
}
};
decorViewHandler.post(decorViewTask);
}
private void postTaskWithHandlerOnBackgroundThread() {
final Runnable pretendsToBeSomeOtherTask = new Runnable() {
@Override
public void run() {
Log.d(TAG, "[postTaskWithHandlerOnBackgroundThread] Is there a looper? " + (Looper.myLooper() != null));
// you'll get java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
// this is because Handlers need to have a Looper associated with them; when the task was run on the main thread,
// main thread looper was used, but if we call this from the background thread, there is NO looper to use
// read more - https://developer.android.com/reference/android/os/Looper.html
postTaskWithHandlerOnCurrentThread();
}
};
final Thread thread = new Thread(pretendsToBeSomeOtherTask);
thread.start();
}
private void postTaskWithNotLeakyHandlerAndRunnable() {
// in order to eliminate leaks, both Handler and Runnable should be static
// static inner classes do not hold an implicit reference to the outer class
// it seems like a lot of useless work, but it's the most accurate and bug-free way
// read more - http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
notLeakyHandler.postDelayed(notLeakyRunnable, 500);
}
private static class ChangeTextRunnable implements Runnable {
private final WeakReference<MainActivity> activity;
private final String greetingMessage;
public ChangeTextRunnable(MainActivity activity, String greetingMessage) {
this.activity = new WeakReference<>(activity);
this.greetingMessage = greetingMessage;
}
public void run() {
if (greetingMessage == null) {
Log.e(TAG, "The message is null ChangeTextRunnable.run()!");
return;
}
MainActivity activity = this.activity.get();
if (activity == null) {
Log.e(TAG, "Activity is null ChangeTextRunnable.run()!");
return;
}
activity.tv05.setText(greetingMessage);
}
}
// === OBTAIN AND HANDLE A MESSAGE ===
private void sendMessageToChangeTextHandler() {
Message messageToSend = customHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString(BUNDLE_KEY, "Hi from custom inner Handler!");
messageToSend.setData(bundle);
messageToSend.what = 6;
customHandler.sendMessage(messageToSend);
}
private static class ChangeTextHandler extends Handler {
private final WeakReference<MainActivity> activity;
public ChangeTextHandler(MainActivity activity) {
this.activity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = this.activity.get();
if (activity == null) {
Log.e(TAG, "Activity is null ChangeTextHandler.handleMessage()!");
return;
}
final String text = (String) msg.getData().get(BUNDLE_KEY);
if (!TextUtils.isEmpty(text)) {
switch (msg.what) {
case 6:
activity.tv06.setText(text);
break;
default:
activity.tv01.setText(text);
break;
}
}
}
}
// === END - OBTAIN AND HANDLE A MESSAGE ===
}
Run
Führen Sie schließlich das Projekt aus.
Referenz
Nachfolgend finden Sie die Links zu den Code-Referenzen:
Nummer | Link |
---|---|
1. | Code herunterladen |
2. | Dem Autor des Codes folgen |
Beispiel 2: Handler
mit ProgressBar Beispiel
In diesem Tutorial wollen wir sehen, wie man Updates von einem thread
im Hintergrund an den thread
der Benutzeroberfläche mit Hilfe eines Handlers
sendet.
Wir klicken auf eine Schaltfläche und simulieren eine schwere Arbeit im Hintergrund. In der Zwischenzeit können wir unseren Fortschrittsbalken aktualisieren, während die Arbeit weitergeht.
(a). MainActivity.java
Dies ist die Hauptaktivität. Wir leiten sie von der AppCompatActivity
ab. Einer der Importe, die wir hinzufügen, ist der Handler
aus dem Paket android.os
.
...
import android.os.Handler;
...
Wir werden drei Instanzfelder pflegen:
Handler
- eine imandroid.os
-Paket definierte Klasse, über die wirMessage
- undRunnable
-Objekte, die mit derMessageQueue
einesThreads
verbunden sind, senden und verarbeiten können.ProgressBar
- ein Widget, das uns erlaubt, den Fortschritt anzuzeigen.- Schaltfläche` - eine Aktionsschaltfläche.
Hier ist der vollständige Code:
package info.camposha.mrhandler;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
public class MainActivity extends AppCompatActivity {
private Handler mHandler;
private ProgressBar mProgressBar;
private Button mStartButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler();
mProgressBar = findViewById(R.id.mProgressBar);
mStartButton = findViewById(R.id.startBtn);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
performStuff();
}
});
}
private void performStuff() {
//Simulate Heavy task in background thread
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 30; i++) {
final int currentProgressCount = i;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Post updates to the User Interface
mHandler.post(new Runnable() {
@Override
public void run() {
mProgressBar.setProgress(currentProgressCount);
}
});
}
}
}).start();
}
}
(b). activity_main.xml
Dies ist das Hauptlayout der Aktivität. An der Wurzel haben wir ein LinearLayout. Dieses Element ermöglicht es uns, seine Kinder entweder horizontal oder vertikal linear anzuordnen. Wir haben auch eine TextView, um den Kopftext unserer Anwendung anzuzeigen. Außerdem haben wir einen Fortschrittsbalken zur Anzeige des Fortschritts. Wir haben auch eine Schaltfläche, die, wenn sie angeklickt wird, den "Thread" startet, um unsere Arbeit im Hintergrund "Thread" auszuführen.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android_layout_width="match_parent"
android_layout_height="match_parent"
android_gravity="center"
android_orientation="vertical"
tools_context=".MainActivity">
<TextView
android_id="@+id/headerLabel"
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_layout_alignParentTop="true"
android_layout_centerHorizontal="true"
android_fontFamily="casual"
android_text="Handler ProgressBar"
android_textAllCaps="true"
android_textSize="24sp"
android_textStyle="bold" />
<ProgressBar
android_id="@+id/mProgressBar"
style="?android:attr/progressBarStyleHorizontal"
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_layout_margin="10dp"
android_indeterminate="false"
android_max="10" />
<Button
android_id="@+id/startBtn"
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="Start" />
</LinearLayout>
Beispiel 3: Einfaches Handler
Beispiel mit Timer
In diesem Beispiel wird gezeigt, wie man Handler
mit Timer
verwendet.
Schritt 1: Abhängigkeiten
Für dieses Beispiel werden keine Abhängigkeiten von Drittanbietern benötigt.
Schritt 2: Layouts
Für dieses Beispiel benötigen wir kein Layout.
Schritt 3: Code schreiben
Hier ist der vollständige Code:
HandlerActivity.java
package com.sdwfqin.sample.handler;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.sdwfqin.sample.R;
import java.lang.ref.WeakReference;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author zhangqin
*/
public class HandlerActivity extends AppCompatActivity {
private static final String TAG = "HandlerActivity";
private MyHandler mMyHandler;
private Timer mTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
mMyHandler = new MyHandler(this);
mMyHandler.sendEmptyMessage(1);
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
mMyHandler.sendEmptyMessage(2);
}
}, 1000, 1000);
mMyHandler.sendEmptyMessage(3);
}
static class MyHandler extends Handler {
private WeakReference<HandlerActivity> mActivity;
public MyHandler(HandlerActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity = mActivity.get();
switch (msg.what) {
case 1:
Log.e(TAG, "handlerA:case:1");
break;
case 2:
Log.e(TAG, "handlerA:case:2");
break;
case 3:
Log.e(TAG, "handlerA:case:3");
break;
default:
break;
}
}
}
@Override
protected void onDestroy() {
mTimer.cancel();
mMyHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
Referenz
Laden Sie den Code unten herunter:
Nummer | Link |
---|---|
1. | Code herunterladen |
2. | Autor des Codes folgen |
Speichersicherer Handler
Die ursprüngliche Implementierung von Handler
behält immer einen harten Verweis auf Handler
in der Warteschlange der Ausführung. Jedes Objekt in Message oder Runnable
, das an android.os.Handler
gesendet wird, wird für einige Zeit hart referenziert. Wenn Sie einen anonymen Runnable
erstellen und postDelayed
mit einem großen Timeout aufrufen, wird dieser Runnable
im Speicher gehalten, bis der Timeout abgelaufen ist. Selbst wenn Ihr Runnable
klein erscheint, verweist es indirekt auf die Besitzerklasse, die normalerweise so groß wie Activity
oder Fragment
ist.
Sie können mehr lesen hier.
Lösung - Weak Handler
verwenden
Was ist "weak `handler"?
Es ist eine speichersichere Implementierung von
android.os.Handler
.
WeakHandler" ist kniffliger als "Android.os.Handler", er behält "WeakReferences" auf "Runnables" und Nachrichten, und GC könnte sie sammeln, sobald die "WeakHandler"-Instanz nicht mehr referenziert wird.
Wie benutzt man es?
Schritt 1: Installiere es
Registrieren Sie Jitpack
als Maven Url in Ihrer Projekt-Level build.gradle
Datei wie folgt:
repositories {
maven { url 'https://jitpack.io' }
}
Fügen Sie dann die Implementierungsanweisung unter der Dependencies-Closure in Ihrer App-Level build.gradle
Datei hinzu:
dependencies {
implementation 'com.github.badoo:android-weak-handler:1.2'
}
Sync, um es zu installieren.
Schritt 2: Code schreiben
Sie können WeakHandler
einfach als Ersatz für den android.os.Handler
verwenden. Verwenden Sie ihn einfach so, wie Sie den Handler
verwenden würden. Unten ist ein Beispiel:
BeispielActivity.java
import com.badoo.mobile.util.WeakHandler;
public class ExampleActivity extends Activity {
private WeakHandler handler; // We still need at least one hard reference to WeakHandler
protected void onCreate(Bundle savedInstanceState) {
handler = new WeakHandler();
...
}
private void onClick(View view) {
handler.postDelayed(new Runnable() {
view.setVisibility(View.INVISIBLE);
}, 5000);
}
}
Referenz
Finden Sie die folgenden Referenzlinks:
Nummer | Link |
---|---|
1. | Mehr lesen |