Activity、Window和View之间的关系

1,410 阅读4分钟

一 概述

我们都知道 Activity 是用来显示界面的,其实都是把View设置到 Window 上,最终通过 WindowManagerService 调用底层的 skia 绘制引擎 或者 OpenGL|ES 来真正绘制的。先总结一下步骤吧

  1. Activity 是不能存放任何view的,都是交给了Window,android中PhoneWindow是Window的唯一实现类

  2. 当打开Activity的时候会走到 ActivityThread的performLaunchActivity方法,此时会执行 attach 方法,在 中 ,创建了 PhoneWindow,并且得到了 mWindowManager,也就是 WindowManagerImpl

  3. 之后会走到 Activity的onCreate方法,此时我们setContentView(),其实调用的是 PhoneWindow的setContentView(),此时创建了 DecorView,这个时候,他们三者还没有任何关系,只是创建了View,Window,Activity而已

  4. 之后 执行 ActivityThread 的 handleResumeActivity 方法,里面执行了 Activity的onResume方法之后,会调用 WindowManger的 addView(DecorView view),实际上WindowManger 走到 WindowManagerImpl,在走到 WindowManagerGlobal的addView,之后走到 ViewRootImpl 的 root.setView

  5. 之后就走到了SystemServer进程Session.addToDisplay,然后走到 WindowMangerService 的 addWindow()

  6. 顺便说一下 ViewRootImpl 并不是一个View,只不过实现了 ViewParent,里面有requestLayout,invalidate 等方法,以后view或者屏幕的刷新 都会给ViewRootImpl,

  7. 其实 setContentView里面的View都是交给了 DecorView 去添加的,DecorView交给Window

二 Window大致分三种类型

  • 系统Window:比如状态栏(Status Bar)、导航栏(Navigation Bar)、来电显示窗口、锁屏窗口、音量调整窗口。
  • 应用Window:包括所有应用程序自己创建的窗口(比如咱们的Acitivty上的window),以及在应用起来之前系统负责显示的窗口
  • 所谓的子Window,是说这个Window必须要有一个父窗体,应用自定义的对话框,比如PopWindow,Dialog

三 从setContentView说起,下面分析的都是android10的代码

当Activity被打开的时候,会走到 ActivityThread 的 performLaunchActivity 方法,,请看我以前的文章打开Activity流程

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
   ContextImpl appContext = createBaseContextForActivity(r);
   Activity activity = null;
      try {
          java.lang.ClassLoader cl = appContext.getClassLoader();
          //通过 Instrumentation 创建了一个新的Activity对象
          activity = mInstrumentation.newActivity(
                  cl, component.getClassName(), r.intent);
          StrictMode.incrementExpectedActivityCount(activity.getClass());
          r.intent.setExtrasClassLoader(cl);
          r.intent.prepareToEnterProcess();
          if (r.state != null) {
              r.state.setClassLoader(cl);
          }
      } catch (Exception e) {

      }
   try {
   // 这里不是创建 咱们的Application,而是从这里拿到咱们的 Application,
   //真正创建的地方是在 handleBindApplication 里面的 mInitialApplication
       Application app = r.packageInfo.makeApplication(false, mInstrumentation);
   	   if (activity != null) {
                CharSequence title = 								       r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
             // 走到Activity的attach方法
                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);
            }
}

我们在看一下 Activity 的attach 方法 和 setContentView

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        attachBaseContext(context);
        // 初始化 Window,PhoneWindow 是Window的唯一实现类
         mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        //....
        // 从PhoneWindow中得到WindowManager,而mWindowManager其实就是 WindowManagerImpl
        // WindowMangerImpl
        mWindowManager = mWindow.getWindowManager();
        
public void setContentView(@LayoutRes int layoutResID) {
		// 调用的是 PhoneWindow的setContentView
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
}

我们看一下PhoneWindow的setContentView

@Override
public void setContentView(int layoutResID) {
        // before this happens.
        if (mContentParent == null) {
        // 其实这里初始化了一个DecorView,DecorView是继承FrameLayout的一个View
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

然后我们在看一下 ActivityThread 的 handleResumeActivity

@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
        // 执行 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;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            // mVisibleFromClient 默认是true
            if (a.mVisibleFromClient) {
            // 此时 mWindowAdded 为false ,还没有添加,所以会走到这里
                if (!a.mWindowAdded) {
                	// 把mWindowAdded 设置为true ,已添加
                    a.mWindowAdded = true;
                    //调用WindowManager的 addView()方法,刚咱们也分析了,实际上调用的是
                    //WindowManagerGlobal的 addView()
              
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }

        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
        
         if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
           // 省略代码
            r.activity.mVisibleFromServer = true;
            mNumVisibleActivities++;
            if (r.activity.mVisibleFromClient) {
               // activity的makeVisible 此时只是让 mDecor.setVisibility(View.VISIBLE);
               // 因为此时 window 已经添加了。
                r.activity.makeVisible();
            }
        }

由此可看,在Activity的onResume之后,会走到 WindowManagerGlobal ,并且把 DecorView add到window上。 然后我们看一下 WindowManagerGlobal 的addView 方法。

  public void addView(View view, ViewGroup.LayoutParams params,
          Display display, Window parentWindow) {
     // 省略代码
		
      ViewRootImpl root;
      View panelParentView = null;

        // 省略代码
		// 初始化  ViewRootImpl
        //  ViewRootImpl虽然不是一个View,但是实现了 ViewParent 接口,以后关于View的刷新,都是通过
        // 调用ViewRootImpl的方法刷新的
          root = new ViewRootImpl(view.getContext(), display);

          view.setLayoutParams(wparams);

          mViews.add(view);
          mRoots.add(root);
          mParams.add(wparams);

          // do this last because it fires off messages to start doing things
          try {
          // 把 DecorView 放到 ViewRootImpl里面
              root.setView(view, wparams, panelParentView);
          } catch (RuntimeException e) {
              throw e;
          }
      }
  }

然后我们看一下ViewRootImpl的setView

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  synchronized (this) {
    //省略
    //这就是binder机制,夸进程了 下面正式进程 WindowMangerService
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
          getHostVisibility(), mDisplay.getDisplayId(),
          mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
          mAttachInfo.mOutsets, mInputChannel);
  }
}
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                IWindowManager windowManager = getWindowManagerService();
                // 实际上调用的是 WindowMangerService 的openSession 方法。
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {
                                ValueAnimator.setDurationScale(scale);
                            }
                        });
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        return sWindowSession;
    }
}

然后我们看一下 WindowMangerService 的 openSession 返回的Session,Session中调用addToDisplay方法,最终调用到 WindowMangerService 的 addWindow 方法中,下面的都不讲了,就是WMS与底层绘制的调用了。

参考

以Window视角来看startActivity