Dialog의 작성 프로세스를 한 걸음 한 걸음 추적(1)

Dialog가 어떻게 만들어졌는지 알고 싶었는데 오늘은 시간이 좀 있어서 Dialog의 생성 과정을 추적해 보겠습니다.능력에 한계가 있기 때문에 구체적인 실현 세부 사항은 분석하지 않고 주로 코드 호출 절차, new Diaolg에서dialog에 나타난 화면에서의 호출 관계를 이해한다.다음은 Dialog의 구조 함수에서 시작하여 Dialog의 생성 절차를 추적하기 시작합니다.
하나.Dialog의 구조 함수
구조 함수에서 출발하여 모든 구조 함수는 아래의 이 구조 함수를 호출한다.
   public Dialog(Context context, int theme) {
        this(context, theme, true);
    }

    Dialog(Context context, int theme, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (theme == 0) {
                TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
                        outValue, true);
                theme = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, theme);
        } else {
            mContext = context;
        }

        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Window w = PolicyManager.makeNewWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);
        mListenersHandler = new ListenersHandler(this);
    }
중요하지 않은 것을 제거하는데 이 안에는 두 가지가 특히 중요하다. mWindow 관리자와 mWindow이다.그것들은 각각 창 관리 클래스와 하나의 창을 대표하는 클래스이다.mWindow를 만드는 데는 Policy Manager라는 클래스를 사용합니다. 이 클래스는androidapi에 없고 시스템 수준의 클래스입니다.그래서 이 클래스를 사용하여 윈도우를 만들고 직접 다이얼로그를 만드는 것은 비교적 어렵다.당분간 이런 생각은 하지 않겠습니다. 이 창을 대표하는 클래스 (Window) 는 어떤 것입니까?mWindow Manager는 또 어떻게 창을 관리합니까?문제를 가지고 코드를 계속 추적하다.일단 mWindow가 뭔지 보자.
둘.Window 및 PhoneWindow 클래스에 대한 설명은 다음과 같습니다.
/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background, title
 * area, default key processing, etc.
 *
 * <p>The only existing implementation of this abstract class is
 * android.policy.PhoneWindow, which you should instantiate when needing a
 * Window.  Eventually that class will be refactored and a factory method
 * added for creating Window instances without knowing about a particular
 * implementation.
 */

이 말은 윈도 클래스는 추상적인 맨 윗부분 창의 외관과 행위 전략의 추상적인 클래스라는 뜻이다.이 종류의 실례는 윈도우 관리자에 층을 정한view로 추가해야 합니다.Window는 배경, 제목 영역, 기본 키 처리 등 표준 UI 정책을 제공합니다.
이 종류의 유일한 실례는 바로 Phone Window입니다. 우리가 윈도를 필요로 할 때, Phone Window는 실례화되어야 합니다.최종적으로, 이 클래스는 재구성되고, 윈도우를 만드는 실례를 만드는 공장 방법을 추가합니다. 그러면 이 클래스를 만드는 구체적인 세부 사항을 숨깁니다.
다음은 Phone Window입니다. 이 Window의 유일한 실현 클래스는 UI의 외관 그리기와 이벤트 처리를 어떻게 실현하는지 보십시오.
다음은 구조 함수입니다.
    public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }

