Activity、Window、View、ViewRootImpl的理解

概述:

对于题目中的几个对象,我们可以简单地理解为Activity封装了Window,而Widnow又封装了View,而View又是通过ViewRootImpl把它添加WindowManagerService中去的。
要较详细的分析,我们可以先从Activity的启动开始分析。

源码分析:

一个Activity开始启动的时候,会在ActivityThread中调用handleLaunchActivity

handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
	
	//应用全局windowmanager的初始化
	 WindowManagerGlobal.initialize();
	
	//执行创建Activity的方法
	final Activity a = performLaunchActivity(r, customIntent)
	
	}

这里初始化了WindowManagerGlobal,它使用单例模式,在整个应用存在。同时WindowManagerGlobal这个类后面将作为WindowManagerImpl策略模式的内部成员变量,代理其大部分功能。

接下来跟进performLaunchActivity方法:

performLaunchActivity(ActivityClientRecord r, Intent customIntent){
		 
		 //创建Context
		 ContextImpl appContext = createBaseContextForActivity(r); 
		
		//类加载器创建Activity
		java.lang.ClassLoader cl = appContext.getClassLoader();
		activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
					
		//创建Application			
		Application app = r.packageInfo.makeApplication(false, mInstrumentation);
		
		//activity关联到系统服务上
		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);
		
	}

从上面代码可以看到,在ActivityThread中创建了activity的实例,并调用activity的attach方法。一看整个方法的名字就可以猜到,activity肯定是要把自己和某些重要的东西关联起来了。

来看看activity中对应的代码段:

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) {
		
		//创建phonewindow
		mWindow = new PhoneWindow(this, window, activityConfigCallback);
		
		//UiThread就是ActivityThread
		 mUiThread = Thread.currentThread();
		 //这里把activity的window和windowmanager关联起来
		mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); 
				
		//给activity中的windowmanager赋值		
		 mWindowManager = mWindow.getWindowManager();
	
		}

这个方法里面出现了PhoneWindow,它是抽象类Window的唯一实现类。可以看出PhoneWindow是作为activity的一个成员变量出现的。
实例化PhoneWindow之后,接着给它设置了WindowManager,这样activity的window和windowmanager就关联了起来。
这里可以拓展一下,其实这个mWindowManager并不是系统服务的全局WindowManager,而是一个相当于只属于此activity的“localWindowManager”,仅作了解。

分析到这里,只是做了一些前期的准备工作,继续吧。

ActivityThread的performLaunchActivity在调用了activitity的attach方法之后,还有动作哦:

 mInstrumentation.callActivityOnCreate(activity, r.state);

不用说,这个方法跟下去肯定会调用到activity的onCreate方法,这个方法我们再熟悉不过了。

于是乎,就来到了了setContentView():

public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

我们上面分析了,这个getWindow()获取到的对象就是PhoneWindow,于是来看看PhoneWindow对应的方法:

//成员变量
	private DecorView mDecor;

	ViewGroup mContentParent;
	
	 public void setContentView(int layoutResID) {
       
        if (mContentParent == null) {
			//初始化decorview以及对应的mContentParent
            installDecor();
        } 
		
		//把内容资源布局文件加载到mContentParent控件上
		mLayoutInflater.inflate(layoutResID, mContentParent)
        
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
		
    }
 private void installDecor() {
 
  if (mDecor == null) {
            mDecor = generateDecor(-1);
           ......         
        }
  if (mContentParent == null) {
		mContentParent = generateLayout(mDecor);
  }
 }

这里又一个关键类DecorView出现了,它作为PhoneWindow的成员变量,在这里是作为Actiity的顶级根View,准确说是ViewGroup。
这里的mContentParent则是DecorView内部的一个子View,它同样是一个ViewGroup,并且我们设置的xml文件就会被加载到这个ViewGroup上。上面的代码中已经标注了出来。

当然,DecorView虽然是继承FrameLayout,但是看它的内部,同样还有一个顶级的根View:

ViewGroup mContentRoot;
	//由phonewindow调用
  void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {

        final View root = inflater.inflate(layoutResource, null);
		// Put it below the color views.
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 
        mContentRoot = (ViewGroup) root;
    }

这里的layoutResource会根据不同的主题Thme来设置不同的xml,例如:

常用的screen_progress.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>
    <!-- Popout bar for action modes -->
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />

    <RelativeLayout android:id="@android:id/title_container" 
        style="?android:attr/windowTitleBackgroundStyle"
        android:layout_width="match_parent" 
        android:layout_height="?android:attr/windowTitleSize"
    >
    <ProgressBar android:id="@+android:id/progress_circular"
            style="?android:attr/progressBarStyleSmallTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="5dip"
            android:layout_alignParentEnd="true"
            android:layout_centerVertical="true"
            android:visibility="gone"
            android:max="10000"
        />
        <ProgressBar android:id="@+android:id/progress_horizontal"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="2dip"
            android:layout_alignParentStart="true"
            android:layout_toStartOf="@android:id/progress_circular"
            android:layout_centerVertical="true"
            android:visibility="gone"
            android:max="10000" 
        />
        <TextView android:id="@android:id/title"
            style="?android:attr/windowTitleStyle"
            android:layout_width="match_parent" 
            android:layout_height="match_parent"
            android:layout_alignParentStart="true"
            android:layout_toStartOf="@android:id/progress_circular"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical"
            android:scrollHorizontally="true" 
        />
    </RelativeLayout>
    <FrameLayout android:id="@android:id/content"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay"
    />
