View怎么显示在屏幕上的

本文深入剖析了Android中Activity的启动流程,从context.startActivity出发,详细介绍了Activity如何与ActivityThread和ActivityManagerService建立连接,以及Activity从创建到窗口显示的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#本文基于Android 8.0源码,流程梳理,权当总结和笔记

结论先行:

1、context.startActivity
2、ActivityThread#main()---attach()和AMS建立连接
3、Activity收到onCreate回调,setContentView(layoutId),new DecorView
4、ActivityThread收到handleLaunchActivity回调---addView,add的就是上面的new DecorView
5、ViewRootImpl开启了measure、layout、draw等操作

附上流程图:

#1.Android也是Java程序,那么必然有个main方法作为入口,即ActivityThread.java#main

  public static void main(String[] args) {


       Looper.prepareMainLooper();

       ActivityThread thread = new ActivityThread();
       //attach
       thread.attach(false);

       Looper.loop();

       //主线程的loop是一直在跑着的,一旦停止那么会抛出异常,同时也解释了我们创建的Hanlder,如果没有指定loop的线程的话,就是默认在主线程
       throw new RuntimeException("Main thread loop unexpectedly exited");
  }

attach方法的主要作用是为了和AMS建立连接,以便于获取Activity生命周期回掉的消息。

 final IActivityManager mgr = ActivityManager.getService();
 try {
      mgr.attachApplication(mAppThread);
 } catch (RemoteException ex) {
      throw ex.rethrowFromSystemServer();
 }

 #context.startActivity,这句是启动Activity的方法。

关于Activity的启动,这里不详细说,只需要知道最后经过各种调用来到了ActivityThread.java#scheduleLaunchActivity(),然后调用了handleLaunchActivity()方法,接着又调用到了scheduleResumeActivity(),同样真正做事的是handleResumeActivity()

#下面看下handleResumeActivity这个方法具体做了哪些事情?

思考:其实在这里就可以很清楚的解释为什么在Actviity的onCreate方法里获取不到view的宽高或者获取到为0了,因为这个时候view都还没有被add到PhoneWindow中,就更别提measure了。

 ActivityClientRecord r = mActivities.get(token);

 #1、r = performResumeActivity(token, clearHide, reason);

 
 r.window = r.activity.getWindow();
 #2、View decor = r.window.getDecorView();
 decor.setVisibility(View.INVISIBLE);
 ViewManager wm = a.getWindowManager();
 WindowManager.LayoutParams l = r.window.getAttributes();
 a.mDecor = decor;

ViewRootImpl impl = decor.getViewRootImpl();

if (!a.mWindowAdded) {
    a.mWindowAdded = true;
    #3、wm.addView(decor, l);
} else {
    a.onWindowAttributesChanged(l);
}

这里很重要,一步步分析:

1、通过token从ActivityClientRecord中获取对应的Activity,token可以理解为一个身份的标志。

2、View decor = r.window.getDecorView();

这里getDecorView是Window.java的一个抽象方法

/*
 *
 * @return Returns the top-level window decor view.
 */
public abstract View getDecorView();

 它的实现在PhoneWindow.java

    @Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

那么这里的mDecor是什么?它是DecorView,什么是DecorView,一张图说明:

那么它是在哪里赋值的呢?

回到AMS,在告诉ActivityThread.java#handleResumeActivity消息之前调用了Activity生命周期的onCreate方法,开发者可以在这个方法里设置自己定义的布局文件:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //通过setContentView方法设置自己定义的布局文件
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate");
    }

看这个方法setContentView,在这个方法里初始化mDecor,并把我们自己定义的布局文件activity_main.xml添加到mDecor中。

详细可参考:Android FrameWork之旅 --- setContentView

所以decor就是一个add了我们自定义的布局文件的ViewGroup。

Ok,到这里我们定义的布局文件终于被add到DecorView中了,那么什么时候真正的显示在屏幕之上呢?

继续ActivityThread.java#handleResumeActivity的第三个步骤:

final Activity a = r.activity;
ViewManager wm = a.getWindowManager();
if (!a.mWindowAdded) {
    a.mWindowAdded = true;
    wm.addView(decor, l);
}

wm是什么?是一个ViewManager对象,所以wm.addView又是把decor怎么了?

那就要看看addView方法做了什么了?

该方法是在ViewManager里声明的,ViewManager是一个interface:

public void addView(View view, ViewGroup.LayoutParams params);

这里最终调用到了WindowManagerImpl.java#addView方法,该方法又调用了

mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);

所以,最后的实现是在WindowManagerGlobal.java中:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {

      ViewRootImpl root;
      root = new ViewRootImpl(view.getContext(), display);

      view.setLayoutParams(wparams);
      
      //mViews是一个ArrayList对象,具体做什么的还没研究过
      mViews.add(view);
      mRoots.add(root);
      mParams.add(wparams);

      // do this last because it fires off messages to start doing things
      try {
             //重点看这里
             root.setView(view, wparams, panelParentView);
      } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
             }
      throw e

走到了ViewRootImpl.java#setView方法,到这里开始对这个view做各种操作:

// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
   requestLayout();

GoOn:

   @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            //检查当前操作是否在UI线程,也就是Android的主线程
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

scheduleTraversals()方法中最主要是调用到了doTraversal:

void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            //看我看我看我
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

后续依次通过performMeasure()、performLayout()及performDraw()来调用View的三个非常重要的方法:

measure()、layout()及draw()。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值