구조 함수는 Layout Inflater의 실례를 얻었다. 이 실례는view를 구성하는 데 사용되기 때문에 Phone Window에서 어떤view를 구성할 것으로 추측할 수 있다.
구조 함수를 봤는데 뭘 봐야 되지?이 종류에는 많은 함수가 있는데, 어쨌든 한 번 다 볼 수는 없겠지?이럴 때 생각이 없어지면 다이아로그류를 돌아보고 다이아로그를 어떻게 나타냈는지 봐야 한다.
셋.show () 방법
우리가 Dilog를 어떻게 사용했는지 생각해 보자. 일반적으로 Dilog 대상을 만들고 한 무더기의 물건을 설정한다. 무엇을 설정하든지 최종적으로 show () 방법을 호출해야 한다. 그렇지 않으면 Dialog가 나타나지 않기 때문에 비밀은 show () 방법에 있는 것 같다.
그렇다면 쇼 방법은 무엇일까?
    /**
     * Start the dialog and display it on screen.  The window is placed in the
     * application layer and opaque.  Note that you should not override this
     * method to do initialization when the dialog is shown, instead implement
     * that in {@link #onStart}.
     */
    public void show() {
        if (mShowing) {
            if (mDecor != null) {
                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                }
                mDecor.setVisibility(View.VISIBLE);
            }
            return;
        }

        mCanceled = false;
        
        if (!mCreated) {
            dispatchOnCreate(null);
        }

        onStart();
        mDecor = mWindow.getDecorView();

        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
            final ApplicationInfo info = mContext.getApplicationInfo();
            mWindow.setDefaultIcon(info.icon);
            mWindow.setDefaultLogo(info.logo);
            mActionBar = new WindowDecorActionBar(this);
        }

        WindowManager.LayoutParams l = mWindow.getAttributes();
        if ((l.softInputMode
                & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
            WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
            nl.copyFrom(l);
            nl.softInputMode |=
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
            l = nl;
        }

        try {
            mWindowManager.addView(mDecor, l);
            mShowing = true;
    
            sendShowMessage();
        } finally {
        }
    }
코드가 비교적 많고 주간을 잡는다.mWindow에서 DecorView 객체를 가져옵니다.2. Window Manager의 addView() 방법을 사용하여 DecorView를 추가합니다.그렇다면 이 DecorView는 무엇일까요?
넷.DecorView
DecorView의 정의는 다음과 같습니다.
    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
DecorVeiw의 정의에서 볼 수 있듯이 FrameLayout을 계승했다. 즉, 이것은 하나의 View이다.세 번째 show () 방법에서 DecorView가 처음으로 윈도우 관리자에 추가된 것은 DecorView가 윈도우의 루트 View라는 것을 의미하는 것입니까?그래야지, 그렇지 않으면 또 누가 있겠는가?DecorView에서 분석을 많이 하지 않았습니다. 왜냐하면, 제가 알고 싶은 것은Dialog가 디스플레이에 생성되는 과정입니다.
오.mWindowManager,addView(...)도대체 무슨 짓을 한 거야?
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
mGlobal의addview(...)를 다시 호출한 것을 볼 수 있습니다.메서드, 이 메서드는 다음과 같습니다.
    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");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent and we're running on L or above (or in the
            // system context), assume we want hardware acceleration.
            final Context context = view.getContext();
            if (context != null
                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            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);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

이 방법은 비교적 복잡하다. 코드는 매우 길지만, 하는 일은 결코 복잡하지 않다. 앞에서 무엇을 하든지, 마지막으로, 코드는view, 루트, wparams와 대응하는 데이터 구조에 추가된다.view는 Dialog에서 전해 내려오는 DecorView입니다.
wparams는 우리가 전해 준 또 다른 매개 변수입니다. 여기에 또 처리를 했습니다.
세 가지 데이터 구조는 다음과 같습니다.
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
여기에 무거워 보이는 또 다른 것이 생겼다. 바로 루트다. 이것은 ViewRootImpl의 실례이다.그럼 ViewRootImpl은 또 뭐 하는 거예요?먼저 알아본 다음에 그것을 쓸 때 다시 자세하게 분석해라.
root,view,mParams 세 개는 틀림없이 연락이 있을 거야, 마지막.
root.setView(view, wparams, panelParentView);
          ,              。           。         ,  ViewRootImpl,WindowSession       。

