源碼分析android 系統framework(一)之Activity 與 Window 與 View 之間的關係

源碼分析android 系統framework(一)之Activity 與 Window 與 View 之間的關係

1.先從第一步Activity 中看起
public class HelloActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_hello);  // 設置UI都是這裏開始的
	}
}
2. Activity.java
    /**
     * Set the activity content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the activity.
     *
     * @param layoutResID Resource ID to be inflated.
     *
     * @see #setContentView(android.view.View)
     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
     */
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID); 從這裏看到我們setContentView 直接set到window中
        initWindowDecorActionBar();
    }
   public Window getWindow() {
        return mWindow;
    }
  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 對象
        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;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
                //每個 Activity 會有一個 WindowManager 對象,這個 mWindowManager 就是和 WindowManagerService 進行通信,也是 WindowManagerService 識別 View 具體屬於那個 Activity 的關鍵,創建時傳入 IBinder 類型的 mToken。這個 Activity 的 mToken 是一個 IBinder,WindowManagerService 就是通過這個 IBinder 來管理 Activity 裏的 View。
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);

        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
        enableAutofillCompatibilityIfNeeded();
    }

可看出 Activity 裏新建一個 PhoneWindow 對象。在 Android 中,Window 是個抽象的概念, Android 中 Window 的具體實現類是 PhoneWindow,Activity 和 Dialog 中的 Window 對象都是 PhoneWindow。

同時得到一個 WindowManager 對象,WindowManager 是一個抽象類,這個 WindowManager 的具體實現是在 WindowManagerImpl 中。

3.Window.java
  /**
     * Convenience for
     * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
     * to set the screen content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the screen.
     *
     * @param layoutResID Resource ID to be inflated.
     * @see #setContentView(View, android.view.ViewGroup.LayoutParams)
     */
    public abstract void setContentView(@LayoutRes int layoutResID);// 這裏看到這個window是一個抽象類
4. PhoneWindow.java
 @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();  // 這裏DecorView 出現了,初始化DecorView,DecorView 其實是一個 FrameLayout
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);//
            將佈局添加到mContentParent
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

在這裏插入圖片描述

總結下來如上圖所示。但是此時我們還沒有在代碼中看到DecorView與PhoneWindow的關係。
那DecorView是何時被繪製到屏幕中的呢。
我們都知道activity的生命週期中,onCreate時activity還是不可見,只有執行完onResume 纔會可見。

5. ActivityThread.java

瞭解Android hander 機器的應該都知道這個類,這個類看名字就知道,他其實就是我們常說的UI線程,主線程。這裏面有個和java 中一模一樣的main方法。裏面有初始化Loop等操作。
我們接着說正題

 @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
      ...........
          //執行到 onResume()
    ActivityClientRecord r = performResumeActivity(token, clearHide);
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();  // 這個decorView 就是我們上面的DecorView
            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;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);  // 將decorView 添加到wms中
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
        ............
public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
        final ActivityClientRecord r = mActivities.get(token);
       .................
        try {
            r.activity.onStateNotSaved();
            r.activity.mFragments.noteStateNotSaved();
            checkAndBlockForNetworkAccess();
            if (r.pendingIntents != null) {
                deliverNewIntents(r, r.pendingIntents);
                r.pendingIntents = null;
            }
            if (r.pendingResults != null) {
                deliverResults(r, r.pendingResults, reason);
                r.pendingResults = null;
            }
            r.activity.performResume(r.startsNotResumed, reason); // 我們看到這裏調用到activity中的Resume

 ...................
6. WindowManagerImpl.java
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {}

WindowManager 是一個接口類型,所以真正實現addView的是WindowManagerImpl

  @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
        //這裏又會調用WindowManagerGlobal 的addview
    }
7.WindowManagerGlobal.java

這個類是一個單例的

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
        // 上面這些錯誤大家剛學android時應該在log 中有碰到吧,我們自己學框架時也可以這樣仿照
		............
            root = new ViewRootImpl(view.getContext(), display);
			// 1
            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);
     			將view 添加到wms 中
     ...............

以上1處創建一個 ViewRootImpl,並將之前創建的 DecoView 作爲參數傳入,以後 DecoView 的事件都由 ViewRootImpl 來管理了,比如,DecoView 上添加 View,刪除 View。

8.ViewRootImpl.java
 /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {  
              // 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();   // 這裏這個方法就是用來測量,繪製,已經layout的。
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                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);
                            這裏真正的將view 交給wms了
            ....................
   @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

  void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");  
      // 這裏這句話大家肯定見過,別問我爲什麼知道。哈哈哈哈。子線程不能更新UI就是這來的。
        }
    }
9. 回到WindowManagerGlobal.java 中

上面mWindowSession.addToDisplay 中的mWindowSession 如下:

mWindowSession = WindowManagerGlobal.getWindowSession();

    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                //獲取 InputManagerService 的代理類
                    InputMethodManager imm = InputMethodManager.getInstance();
                    //獲取 WindowManagerService 的代理類
                    IWindowManager windowManager = getWindowManagerService();
                    //經過 Binder aidl調用,最終調用 WindowManagerService 
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {

    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
        // 這裏這個mService 就是WindowManagerService
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
}
10.WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
    ...
    WindowToken token = mTokenMap.get(attrs.token);
    //創建 WindowState
    WindowState win = new WindowState(this, session, client, token,
                attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
    ...
    //調整 WindowManager 的 LayoutParams 參數
    mPolicy.adjustWindowParamsLw(win.mAttrs);
    res = mPolicy.prepareAddWindowLw(win, attrs);
    addWindowToListInOrderLocked(win, true);
    // 設置 input
    mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
    // 創建 Surface 與 SurfaceFlinger 通信,詳見下面[SurfaceFlinger 圖形系統]
    win.attach();
    mWindowMap.put(client.asBinder(), win);
    
    if (win.canReceiveKeys()) {
        //當該窗口能接收按鍵事件,則更新聚焦窗口
        focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                false /*updateInputWindows*/);
    }
    assignLayersLocked(displayContent.getWindowList());
    ...
}

到此這裏 幾個關係就聯繫在一起了。

總結

總體來說上面的意思可以用下面兩張圖表示
在這裏插入圖片描述
在這裏插入圖片描述

  • 1.activity 相當於android 提供給window和view的工具管理類一樣
  • 2.一個Activity 中有一個window,也就是PhoneWindow對象,在PhoneWindow 中有一個DecorView,在setContentView中會將layout填充到此DecorView 中
  • 3.一個應用進程只有一個WindowManagerGlobal 對象,因爲在viewRootImpl 中它是static 類型
  • 4.每一個PhoneWindow 對應一個ViewRootImpl對象
  • 5.WindowMangerGlobal 通過調用ViewRootImpl的setView 方法,完成window 的添加過程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章