研究Handler源码心得

1:初始化,子线程和主线程通讯流程分析

1:Looper的初始化有两种,一种是prepareMainLooper()和prepare(),prePareMainLooper是在主线程中创建的Looper,是在ActivityThread的main方法中初始化的

 public static void main(String[] args) {
      ...省略一些代码
        Looper.prepareMainLooper();
         ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
		...省略一些代码
        Looper.loop();

下面是是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();
        }
    }

下面是prepare()方法源码

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

prepareMainLooper()中其实也是调用的prepare(boolean isquit),当时默认是是不能quit(quit就是退出looper的循环),prepare方法的实现就是把当前的线程和Looper绑定起来;
2:下面看下Looper的构造方法,和MessageQueue的构造方法

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

基本上没有什么参数,就是把前面设置进来的quitAllowed分别传递到Looper和MessageQueue
3:启动Looper.loop()方法(ActivityThrea的main方法中系统给我调用了),looper找到messagequeue,链表循环next()去处message,调用message.target.dispatchMessage(msg),调用完之后message销毁放回到message池中.

 /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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;//注释1
       		 ...省略
          for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
		       		 ...省略
 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);
                }
            }
                  		 ...省略
             msg.recycleUnchecked();

target就是message发送出去的handler.源码如下
跟踪handler的sendmessage(msg),后来会走到sendMessageAtTime(Message msg, long uptimeMillis)

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

看下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);
    }

this就是当前的handler的,赋值给了message的target
4:下面看下handler的dispatchMessage(Message msg);

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {  // 注释1 :这里是Handler.post(runnable)
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

在这里可以看出如果优先设置callback就走callback实在没有设置就走handlemessage(msg)(这个是新版本google放弃的一个Handler的创建方式);注释一表明,runnable的处理优先级是高于message的.到这里子线程和主线程通讯过场算是完成了.

2:handler内存泄漏导致的原因和怎么避免内存泄漏;

handler内存泄漏主要是因为handler发送出去的Message或者是runnable都MessageQueue中(按照发送出来的时间先后顺序,执行也是通过loop死循环按照时间的先后(存放)取出来交给handler执行,如果当前界面销毁了但是messagequeue中的这个message或者是runnable没有执行,当执行到这个message的时候引用到之前类的资源就会出错.)

 Handler handler1 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == 0) {
                startActivity(new Intent(MainActivity.this,SecondActivity.class));
            }
            return false;
        }
    });
   private void test() {
        new Thread(new Runnable() {
            @Override
            public void run() {
//                SystemClock.sleep(3000);
                Message message = handler1.obtainMessage();
                message.what = 0;
                handler1.sendMessageDelayed(message,3000);
            }
        }).start();
    }
     @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG,">>>>销毁了");
    }

比如上面的场景,延时3秒钟发送跳转到secondActivity的message,在3秒钟的时间没销毁了当前的Activity推到了桌面,但是执行这个message会跳转到secondAcivity.解决方案.在ondestroy方法中handler1= null ;在handler1.sendMessageDelayed(message,3000)之前给给!=null的判断就可以避免;

子线程中可以new Handler()吗?

我的理解是可以的,不过在创建Handler之前要创建一个子线程的Looper对象,然后通过这个looper对象去创建Handler,但是自己创建的Looper对象是个异步操作可以没有没有创建完成就去创建Handler也会报错,当然系统是给我创建了一个handlerThread(其实是一个子线程,在run方法中创建一个looper通过唤醒机制)

  @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

因此在子线程创建一个Handler方法如下:

HandlerThread thread = new HandlerThread("thread-1");
        thread.start();
        Looper looper = thread.getLooper();
        Handler handler = new Handler(looper);

注意:一定要在run之后再获取looper对象,因为run方法就是创建looper对象;

更新ui的操作一定是在主线程吗?

对于这个问题肯定很多人想到的是肯定的,其实这句话也不完全对.比如
textView.setText();有时候在子线程运行是不会报错的;(有时候是viewrootimpl对象还没有创建的时候)
如果我们在更新ui之前做延时就会报错"Only the original thread that created a view hierarchy can touch its views." 我们知道android更新界面会从新requestLayout.(比如textview.settext()中会有个checkForRelayout()里面会掉view.requestlayout)我们看下view的reqeustLayout()

   public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();

        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }

        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;

        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();  //注释1
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }

viewrootImpl是viewparent的实现类,注释1处调了之后代码会走到viewrootimpl来的requstLayout()


    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

看下checkThread()

  void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

思路清晰了android更新界面 view.requestLayout()-viewparent.requestlayout()-viewrootimpl
.requestLayout-checkThread().如果当前线程不是主线程想去更新ui就会抛出上面的异常;
那么程序刚开始的时候为什么在子线程中更新ui没有报错了,原因只有一个那个时候viewrootimpl对象还没有创建成功;我们在Activity的生命周期创建过程中防线viewrootimpl是在onresume之后才创建成功的.(下次分析下acitivity的启动原理)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值