Handler+ Message 源码分析及手写实现 一

本文深入探讨了Android中Handler的工作原理,解析了其内存泄漏的原因及解决方案,解释了为何不能在子线程直接创建Handler,以及两种创建Handler方式的区别。

什么是Handler?

在这里插入图片描述

抛出三个问题

1 handler内存泄漏?
2 handler为什么不能在子线程中 new 出来?
3 new  Handler() 两种写法的区别

1 handler内存泄漏?通过代码分析

在 MainActivity 中创建 Handler对象,当收到消息后 就跳转到另外一个 PersonActivity

public Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            super.handleMessage(msg);
            startActivity(new Intent(MainActivity.this, PersonActivity.class));
        }
    };

触发事件

  button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                new Thread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        SystemClock.sleep(5000);
                        if (handler != null)
                        {
                            Message message = handler.obtainMessage();
                            message.what = 1;
                            handler.sendMessage(message);
                        }
                    }
                }).start();
            }
        });

执行逻辑
当我们点击Button按钮后,在子线程中执行上述代码,休眠5秒后,发送一条消息执行跳转逻辑,我们点击按钮后立即back健返回桌面,此时是否会跳转到PersonActivity这个界面了?

重写onDestroy方法,查看当点击back按钮后,MainActivity 是否销毁,销毁就会执行onDestroy()方法

  @Override
    protected void onDestroy()
    {
        super.onDestroy();
        Log.e(TAG, "=======>>onDestroy");
    }

最后结果发现onDestroy()执行了,也跳转到了PersonActivity这个界面,按道理说MainActivity 销毁后,就不应该再去执行跳转逻辑啊,事实上 这就是Handler的内存泄漏点,MainActivity 其实并没有被真正销毁,因为
handler 对象在子线程中被引用导致handler对象不能被回收,MainActivity也不能被真正的销毁,我们只需要在onDestroy()方法里加上 handler = null;

 @Override
    protected void onDestroy()
    {
        super.onDestroy();
        handler = null;
    }

这样,当MainActivity执行onDestroy()方法时,我们手动把handler 设置为空,这样handler 就能被gc,这样就能解决 由handler 造成的内存泄漏问题

2 handler为什么不能在子线程中 new 出来?

 button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                new Thread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        new Handler();
                    }
                }).start();
            }
        });

这样一段代码,当在部分手机,例如华为手机上,它是不会报错的,猜想华为应该把android原生的改了吧,但是在谷歌的模拟器上,它是会抛出异常的

11-25 13:48:07.967 1367-1428/? E/AndroidRuntime: FATAL EXCEPTION: Thread-86
    Process: com.rx.hander, PID: 1367
    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:200)
        at android.os.Handler.<init>(Handler.java:114)
        at com.rx.hander.MainActivity$4$1.run(MainActivity.java:72)
        at java.lang.Thread.run(Thread.java:818)

Can’t create handler inside thread that has not called Looper.prepare()

我们来看看  new Handler()  的源码

在这里插入图片描述
再往下找

在这里插入图片描述
关键代码

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

这就是抛出上面异常的代码,它的执行条件是 mLooper == null,就是说 Looper.myLooper()取出的对象是一个空,为什么会这样了?我们先来了解一个ActivityThread这个类

ActivityThread可以说是我们App真正的入口, 当应用程序启动时,会执行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()));
                }

重点关注 Looper.prepareMainLooper(); 创建一个Looper消息循环对象, 顺着源码追踪

 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
   private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
sThreadLocal.set(new Looper(quitAllowed));
 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

ActivityThread 它所处的是一个主线程,当app启动时,会执行里面的main()方法,执行 main()方法中的Looper.prepareMainLooper(),创建一个消息循环对象,并且这个 Looper对象是全局唯一的

//在Looper类里面 它是 static 修饰的
 private static Looper sMainLooper;  // guarded by Looper.class

sThreadLocal.set(new Looper(quitAllowed));,重点关注里面的 new Looper(quitAllowed)

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

创建一个Looper对象,对象里面包含一个 MessageQueue队列,包含一个当前线程 mThread ,

再回到 这段代码

  sThreadLocal.set(new Looper(quitAllowed));

把创建这个Looper对象set进 sThreadLocal 里面来

  public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

最终 执行 createMap(t, value); 当前线程作为 key值 , looper对象作为值被放在 map对象中去,那么既然向map中添加对象,那肯定有从map中取出对象才有意义, 那在那里取出的对象了?
答案是 在new Hander()对象时,就有取的操作了

第一步

 new Thread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        new Handler();
                    }
                }).start();

第二步

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

第三步

  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;
    }

第四步

 public static @Nullable Looper myLooper() {
        return 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();
    }

Thread t = Thread.currentThread(); 重点来了 t 当前线程作为key值 去map里面取值,我们是在子线程里面new 的 handler 对象,那么当前线程就是 子线程,我们APP启动时是在主线程里面创建的Looper对象,并且 map的key值是主线程,现在我们现在用子线程key值去取 当然取不到了?
所以

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

子线程当中取到的 mLooper 为空,所以抛出异常,到此我们解决了第二个问题,handler为什么不能在子线程中 new 出来。

3 new Handler() 两种写法的区别

第一种写法

 public Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            super.handleMessage(msg);
         
        }
    };

第二种写法

 public Handler handler1=new Handler(new Handler.Callback()
    {
        @Override
        public boolean handleMessage(Message msg)
        {
            return false;
        }
    });

找到 Handler源码中 dispatchMessage 这个方法

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

当 mCallback 不为空 就调用

mCallback.handleMessage(msg)

否则就执行

 handleMessage(msg);

这个handleMessage(msg) 就是 Handler中的一个空实现方法

  /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

上面的第一种方法就是重写的这个方法,所以说了,推荐使用第二种写法,第一种写法更像备胎写法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值