onCreate和onResume为什么不能直接获取宽高

onCreate 和 onResume(首次) 中能不能获取宽高

答案是不能的

如果是从上个页面回来的 onResume 是可以获取到的

Activity的生命周期都在ActivityThread中, 当我们调用startActivity时,最终会走到ActivityThread中的performLaunchActivity


private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
……
    Activity activity = null;
    try {
      java.lang.ClassLoader cl = appContext.getClassLoader();
     // 【关键点1】通过反射加载一个Activity
      activity = mInstrumentation.newActivity(
          cl, component.getClassName(), r.intent);
      ……
     } catch (Exception e) {
      ……
     }

    try {
      ……

      if (activity != null) {
        ……
        // 【关键点2】调用attach方法,内部会初始化Window相关信息
        activity.attach(appContext, this, getInstrumentation(), r.token,
            r.ident, app, r.intent, r.activityInfo, title, r.parent,
            r.embeddedID, r.lastNonConfigurationInstances, config,
            r.referrer, r.voiceInteractor, window, r.configCallback,
            r.assistToken);

        ……
         
        if (r.isPersistable()) {
         // 【关键点3】调用Activity的onCreate方法
          mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
         } else {
          mInstrumentation.callActivityOnCreate(activity, r.state);
         }
        ……
       }
      ……
    return activity;
   }

performLaunchActivity 中主要是做了3件事情:

  1. 创建了Activity对象
  2. 调用attach, 初始化 WindowWindowManager
  3. 调用了onCreate方法。

onCreate流程中的setContentView只是解析了xml,初始化了DecorView,创建了各个控件的对象;即将xml中的 转化为 View 树。并没有启动View的绘制流程。
上面走完了onCreate,接下来看onResume生命周期,同样是在ActivityThread中的performResumeActivity

  @Override
  public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
      String reason) {
    ……
    // 【关键点1】performResumeActivity 中会调用activity的onResume方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        ……
     
    final Activity a = r.activity;

    ……
     
    if (r.window == null && !a.mFinished && willBeVisible) {
      r.window = r.activity.getWindow();
      View decor = r.window.getDecorView();
      decor.setVisibility(View.INVISIBLE); // 设置不可见
      ViewManager wm = a.getWindowManager();
      WindowManager.LayoutParams l = r.window.getAttributes();
      a.mDecor = decor;
      ……
       
      if (a.mVisibleFromClient) {
        if (!a.mWindowAdded) {
          a.mWindowAdded = true;
         // 【关键点2】在这里,开始做View的add操作
          wm.addView(decor, l); 
         } else {
          ……
          a.onWindowAttributesChanged(l);
         }
       }

      
     } else if (!willBeVisible) {
      ……
     }
    ……
   }

handleResumeActivity 按照顺序主要做了两个事情:

  1. 执行 Activity 的 onResume 方法
  2. 执行 wm.addView(decor, l); , 把之前的 DecorView 添加到视图中,开始执行布局的绘制流程。

由于onResume之后才执行测绘流程,所以是拿不到宽高的

为什么 View.post 可以获取View的宽高

    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        // 判断是否已attach,如果已经attach就添加到AttachInfo的Handler消息队列中
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // 添加到队列中,等待执行
        getRunQueue().post(action);
        return true;
    }
    
    // 当绘制完成之后会执行这个方法,判断执行队列里面的任务
    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        ...
        // Transfer all pending runnables.
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
        ...
    }

在布局管理器中ViewRootImpl中直接定位到performTraversals方法中

  private void performTraversals() {
     ……
     // 【关键点1】分发mAttachInfo
     host.dispatchAttachedToWindow(mAttachInfo, 0);
     ……
    
   //【关键点2】开始测量
   performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
   ……
   //【关键点3】开始布局
   performLayout(lp, mWidth, mHeight);
   ……
   // 【关键点4】开始绘制
   performDraw();
   ……
   }


总结:post 会把任务添加到队列中,在当绘制完成之后会执行 dispatchAttachedToWindow,执行队列里面的任务, 就可以获取view的宽高了。

这里要结合Handler的消息机制,我们post到Handler中的消息,并不是立刻执行,不要认为我们是先dispatchAttachedToWindow的,后执行的测量和绘制,就没办法拿到宽高。实则不是,我们只是将Runnable放到了handler的消息队列,然后继续执行后面的内容,也就是绘制流程,结束后,下一个主线程任务才会去取Handler中的消息,并执行。

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值