</LinearLayout>

nice,现在DecorView也实例化了,设置的布局文件也加载进mContentParent了,接下来应该就是绘制显示出来了吧。

没错,接下来就是ActivityThread调用handleResumeActivity方法了:

handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
		final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
		
			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;
			
			//windowmanager关联decorview
			wm.addView(decor, l);
	
	}
performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
			//这里就会去调用我们熟悉的onResume生命周期方法
			 r.activity.performResume(r.startsNotResumed, reason);
	}	

这段代码中,最关键的就是wm.addView这个方法了,我们可以联想到以前在开发时,实现一个悬浮窗的功能,就是调用的这个方法。

从方法名可以看出,这是把decorview添加到windowmanager上去,那我们去看看实现的方法:

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }

之前说过,WindowManagerImpl里面的大部分功能时交由WindowManagerGlobal实现的,跟进去:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
			
		ViewRootImpl root;
        View panelParentView = null;	
	
		root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
		
		root.setView(view, wparams, panelParentView, userId);
	
	}

这里又出现了一个ViewRootImpl,并且这里把要显示的View、ViewRootImpl还有对应的LayoutParams按照统一的顺序存储了起来。

下面我们看一下ViewRootImpl类的注释:

	/**
 * The top of a view hierarchy, implementing the needed protocol between View
 * and the WindowManager.  This is for the most part an internal implementation
 * detail of {@link WindowManagerGlobal}.
 *	
 */  

从注释可以看出ViewRootImpl实现了WindowManagerGlobal里面的大部分功能。
ViewRootImpl是视图层级的顶部,但它不同于DecorView。DecorView是View树的根,而它是ViewRootImpl的一个成员变量,并且通过ViewRootImpl把自己加载到显示服务上去(ViewRootImpl不是严格意义上的View,只是实现了ViewParent接口,更像是一个桥梁)

看它的setView方法:

/**
     * We have one child 从注释可以看出,一个ViewRootImpl只有一个子view
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
			
			mView = view;
			
			mAttachInfo.mRootView = view;
			
            requestLayout();
					
			//这里就是真正把window添加到windowManagerService服务里面去
		    res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);
	}
	
	public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
			
			//检查是否是在主线程绘制
            checkThread();
            mLayoutRequested = true;
			
			//异步执行测量,布局,绘制的操作
            scheduleTraversals();
        }
    } 

这里的变量mWindow类型是内部类W,它是一个binder接口的实现类,并且包含了ViewRootImpl,如下:

static class W extends IWindow.Stub {
        private final WeakReference<ViewRootImpl> mViewAncestor;
        private final IWindowSession mWindowSession;

        W(ViewRootImpl viewAncestor) {
            mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
            mWindowSession = viewAncestor.mWindowSession;
        }

里的scheduleTraversals方法里面,去调用了View(这里是DecorView)的measure、layout、draw方法。也就是说View的绘制流程其实是在ViewRootImpl中被调用的。

最后可以看到,是mWindowSession把这次创建的根View(DecorView)添加到WindowManagerService中去的。mWindowSession的实现类型是Session,继承自IWindowSession,是一个Binder对象,是和WindowManagerService通信的接口,addToDisplayAsUser是一次IPC的过程。

自此,一个Activity的视图便添加到了系统服务中去了,我们的代码就先分析到这里了。

总结:

Activity与Window的关系:
Activity包含一个Window,这个Window实际是PhoneWindow。PhoneWindow其实是对View的进一步封装,内部负责与WindowManager的交互并且显示。而Activity除了封装了显示的功能之外,它更大的意义是作为Android的四大组件之一,为开发者的开发提供更加准确便捷的API,比如它提供生命周期的回调、提供设置各种主题的接口、便捷地设置内容、丰富的启动模式等等。

Window与View的关系:
Window是对View的进一步封装。View可以说只是负责单纯地显示自己的View树,而Window还制定了一系列UI的显示规则,如背景色,标题栏显示,沉浸式等等。Android对窗口的管理是按照Window为单位的,除了直接使用WindowManager.addView的方式。这也是为什么显示系统服务叫WindowManagerService,而不是叫ViewManagerService。Window就像是一本书的一页纸,而View就像是纸上附的一些画、写的一些字,我们只能是一页一页地翻书,然后看书里面的内容。

View与ViewRootImpl的关系:
ViewRootImpl负责调用View的绘制流程,并且它负责把View添加到WindowManagerService中去。一个窗口即一个Window需要显示出来,是需要ViewRootImpl把这个Window的DecorView添加到WindowManagerService中去的。

DecorView与一般的View有什么区别:
一个Window上的View是以树的形式存在的,DecorView就是这棵View树的树根。

ViewRootImpl个数:
一个窗口会对应一个ViewRootImpl,这里的窗口包含Activity、Dialog、Toast、还有悬浮窗。比如一个程序中打开了两个activity,那么就会有两个PhoneWindow,也就是两个ViewRootImpl。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值