Android Handler学习及复习总结(一篇全)


1.Handler是什么

  • Handler在Android中是用来结合线程的消息队列来发送、处理Message对象和Runnable的工具
  • Android只允许在主线程更新UI,因此Handler作为异步消息处理机制,可以使我们在不阻塞UI线程的情况下完成UI的更新及相关消息处理

2.Handler的基本用法

方式1:使用handler.sendMessage()
 	//使用匿名内部类创建Handler对象
    Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                //执行UI操作
            }
        }
    };
    //在工作线程中发送消息至消息队列
	 new Thread(new Runnable() {
            @Override
            public void run() {
                //创建Message对象
                Message msg=handler.obtainMessage();
                msg.what=1001;
                //发送消息
                handler.sendMessage(msg);
            }
        }).start();
        
   /**
     * 自定义Handler子类,继承Handler,重写handleMessage()方法
     */
    class MyHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                //执行UI操作
            }
        }
    }
	//在主线程中创建MyHandler实例
	private Handler mHandler=new MyHandler();
	 //在工作线程中发送消息至消息队列
	 new Thread(new Runnable() {
            @Override
            public void run() {
                //创建Message对象
                Message msg=handler.obtainMessage();
                msg.what=1001;
                //发送消息
                mHdler.sendMessage(msg);
            }
        }).start();
方式2 :使用handler.post()
 new Thread(new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        //UI操作
                    }
                });
            }
        }).start();

本质上post方式方式和sendMessage方式没有什么区别,查看源码如下:

  public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    //继续往下看
   public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
 	//最终插入消息队列
 	public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    //回调方法有所不同
     public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    //直接调用run方法处理UI
    private static void handleCallback(Message message) {
        message.callback.run();
    }

3.Handler工作原理

我们先来看一下Handler的构造方法

	public Handler() {
        this(null, false);
    }
	public Handler(Callback callback) {
        this(callback, false);
    }
	public Handler(Looper looper) {
        this(looper, null, false);
    }
	public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
   /**
     * @hide
     */
    public Handler(boolean async) {
        this(null, async);
    }
   /**
     * @hide
     */
    public Handler(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;
    }

我们分析一下上面的代码,最终都是调用最后一条构造方法`mAsynchronous``表示是否是异步消息,一般是false,首先获取Looper对象,myLooper中的代码

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

sThreadLocal又是什么呢?我们先看一下get方法

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

这样我们就清楚了,其实就是一个ThreadLocal,ThreadLocal内部维护了一个map,以当前线程为key,以你想存的值为value,这里我们的get方法返回了一个Looper对象,当我们在主线程中初始化Handler,这里的当前线程就是主线程了,我们可以认为这里存的值就是Looper对象,那我们是什么时候将该Looper对象存入map的呢?我们继续往后看
接着获得looper对应的MessageQueue,至此handler的初始化工作就结束了

我们再看一下sendMessage方法

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

接着看sendMessageDelayed

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

继续sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

到这可以看到每个messgae设置了一个target属性指示当前的handler,将消息入队列,发送消息结束,我们再看一下如何处理消息的

我们先看一下前面的Looper到底是何时被初始化并且加入到map中去的,我们猜想主线程在创建的时候就初始化了Looer,那我们看一下ActivityThread中的main方法

   public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

看到有一个prepareMainLooper方法,点进去看一下

 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

执行了Looper的prepare方法,传入一个false,这里的false表示不允许messageQueue退出,接着在main的最后调用了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);
            }

            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;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                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();
        }
    }

在loop方法中不停地从消息队列中取出消息,我们挑重点看,有用到了msg.target然后分发消息进行处理,这里就将消息交给发送它的hadler进行处理
msg.target.dispatchMessage(msg)
到这工作原理我们基本就清楚了,看到一张逻辑图,觉得还不错

4.Handle核心类

经过上面的分析,相信大家都清楚其工作原理,其用到的核心类也就是我们分析中见到的,下面上个类图,大家看的更清楚

5. Handler使用中存在的问题分析及解决方法

看一下我们前面使用的handler

编辑器为我们标注了黄色,看一下提示

This Handler class should be static or leaks might occur (anonymous android.os.Handler) less… (Ctrl+F1)
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

大致意思:由于handler定义为内部类,可能会阻止GC。如果handler的Looper或MessageQueue 非主线程,那么没有问题。如果handler的Looper或MessageQueue 在主线程,那么需要按如下定义:定义handler为静态内部类,当你实例化handler的时候,传入一个外部类的弱引用,以便通过弱引用使用外部类的所有成员。

为何会导致内存泄漏?我的理解是这样的:
首先在主线程初始化的时候初始化了looper而我们在这样使用handler时,非静态内部类对象或匿名内部类对象会持有Activity的引用,当我们销毁Activity时,如果有消息未处理,会导致Activity无法回收,按照提示,我们可以这样做

  1. 使用静态内部类
 static class  MyHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                //执行UI操作
            }
        }
    }

将MyHandler定义为静态内部类,这样,当Activity销毁时,MyHandler也将被处理

这样做又有一个问题,如果我们要操作MainActivity中的UI控件时,我们就再次持有了Activity的强引用,从而再次导致上述情况的发生,那要怎么解决呢?这里我们可以采用弱引用的方式持有Activity,从而操作其中的控件,这样当GC时也会被直接处理掉

 static class  MyHandler extends Handler{
        private WeakReference weakReference;
        private MyHandler(HandlerActivity handlerActivity){
            weakReference = new WeakReference<>(handlerActivity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    HandlerActivity activity= (HandlerActivity) weakReference.get();
                    if(activity!=null){
                        activity.textView.setText("111");
                    }
                    break;
            }
        }
    }

当然,既然问题出在handler还有消息未被处理导致持有Activity强引用,那我们可以在销毁Activity时将消息全部移除来使得Activity可以被回收掉,这样可以吗?答案是yes

 @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }

这样就可以解决问题啦

最后再感慨一句,真的真的真的要去看源码,才能知其所以然,文中如果有不正确的地方,望大家指正!
本文参考
Handler消息源码流程分析
Android中的handler源码解析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值