3-What is the relationship between Activity Window View

1. We know that the page activity displays the set layout through setContentView(layoutId), but we also know through the source code that the display work is not implemented by the activity.

1,xxActivity.java
 @Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_xxx);//设置布局文件
}

2,Activity.java
public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);//由此可知,是调用window类中的方法实现布局显示的
        initWindowDecorActionBar();
    }
    
public Window getWindow() {//问题来了,mWindow是哪里实例化的
        return mWindow;
    }


3,直接全局查找实例化相关的代码
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) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);//PhoneWindow是Window抽象类的实现类,这里实例化了
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
		...
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//将系统windowmanager设置给phonewindow
    	...
}


4,PhoneWindow持有一个WindowManagerImpl的引用
 /**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is <em>not</em> used for displaying the
     * Window itself -- that must be done by the client.
     *将此窗口使用的窗口管理器设置为,例如,显示面板。这不用于显示窗口本身——这必须由客户端完成。
     *
     * @param wm The window manager for adding new windows.
     */
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }


5,通过activity启动源码分析,attach()的调用是在ActivityThread中的performLaunchActivity(),反射创建activity并调用attach()方法
  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(//之前分析过,这里面通过反射生成activity
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (activity != null) {
                ContextImpl appContext = new ContextImpl();
                appContext.init(r.packageInfo, r.token, this);
                appContext.setOuterContext(activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                activity.attach(appContext, this, getInstrumentation(), r.token,//activity这里调用了attach(),这里就是上面的attach()
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
				...
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
        ...
        } catch (Exception e) {
            ...
        }

        return activity;
    }
    
6,通过第4步实现了activity与PhoneWindow的关联,每个activity都持有一个Window的实现类PhoneWindow,并通过PhoneWindow实现对布局的展示(setContentView())
   

2. Let's take a look at how PhoneWindow implements the layout display function.

1,整个PhoneWindow共3000多行代码,我们从setContentView()开始入手。
	PhoneWindow.java//一个activity拥有一个Window的实现类-PhoneWindow,PhoneWindow作为布局的管理类
    

	private ViewGroup mContentParent;
    DecorView decorView;//一个定义在PhoneWindow并继承自FrameLayout的布局实际操作类
	@Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();//创建DecorView
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);//传进来的布局添加到mContentParent上
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }


2,是通过创建DecorView来管理布局的

    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();//实例化DecorView,在DecorView的构造方法中,还带了一个PhoneWindow的引用
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
        }
       if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);//去实例化mContentParent

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);
			//decorview中的标题
            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
                mDecorContentParent.setWindowCallback(getCallback());
                if (mDecorContentParent.getTitle() == null) {
                    mDecorContentParent.setWindowTitle(mTitle);
                }
               ...

            } else {
                mTitleView = findViewById(R.id.title);
              
            }         
        }
    }
 
3,看看mContentParent是如何诞生的
protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme. 使用来自theme的配置数据
        

        TypedArray a = getWindowStyle();
		...太多了,省略中...
                 
       
        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        
        mDecor.startChanging();

        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//findviewbyId是谁调用的?答案是decorView 
       																		  //而且在布局screen_simple.xms可见,mContentParent也是个framelayout
       ...
 
        mDecor.finishChanging();

        return contentParent;
    }
    
由此可知,contentParent是从DecorView中生成的,而传进来的布局layoutId是在解析在contentParent中

4,题外话:关于findViewById
在没有深究源码之前,一直以为activity中常用的findviewById是View中相关的方法,毕竟见闻知意,是根据ID找到view,而且好巧不巧的是,view中也有findviewById
但实际上,activity中常用的findviewById()实际上是Window类提供的,也就是PhoneWindow中实现的

Activity.java
 @Nullable
public <T extends View> T findViewById(@IdRes int id) {
        return getWindow().findViewById(id);
}

Window.java
@Nullable
public <T extends View> T findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
}
虽然DecorView本质也是View,但也体现了DecorView作为根view在整个过程的作用。

After analysis, the overall hierarchical relationship is as follows:

To sum it up briefly:

  1. The activity passes in the layout through setcontent, and the class that actually receives the layout is getWindow()
  2. What getWindow() gets is the implementation class of the window class - PhoneWindow
  3. PhoneWindow is instantiated when the activity is created, by holding a reference to WindowManagerImpl
  4. PhoneWindow has a DecorView, and inside the DecorView there is a layout of mContentParent
  5. Parse the layout to mContentParent, so that the layout is displayed on the window

At this time, you will find that the activity is completely free from the window, and does not participate in the loading and display process of the view, which is an embarrassing activity.

At this time, the window has not been associated with the activity, and setContent(layoutId) is called in onCreate(), so the page is not visible at this time.

While onresume(), the page layout is already visible.

1,在ActivityThread的handleResumeActivity()中
    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
     ...
        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管理类
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
                       if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);//把decorview添加到windowManager类中
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }


        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
		...
    }

2,看下WindowManager是如何addView()
WindowManager是个接口类,继承自ViewManager类,而WindowManager的实现类却是WindowManagerImpl

Window.java
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

3,WindowManagerImpl的AddView()
    
public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); //实际的处理类   
    
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
 	...   
}

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

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
           
         
            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 {
                root.setView(view, wparams, panelParentView);//还是得看ViewRootImpl
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

5,ViewRootImpl

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
				...
                requestLayout();//请求刷新UI 此方法执行后viewRootImpl所关联的View将执行measure->layout->draw方法
                				//确保在添加到window上显示到屏幕上之前,完成测量及绘制
      			...
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                  //mWindowSession是WindowManagerGlobal的单例,此方法为将view添加到WindowManagerSystem(WMS)中,由WMS来完成最终的添加功能
                } catch (RemoteException e) {           
          }
    }

So far, the view has been added to the WindowManagerSystem, and the display function of the view has been roughly completed.

Before that, we analyzed how the layout id is parsed into mContentParent in the decorview step by step, and then analyzed how to add the decorview to the WMS for display step by step, so that the loading and display part of the View is all completed.

Guess you like

Origin blog.csdn.net/sunguanyong/article/details/125795413