여섯.ViewRootImpl
구조 함수는 다음과 같습니다.
   public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();

        mDisplayAdjustments = display.getDisplayAdjustments();

        mThread = Thread.currentThread();
        mLocation = new WindowLeaked(null);
        mLocation.fillInStackTrace();
        mWidth = -1;
        mHeight = -1;
        mDirty = new Rect();
        mTempRect = new Rect();
        mVisRect = new Rect();
        mWinFrame = new Rect();
        mWindow = new W(this);
        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
        mViewVisibility = View.GONE;
        mTransparentRegion = new Region();
        mPreviousTransparentRegion = new Region();
        mFirst = true; // true for the first time the view is added
        mAdded = false;
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
        mAccessibilityManager = AccessibilityManager.getInstance(context);
        mAccessibilityInteractionConnectionManager =
            new AccessibilityInteractionConnectionManager();
        mAccessibilityManager.addAccessibilityStateChangeListener(
                mAccessibilityInteractionConnectionManager);
        mHighContrastTextManager = new HighContrastTextManager();
        mAccessibilityManager.addHighTextContrastStateChangeListener(
                mHighContrastTextManager);
        mViewConfiguration = ViewConfiguration.get(context);
        mDensity = context.getResources().getDisplayMetrics().densityDpi;
        mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
        mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
        mChoreographer = Choreographer.getInstance();
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        loadSystemProperties();
        mWindowIsRound = context.getResources().getBoolean(
                com.android.internal.R.bool.config_windowIsRound);
    }

구조 함수에서 IWindow Session의 실례를 얻었는데, 이 종류는 또 무엇을 하는 것입니까????
일곱IWindowSession
IWindowSession은 구현 클래스가 Session인 인터페이스로, Session의 정의는 다음과 같습니다.
final class Session extends IWindowSession.Stub
에서 볼 수 있듯이 이것은 binder 클래스로 서비스와 통신할 수 있다.이렇게 좀 알아봤으면 좋겠어, 본분을 잃어서는 안 돼.
여덟root.setView(view,wparams,panelParentView)가 다시 이 방법으로 돌아가 이 방법이 도대체 무엇을 했는지 살펴보자.
    /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
<span style="white-space:pre">		</span>...
                // 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();
<span style="white-space:pre">		</span>...
<span style="white-space:pre">	      try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                      getHostVisibility(), mDisplay.getDisplayId(),
                       mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }</span>
            }
        }
    }
이 함수는 매우 길고 주요한 단계는 RequestLayout()을 호출하는 것이다.리퀘스트 Layout 호출에 대한 설명입니다: window Manager에 가입하기 전에 첫 번째layout을 호출합니다.시스템의 다른 이벤트를 받기 전에 Relayout을 합니다.이후 mWindowSession을 사용했습니다.addToDisplay 방법.mWindowSession은 실제로 Session의 실례이며, addToDisplay 방법은 다음과 같습니다.
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outInputChannel);
    }

여기에서 mService가 호출된 것을 볼 수 있습니다.dd Window, 여기는 분명히 프로세스 통신입니다. mService는 Window Manager 서비스의 실례입니다. 즉, 진정한 window 추가는 실제로 Window Manager 서비스에서 이루어집니다.
아홉임시 소결
밤이 깊어서 하루 종일 피곤했고 피곤한 기운이 넘쳐 잠을 잘 시간이고 내일도 일찍 일어나서 출근해야 한다.여기까지만 분석해 보면 이러한 추적을 통해 적어도 다음과 같은 결론을 얻을 수 있다.
1. DecorView는 루트 뷰입니다.
2. ViewRootImpl의 setView 방법은 DecorView와 ViewRootImpl을 밀접하게 연결시킨다.
3. Window Manager Service의ddWindow는 최종적으로 Window를 추가했습니다.
뷰 트리를 그리기 시작한 곳을 추적했을 거예요. 구체적으로 어떻게 그렸는지, 아직 긴 코드가 있을 거예요. 이 부분은 다음 절에서 탐색해 보세요.
마지막으로 이것은 제가 처음으로 이 부분의 코드를 분석하려고 시도한 것입니다. 분석이 비교적 거칠고 잘못된 부분도 있을 수 있으니 여러분의 지적을 바랍니다.

좋은 웹페이지 즐겨찾기