Android之Handler简单介绍

今天我们的主角:Android消息机制——Handler

Handler是什么:

Android系统中线程间传递消息的一种机制

Handler作为这么重要的一种机制,虽然我们在使用的时候基本上只用到这一个类,但是还是有必要了解一下与之相关的类。

Message:消息的载体

MessageQueue:消息队列,负责存储消息

Handler:负责发送和处理消息(发送和处理不在同一个线程)

Looper:通过死循环来取出消息并且通知Handler处理

我们先通过代码来看看基本用法

class AActivity : AppCompatActivity() {

    private lateinit var handler: Handler

    @SuppressLint("HandlerLeak")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        handler = object : Handler() {
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                Log.d("ZLog AActivity", "接收到消息: $msg")
            }
        }

        Thread {
            kotlin.run {
                val msg = Message()
                msg.what = 110
                msg.arg1 = 1
                msg.arg2 = 2
                msg.obj = "obj"
                handler.sendMessage(msg)
            }
        }.start()
    }
} 

AActivity: 接收到消息: { when=-57ms what=110 arg1=1 arg2=2 obj=obj target=com.example.androidteach.AActivity$onCreate$1 }

我们这里创建了一个handler并且重写了handleMessage方法,然后新建了一个线程,在线程中构建了一个消息并且发送。

Message有几个参数是我们可以直接使用的:

what:一般用来区分消息类型

arg1、arg2:这2个是int型,可以携带简单的参数

obj、setData:可以设置复杂的数据。

Handler的发送方法有几个:

sendEmptyMessage(int what):系统帮我们构建一个message并且设置what参数然后发送

sendEmptyMessageAtTime(int what, long uptimeMillis) 在指定的时间发送消息

sendEmptyMessageDelayed(int what, long delayMillis):延时发送消息

sendMessage(@NonNull Message msg):发送普通消息

sendMessageAtTime(@NonNull Message msg, long uptimeMillis):指定时间发送消息

sendMessageDelayed(@NonNull Message msg, long delayMillis):延时发送消息

obtainMessage():返回一个Message

基本的使用就这样就可以了,下面我们就来看看它的运行机制:

先来看看Handler:

查看handler的源码我们可以看到它还有几个带参数的构造方法:

    public Handler() {
        this(null, false);
    }

    public Handler(@Nullable Callback callback) {
        this(callback, false);
    }

    public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }

    public Handler(@NonNull Looper looper, @Nullable Callback callback) {
        this(looper, callback, false);
    }

    @UnsupportedAppUsage
    public Handler(boolean async) {
        this(null, async);
    }

    public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

   
    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

其中主要的就2个,一个传了Callback,一个传了Looper。Callback为消息的回调接口,只有一个方法:

public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
}

我们刚刚创建的handler并没有给任何参数,所以最后会调用传递callbac的构造方法,并且默认为空。

可以看到其中的mLooper默认取Looper.myLooper()

这里我们的Handler已经拿到了looper和messageQueue了。再来看看发送消息,通过刚刚的几个sendMessage方法最后调用的地方:

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

到这里我们可以看到,先给msg设置了一个target,就是一个handler对象,设置为了本身,然后就是将消息添加到队列中。

下面我们来看一下Looper:

通过刚刚的Handler构造方法中可以看到Looper有一个静态方法myLooper,我们先进入这个方法看看:

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

可以看到它用sThreadLocal.get返回的,sThreadLocal是一个ThreadLocal<Looper>的静态成员,ThreadLocal类简单说就是以线程为key来存储数据的结构,每个线程保存的数据互不影响,调用的get和set方法只对当前线程有效。

所以如果Looper在调用myLooper()之前没有设置过的话获取的会是空值。那为什么我们直接创建的Handler里面Looper并不是空?我们来找一下相关的设置方法:

我们看到一个prepare方法,调用了一个私有的prepare方法,在这里面设置Looper,并且在设置之前判断了当前线程是不是已经设置过了,如果设置过了就会抛出异常,提示一个线程只能创建一个Looper。

我们刚刚创建handler的时候并没有调用prepare方法,那我们就再调用一下看看,是不是当前的线程已经设置过了。

我们可以看到这里就报了刚刚看到的异常,提示第20行。所以在这之前这个方法已经被调用过了。那么是谁调用的呢?肯定是系统了,因为我们是直接在activity的onCreate方法中调用的,所以这里 是main线程,系统在启动apk的时候已经帮我们调用过了,这样我们就可以在这里直接创建Handler而不用再去单独调用一次Looper.prepare方法。

其实系统调用的应该不是Looper.prepare方法,应该是prepareMainLooper方法,大家看看下面的截图:

系统启动的时候就已经帮我们初始化好了主线程的Looper。如果是在子线程呢?就需要我们自己调用Looper.prepare方法了。

那么消息是在那里被取出来的呢?还是在Looper中,我们来看一个loop方法:

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }
            msg.recycleUnchecked();
        }
    }

代码比较长,其实我们只需要关注其中的for循环,这里的for循环没有设置任何条件,那么就是一个死循环,它就是在这里面通过消息队列取出的消息,还记得handler的发送方法吗?在发送的时候,给每个message都设置了一个target,就是handler本身。所以这里取出消息后调用了target的dispatchMessage来分发消息,最终会调用我们重写的handleMessage将消息给到我们自己处理。

到这里整个消息的传递流程就讲完了,说的比较乱,下面大致整理一下:

首先要了解的就是几个类的作用:

Handler:复制消息的发送和最终的处理;

Message:消息的实体,用来保存我们要传递的数据;

MessageQueue:消息队列,存储消息;

Looper:复制循环遍历取出消息并且分发给各自的Handler处理。相当于邮局分派信件,所以每个线程只能有一个Looper。

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
AndroidHandler是用于在主线程(UI线程)与其他线程之间进行通信的机制。它允许你在后台线程执行耗时的操作,并将结果传递回主线程更新UI。 下面是一个最简单的使用Handler的示例: ```java public class MainActivity extends AppCompatActivity { private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化Handler,关联到主线程的Looper mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { // 在主线程处理消息 updateUI((String) msg.obj); } }; // 创建并启动后台线程 Thread backgroundThread = new Thread(new Runnable() { @Override public void run() { // 模拟耗时操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // 发送消息给主线程 Message message = mHandler.obtainMessage(); message.obj = "耗时操作完成"; mHandler.sendMessage(message); } }); backgroundThread.start(); } private void updateUI(String message) { // 更新UI TextView textView = findViewById(R.id.textView); textView.setText(message); } } ``` 这个例子,我们在后台线程模拟了一个耗时操作(这里用Thread.sleep模拟),然后通过Handler将结果传递给主线程更新UI。 在MainActivity的onCreate方法,我们初始化了一个Handler,并重写了handleMessage方法,在这个方法我们可以处理从后台线程发送过来的消息。在后台线程,我们创建了一个Message对象,将结果存储在obj字段,并通过Handler的sendMessage方法发送消息给主线程。 在主线程,我们通过updateUI方法来更新UI,这里我们简单地将结果显示在一个TextView上。 需要注意的是,Handler的构造函数传入了主线程的Looper对象,这样它就能够与主线程进行通信。另外,如果你在后台线程创建Handler而没有传入Looper,那么Handler将与当前线程的Looper绑定,这样它就只能在当前线程使用。 这只是一个简单的例子,实际使用你可能会遇到更复杂的场景,但是基本的原理和用法都是类似的。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值