3-アクティビティウィンドウビューとの関係は何ですか

1. ページ アクティビティが setContentView(layoutId) を通じて設定されたレイアウトを表示することはわかりますが、ソース コードを通じて、表示作業がアクティビティによって実装されていないこともわかります。

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. PhoneWindow がどのようにレイアウト表示機能を実装しているかを見てみましょう。

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在整个过程的作用。

分析後の全体的な階層関係は次のようになります。

簡単に要約すると、次のようになります。

  1. アクティビティは setcontent を通じてレイアウトを渡します。実際にレイアウトを受け取るクラスは getWindow() です。
  2. getWindow() が取得するのは、ウィンドウ クラスの実装クラス - PhoneWindow
  3. PhoneWindow は、アクティビティの作成時に WindowManagerImpl への参照を保持することによってインスタンス化されます。
  4. PhoneWindow には DecorView があり、DecorView 内には mContentParent のレイアウトがあります。
  5. レイアウトを mContentParent に解析して、レイアウトがウィンドウに表示されるようにします。

この時点では、アクティビティはウィンドウから完全に自由であり、ビューの読み込みと表示のプロセスに参加していないことがわかりますが、これは厄介なアクティビティです。

この時点では、ウィンドウはアクティビティに関連付けられておらず、onCreate() で setContent(layoutId) が呼び出されるため、この時点ではページは表示されません。

onresume() の間、ページ レイアウトはすでに表示されています。

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) {           
          }
    }

ここまででWindowManagerSystemにビューが追加され、ビューの表示機能が大まかに完成しました。

その前に、レイアウト ID が Decorview の mContentParent にどのように解析されるかを段階的に分析し、次に、表示のために Decorview を WMS に追加する方法を段階的に分析して、ビューの読み込みと表示部分がすべて完了するようにしました。 。

おすすめ

転載: blog.csdn.net/sunguanyong/article/details/125795413