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

  1. Einplanung von Nachrichten und runnables, die in der Zukunft ausgeführt werden müssen.
  2. 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:

  1. Erstens wird er eine Schnittstelle bereitstellen, um Nachrichten an seine Looper
    Warteschlange.

    1. zweitens wird er den Callback für die Verarbeitung dieser Nachrichten implementieren, wenn sie vom Looper versandt werden.

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:

  1. Handler - eine im android.os-Paket definierte Klasse, über die wir Message- und Runnable-Objekte, die mit der MessageQueue eines Threads verbunden sind, senden und verarbeiten können.
  2. ProgressBar - ein Widget, das uns erlaubt, den Fortschritt anzuzeigen.
  3. 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.

WeakHandler

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

Categorized in:

Tagged in: