Android4.1 关于Rotation相关的Configuration整体分析3


1. 如果要强制设置一个Activity的横竖屏可以通过Manifest去设置,跟Activity相关的信息都会保存在ActivityInfo当中。

android:screenOrientation=["unspecified" | "user" | "behind" |
                                     "landscape" | "portrait" |
                                     "reverseLandscape" | "reversePortrait" |
                                     "sensorLandscape" | "sensorPortrait" |
                                     "sensor" | "fullSensor" | "nosensor"]
    这个screenOrientation会保存在ActivityInfo.screenOrientation中,而且这个值也会在wm.addView(decor, l);的时候传递到WMS中。

2. 如果是要强制设置一个Window的横竖屏可以通过 LayoutParams.screenOrientation来设置。在通过WindowManager.addView的时候把对应的LayoutParams传递给WMS。

WindowManager.LayoutParams.screenOrientation

  

3. 关于需要转屏的两种情况

  Activity -- 启动一个有设置screenOrientation的Activity

3.1 将ActivityRecord中的AppToken添加添加到WMS中

        在ActivityStack.startActivityLocked中,AMS会把ActivityRecord相关的Token加到WMS中,有且仅在这个方法中。这时候会把screenOrientation传递过去。

        atoken.requestedOrientation = requestedOrientation; 这个AppToken会在WMS获取相关App Orientation的时候其作用。

mService.mWindowManager.addAppToken(addPos, r.userId, r.appToken,
                                r.task.taskId, r.info.screenOrientation, r.fullscreen,
                                (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
    public void addAppToken(int addPos, int userId, IApplicationToken token,
            int groupId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked) {
        ... ...
        synchronized(mWindowMap) {
            AppWindowToken atoken = findAppWindowToken(token.asBinder());
            if (atoken != null) {
                Slog.w(TAG, "Attempted to add existing app token: " + token);
                return;
            }
            atoken = new AppWindowToken(this, userId, token);
            atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
            atoken.groupId = groupId;
            atoken.appFullscreen = fullscreen;
            atoken.showWhenLocked = showWhenLocked;
            atoken.requestedOrientation = requestedOrientation;
            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
                    + " at " + addPos);
            mAppTokens.add(addPos, atoken);
            addAppTokenToAnimating(addPos, atoken);
            mTokenMap.put(token.asBinder(), atoken);

            // Application tokens start out hidden.
            atoken.hidden = true;
            atoken.hiddenRequested = true;

            //dump();
        }
    }

3.2 在启动Activity之前去获取当前WMS中的Orientation的Config

      在resumeTopActivityLocked和realStartActivityLocked中回去获取最新的Orientation的config

       1. 通过mWindowManager.updateOrientationFromAppTokens去更新当前WMS中的Orientation值,把WMS中的config返回给AMS  //goto 3.2.1

       2. 通过updateConfigurationLocked去更新AMS中的config,发给每一个ActiviytThread

                synchronized (mService) {
                    Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
                            mService.mConfiguration,
                            next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
                    if (config != null) {
                        next.frozenBeforeDestroy = true;
                    }
                    updated = mService.updateConfigurationLocked(config, next, false, false);
                }

3.2.1 WMS.updateOrientationFroemAppTokens 会直接去调updateOrientationFromAppTokensLocked

          1. 去调用updateOrientationFromAppTokensLocked(false) 做真正的update Orientation的工作,如果返回是true,说明方向发生了变化,就需要去做app的转屏动作。

          2. 如果freezeThisOneIfNeed不为null,说明要做屏幕的转屏操做,就会把当前的atoken freeze掉。

          3. computeNewConfigurationLocked()  计算当前的Configuration,然后返回给AMS。

    private Configuration updateOrientationFromAppTokensLocked(
            Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
        Configuration config = null;

        if (updateOrientationFromAppTokensLocked(false)) {
            if (freezeThisOneIfNeeded != null) {
                AppWindowToken atoken = findAppWindowToken(
                        freezeThisOneIfNeeded);
                if (atoken != null) {
                    startAppFreezingScreenLocked(atoken,
                            ActivityInfo.CONFIG_ORIENTATION);
                }
            }
            config = computeNewConfigurationLocked();

        } else if (currentConfig != null) {
            // No obvious action we need to take, but if our current
            // state mismatches the activity manager's, update it,
            // disregarding font scale, which should remain set to
            // the value of the previous configuration.
            mTempConfiguration.setToDefaults();
            mTempConfiguration.fontScale = currentConfig.fontScale;
            if (computeScreenConfigurationLocked(mTempConfiguration)) {
                if (currentConfig.diff(mTempConfiguration) != 0) {
                    mWaitingForConfig = true;
                    getDefaultDisplayContentLocked().layoutNeeded = true;
                    startFreezingDisplayLocked(false, 0, 0);
                    config = new Configuration(mTempConfiguration);
                }
            }
        }

        return config;
    }

3.2.1.1   updateOrientationFromAppTokensLocked 会查找出当前是否有需要强制Orientation的App或者Window
         1. computeForcedAppOrientationLocked  //goto 3.2.1.1.1

         2. 如果ForcedAppOrientation发生了变化, 就会去通知WindowPolicy去设置当前的Sensor的状态。//goto 3.2.1.1.2

         3. updateRotationUncheckedLocked(inTransaction),由于Orientation可能发生变化,所以需要去重新获取一下Rotation;具体就可以参照前一节了。

             如果Rotation发生了变化就返回true。跟configuration相关的东西,大多都在这个函数中进行。

    boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
        long ident = Binder.clearCallingIdentity();
        try {
            int req = computeForcedAppOrientationLocked();

            if (req != mForcedAppOrientation) {
                mForcedAppOrientation = req;
                //send a message to Policy indicating orientation change to take
                //action like disabling/enabling sensors etc.,
                mPolicy.setCurrentOrientationLw(req);
                if (updateRotationUncheckedLocked(inTransaction)) {
                    // changed
                    return true;
                }
            }

            return false;
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }


3.2.1.1.1 computeForcedAppOrientationLocked

     这个函数的作用就是去查询当前即将要显示的Activity或者Window有没有需要强制Orientation的

     1. 先通过 getOrientationFromWindowsLocked去遍历WMS中的WindowList。

          1)如果在最上面的Window是一个AppWindow就直接返回mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED

          2)如果不是AppWindow,判断这个Window是否可见,如果不可见就continue

          3)最后如果这个Window既不是AppWindow又要是可见的,并且他是有指定ScreenOrientation的,就返回这个window的orientatin。mLastWindowForcedOrientation=req

    2. 如果getOrientationFromWindowsLocked找到的是一个AppWindow或者当前没有指定Orientation的Window,就会走到getOrientationFromAppTokensLocked();

        1)这个函数会去遍历AppWindowToken List去查找需要强制Orientation的Token,这个时候就是根据我们之前在AMS中传递进来的atoken.requestedOrientation;来进行判断,如果设置了就返回回去给req。

    int computeForcedAppOrientationLocked() {
        int req = getOrientationFromWindowsLocked();
        if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
            req = getOrientationFromAppTokensLocked();
        }
        if (mForceLandScape &&
                req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
            req = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        }
        return req;
    }


3.2.1.1.2 PhoneWindowManager.setCurrentOrientationLw()

     1. 用mCurrentAppOrientation保存当前新的Orientation.

     2. updateOrientationListenerLp,通过名字就知道他只是去管理是否启动Sensor Listener的。

    public void setCurrentOrientationLw(int newOrientation) {
        synchronized (mLock) {
            if (newOrientation != mCurrentAppOrientation) {
                mCurrentAppOrientation = newOrientation;
                updateOrientationListenerLp();
            }
        }
    }

3.2.3 WMS. computeNewConfiguration()

      通过computeNewConfigurationLocked() 计算出一个新的configuration.

    public Configuration computeNewConfiguration() {
        synchronized (mWindowMap) {
            Configuration config = computeNewConfigurationLocked();
            if (config == null && mWaitingForConfig) {
                // Nothing changed but we are waiting for something... stop that!
                mWaitingForConfig = false;
                performLayoutAndPlaceSurfacesLocked();
            }
            return config;
        }
    }


3.3 AMS端的工作完成之后,就到ActivityThread和ViewRootImpl的工作了

     3.3.1 在ActivityThread的handleResume的时候会把Activity对应的DectorView加到WMS中,在WMS中会调用addWindow. 如果我们自己添加一个Window的话也是走到这边。

               在AddWindow的时候有可能对Orietation和Configuration

              前提是这个win的isVisibleOrAdding的条件。不过一般都不会满足这个条件。

            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
                reportNewConfig = true;
            }
        }

        if (reportNewConfig) {
            sendNewConfiguration();
        }

3.3.2 当addWindow完成之后,ViewRootImpl就会去requestLayout,而requestLayout实际上就是去做scheduleTraversals 

             1. 在performTraversals中会先去做一次measureHierarchy计算一下View所需的大小。

             2. 然后会调用relayoutWindow去请求WMS帮助实际计算当前Window的布局,创建一个Surface,并且返回对应的Frame。


3.3.2.2 WMS.relayoutWindow()

       1. 根据传进来的requestWidth和requestHeight 或者attrChange的变化来决定是不是真的要relayout,并且把win.mLayoutNeeded设置为true。在performLayoutAndPlaceSurfacesLocked的会根据这个值做判断。

       2. 如果当前的WindowState还没有Surface就调用 winAnimator.createSurfaceLocked();去创建一个Surface会带回给ViewRootImpl

       3. 如果focus window发生变化了就updateFocusedWindowLocked

           如果Layer变化了就调用assignLayersLocked

       4. 再次调用updateOrientationFromAppTokensLocked去判断当前的Orietation是不是发生变化了。//在这个调用中如果有Orientation发生了变化,如果有变化就会调用computeNewConfigurationLocked()去更新DisplayContent相关的信息并且计算出一个新的Configuration

       5. 调用performLayoutAndPlaceSurfacesLocked

       6. 把WindowState中的三个Frame返回给Client端。

    public int relayoutWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int requestedWidth,
            int requestedHeight, int viewVisibility, int flags,
            Rect outFrame, Rect outContentInsets,
            Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
        boolean toBeDisplayed = false;
        boolean inTouchMode;
        boolean configChanged;
        boolean surfaceChanged = false;
        boolean animating;
        ... ...
        synchronized(mWindowMap) {
            // TODO(cmautner): synchronize on mAnimator or win.mWinAnimator.
            WindowState win = windowForClientLocked(session, client, false);
            ... ...
            WindowStateAnimator winAnimator = win.mWinAnimator;
            if (win.mRequestedWidth != requestedWidth
                    || win.mRequestedHeight != requestedHeight) {
                win.mLayoutNeeded = true;
                win.mRequestedWidth = requestedWidth;
                win.mRequestedHeight = requestedHeight;
            }
            if (attrs != null && seq == win.mSeq) {
                win.mSystemUiVisibility = systemUiVisibility;
            }

            if (attrs != null) {
                mPolicy.adjustWindowParamsLw(attrs);
            }

            winAnimator.mSurfaceDestroyDeferred =
                    (flags&WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY) != 0;

            int attrChanges = 0;
            int flagChanges = 0;
            if (attrs != null) {

                flagChanges = win.mAttrs.flags ^= attrs.flags;
                attrChanges = win.mAttrs.copyFrom(attrs);
                if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
                        | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
                    win.mLayoutNeeded = true;
                }
            }
            ... ...
            if (viewVisibility == View.VISIBLE &&
                    (win.mAppToken == null || !win.mAppToken.clientHidden)) {
                toBeDisplayed = !win.isVisibleLw();

    
                if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
                    // To change the format, we need to re-build the surface.
                    winAnimator.destroySurfaceLocked(false);
                    toBeDisplayed = true;
                    surfaceChanged = true;
                }
                try {
                    if (!win.mHasSurface) {
                        surfaceChanged = true;
                    }
                    Surface surface = winAnimator.createSurfaceLocked();
                    if (surface != null) {
                        outSurface.copyFrom(surface);
                        if (SHOW_TRANSACTIONS) Slog.i(TAG,
                                "  OUT SURFACE " + outSurface + ": copied");
                    } else {
                        // For some reason there isn't a surface.  Clear the
                        // caller's object so they see the same state.
                        outSurface.release();
                    }
                } catch (Exception e) {

                }
                if (toBeDisplayed) {
                    focusMayChange = isDefaultDisplay;
                }
                if (win.mAttrs.type == TYPE_INPUT_METHOD
                        && mInputMethodWindow == null) {
                    mInputMethodWindow = win;
                    imMayMove = true;
                }
                if (win.mAttrs.type == TYPE_BASE_APPLICATION
                        && win.mAppToken != null
                        && win.mAppToken.startingWindow != null) {
                    // Special handling of starting window over the base
                    // window of the app: propagate lock screen flags to it,
                    // to provide the correct semantics while starting.
                    final int mask =
                        WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                        | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
                    WindowManager.LayoutParams sa = win.mAppToken.startingWindow.mAttrs;
                    sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask);
                }
            } else {
                winAnimator.mEnterAnimationPending = false;
                if (winAnimator.mSurface != null) {
                    if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
                            + ": mExiting=" + win.mExiting);
                    // If we are not currently running the exit animation, we
                    // need to see about starting one.
                    if (!win.mExiting) {
                        surfaceChanged = true;
                        // Try starting an animation; if there isn't one, we
                        // can destroy the surface right away.
                        int transit = WindowManagerPolicy.TRANSIT_EXIT;
                        if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
                            transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
                        }
                        if (win.isWinVisibleLw() &&
                                winAnimator.applyAnimationLocked(transit, false)) {
                            focusMayChange = isDefaultDisplay;
                            win.mExiting = true;
                        } else if (win.mWinAnimator.isAnimating()) {
                            // Currently in a hide animation... turn this into
                            // an exit.
                            win.mExiting = true;
                        } else if (win == mWallpaperTarget) {
                            // If the wallpaper is currently behind this
                            // window, we need to change both of them inside
                            // of a transaction to avoid artifacts.
                            win.mExiting = true;
                            win.mWinAnimator.mAnimating = true;
                        } else {
                            if (mInputMethodWindow == win) {
                                mInputMethodWindow = null;
                            }
                            winAnimator.destroySurfaceLocked(false);
                        }
                        scheduleNotifyWindowTranstionIfNeededLocked(win, transit);
                    }
                }

                outSurface.release();
                if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);
            }

            if (focusMayChange) {
                //System.out.println("Focus may change: " + win.mAttrs.getTitle());
                if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
                        false /*updateInputWindows*/)) {
                    imMayMove = false;
                }
                //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
            }

            // updateFocusedWindowLocked() already assigned layers so we only need to
            // reassign them at this point if the IM window state gets shuffled
            boolean assignLayers = false;
            ... ...
            if (assignLayers) {
                assignLayersLocked(win.getWindowList());
            }
            configChanged = updateOrientationFromAppTokensLocked(false);
            performLayoutAndPlaceSurfacesLocked();
            if (toBeDisplayed && win.mIsWallpaper) {
                DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
                updateWallpaperOffsetLocked(win,
                        displayInfo.appWidth, displayInfo.appHeight, false);
            }
            if (win.mAppToken != null) {
                win.mAppToken.updateReportedVisibilityLocked();
            }
            outFrame.set(win.mCompatFrame);
            outContentInsets.set(win.mContentInsets);
            outVisibleInsets.set(win.mVisibleInsets);
            ... ...

            mInputMonitor.updateInputWindowsLw(true /*force*/);

        }

        if (configChanged) {
            sendNewConfiguration();
        }

3.3.2.2.6 WMS.performLayoutAndPlaceSurfacesLocked

                WMS.performLayoutAndPlaceSurfacesLockedLoop()

        performLayoutAndPlaceSurfacesLocked直接去调performLayoutAndPlaceSurfacesLockedLoop, 在performLayoutAndPlaceSurfacesLockedLoop里面做两个最主要的操作。

        1. performLayoutAndPlaceSurfacesLockedInner();

        2. requestTraversalLocked();

    private final void performLayoutAndPlaceSurfacesLockedLoop() {
        ... ...

        mInLayout = true;
        boolean recoveringMemory = false;
        ... ...        
        try {
            performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);

            mInLayout = false;

            if (needsLayout()) {
                if (++mLayoutRepeatCount < 6) {
                    requestTraversalLocked();
                } else {
                    Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
                    mLayoutRepeatCount = 0;
                }
            } else {
                mLayoutRepeatCount = 0;
            }

            if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
                mH.removeMessages(H.REPORT_WINDOWS_CHANGE);
                mH.sendMessage(mH.obtainMessage(H.REPORT_WINDOWS_CHANGE));
            }
        } catch (RuntimeException e) {
            mInLayout = false;
            Log.wtf(TAG, "Unhandled exception while laying out windows", e);
        }

        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
    }

3.3.2.2.6.1 WMS.performLayoutAndPlaceSurfacesLockedInner(false)

         1. 首先会获取当前的一个DsiplayContent,由于这个DisplayContent在updateOrientationFromAppTokensLocked的时候已经根据最新的window状态进行跟新过,所以现在的DsiplayContent应该是最新的值。

         2. performLayoutLockedInner; 这个函数回去遍历每一个Window,然后根据当前的DisplayContent去更新Layout.             

              这个方法会根据最新的DisplayContent去判断所有的window是否需要重新layout

              第一步,通过mPolicy.beginLayoutLw去给policy设置一个标准把当前的Content信息设置进去

              第二步, mPolicy.layoutWindowLw(win, win.mAttrs, null); 给win做真正的layout,layout主要做了什么?有几个重要的变量?

                               在layoutWindow中会通过computeFrameLw去计算WindowState的中的Frame,有三个Frame;

         3. updateResizingWindows(w);

              如果WindowState其中的w.mContentInsetsChanged || w.mVisibleInsetsChanged  || winAnimator.mSurfaceResized|| configChanged 任意的一个发生变化了就会加入到mResizingWindows数组中。并且把对应的Window Freezing。

         4. 遍历mResizingWindows, 调用win.mClient.resized通过IPC告诉Clinet端要做resize

             最终ViewRootImpl就会发送一个MSG_RESIZED去处理,WMS会把里面的三个Frame和Configuration发回给Clinet。ViewRoot会保存最新的三个Frame和updateConfiguration,最后 requestLayout();

   private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) {
        ... ...
        Surface.openTransaction();
        try {

            boolean focusDisplayed = false;
            boolean updateAllDrawn = false;

            DisplayContentsIterator iterator = new DisplayContentsIterator();
            while (iterator.hasNext()) {
                final DisplayContent displayContent = iterator.next();
                WindowList windows = displayContent.getWindowList();
                DisplayInfo displayInfo = displayContent.getDisplayInfo();
                final int displayId = displayContent.getDisplayId();
                final int dw = displayInfo.logicalWidth;
                final int dh = displayInfo.logicalHeight;
                final int innerDw = displayInfo.appWidth;
                final int innerDh = displayInfo.appHeight;
                final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);

                // Reset for each display unless we are forcing mirroring.
                if (mInnerFields.mDisplayHasContent != LayoutFields.DISPLAY_CONTENT_MIRROR) {
                    mInnerFields.mDisplayHasContent = LayoutFields.DISPLAY_CONTENT_UNKNOWN;
                }
                ... ...
                int repeats = 0;
                do {
                    repeats++;
                    ... ...
                    // FIRST LOOP: Perform a layout, if needed.
                    if (repeats < 4) {
                        performLayoutLockedInner(displayContent, repeats == 1,
                                false /*updateInputWindows*/);
                    } else {
                        Slog.w(TAG, "Layout repeat skipped after too many iterations");
                    }
                    ... ...
                } while (displayContent.pendingLayoutChanges != 0);

                // Only used if default window
                final boolean someoneLosingFocus = !mLosingFocus.isEmpty();

                final int N = windows.size();
                for (i=N-1; i>=0; i--) {
                    WindowState w = windows.get(i);
                    ... ...

                    //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
                    w.mContentChanged = false;

                    // Moved from updateWindowsAndWallpaperLocked().
                    if (w.mHasSurface) {
                        // Take care of the window being ready to display.
                        ... ...
                        }
                        ... ...
                        }
                    }

                    if (isDefaultDisplay && someoneLosingFocus && (w == mCurrentFocus)
                            && w.isDisplayedLw()) {
                        focusDisplayed = true;
                    }

                    updateResizingWindows(w);
                }

          ... ...

        for (i = mResizingWindows.size() - 1; i >= 0; i--) {
            WindowState win = mResizingWindows.get(i);
            if (win.mAppFreezing) {
                // Don't remove this window until rotation has completed.
                continue;
            }
            final WindowStateAnimator winAnimator = win.mWinAnimator;
            try {
                if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
                        "Reporting new frame to " + win + ": " + win.mCompatFrame);
                int diff = 0;
                boolean configChanged = win.isConfigChanged();
                if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION)
                        && configChanged) {
                    Slog.i(TAG, "Sending new config to window " + win + ": "
                            + winAnimator.mSurfaceW + "x" + winAnimator.mSurfaceH
                            + " / " + mCurConfiguration + " / 0x"
                            + Integer.toHexString(diff));
                }
                win.setConfiguration(mCurConfiguration);
                if (DEBUG_ORIENTATION &&
                        winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i(
                        TAG, "Resizing " + win + " WITH DRAW PENDING");
                win.mClient.resized(win.mFrame, win.mLastContentInsets, win.mLastVisibleInsets,
                        winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING,
                        configChanged ? win.mConfiguration : null);
                win.mContentInsetsChanged = false;
                win.mVisibleInsetsChanged = false;
                winAnimator.mSurfaceResized = false;
            } catch (RemoteException e) {
                win.mOrientationChanging = false;
            }
            mResizingWindows.remove(i);
        }

        ... ...

        // Finally update all input windows now that the window changes have stabilized.
        mInputMonitor.updateInputWindowsLw(true /*force*/);
        ... ...
        }

        // Check to see if we are now in a state where the screen should
        // be enabled, because the window obscured flags have changed.
        enableScreenIfNeededLocked();

        updateLayoutToAnimationLocked();
        ... ...
    }

在WindowState中有几个Frame

    // "Real" frame that the application sees, in display coordinate space.
    final Rect mFrame = new Rect();
    final Rect mLastFrame = new Rect();
    // Frame that is scaled to the application's coordinate space when in
    // screen size compatibility mode.
    final Rect mCompatFrame = new Rect();


    final Rect mContainingFrame = new Rect();
    final Rect mDisplayFrame = new Rect();
    final Rect mContentFrame = new Rect();
    final Rect mParentFrame = new Rect();
    final Rect mVisibleFrame = new Rect();




     Window

<think>好的,用户想要了关于Houdini的笔记。首先,我需要确定用户的需求是什么。Houdini是一个复杂的3D动画和视觉效果软件,主要用于电影、电视和游戏行业。用户可能是刚开始学习Houdini,想要一些基础的学习笔记,或者他们可能有特定的功能或问题需要决。 接下来,我应该考虑用户可能的背景。如果他们刚入门,可能需要介绍Houdini的基本概念、界面布局、常用节点和工作流程。如果他们有一定经验,可能需要更深入的内容,比如VEX脚本、动力学模拟或高级建模技巧。不过,用户的问题比较笼统,只是“介绍Houdini笔记”,所以最好从基础开始,结构清晰,分点说明。 我需要确保回答覆盖Houdini的核心特点,比如程序化工作流程、节点系统、SOP和DOP等模块的区别,以及常用工具和资源。同时,用户要求使用中文,结构清晰,分步骤决问题,所以需要将内容组织成易于理的章节,可能包括基础概念、界面介绍、常用节点、学习资源等。 还要注意用户提到的系统级指令,比如数学表达式用$...$格式,独立公式用$$...$$,但Houdini相关的笔记可能涉及较少的数学公式,不过如果有涉及表达式或法的地方,需要正确应用LaTeX。例如,在粒子系统或物理模拟时,可能需要提到一些数学公式,比如流体动力学的纳维-斯托克斯方程,这时候需要用正确的格式。 另外,需要验证提供的信息是否准确,比如节点名称、工作流程是否正确,避免错误。例如,SOP是几何体操作,DOP是动力学模拟,VOP是可视化编程,这些术语需要正确使用。同时,推荐的学习资源如官方文档、Entagma、cgwiki等,需要确认链接是否有效,但可能不需要具体链接,只需提及名称。 用户可能没有提到的深层需求是希望有一个结构化的学习路径,或者常见问题的决方法,所以在笔记中可以加入一些学习建议和常见问题,比如如何组织节点网络,如何优化性能等。 最后,确保回答符合用户可见层的格式要求,结构清晰,分步骤介绍,使用中文,避免专业术语过多,必要时进行释。同时,保持口语化,让内容更易于理,不过用户可能希望信息准确且专业,所以需要平衡口语化和专业性。</think>以下是一份结构清晰的Houdini学习笔记框架,涵盖核心概念与实用技巧: # Houdini 学习笔记 $$ \text{基于版本: Houdini 19.5+} $$ --- ## 一、核心特性 1. **程序化建模** - 非破坏性工作流:通过参数调整回溯修改 - 节点网络可视化:$node\_network \rightarrow geometry\_output$ 2. **动力学系统** - 刚体/柔体碰撞:`RBD Bullet Solver` - 流体模拟:`Flip Solver`(基于纳维-斯托克斯方程) $$ \frac{\partial \mathbf{u}}{\partial t} + (\mathbf{u} \cdot \nabla)\mathbf{u} = -\frac{1}{\rho}\nabla p + \nu \nabla^2 \mathbf{u} + \mathbf{g} $$ --- ## 二、界面模块速查 | 模块 | 快捷键 | 功能说明 | |-------------|--------|-------------------------| | SOP | F1 | 几何体操作(建模/变形) | | DOP | F4 | 动力学模拟(物理效果) | | VOP | - | 可视化编程(材质/处理) | --- ## 三、常用节点析 1. **Geometry节点** - `Attribute Create`:创建自定义属性 ```vex @velocity = set(0, @Time * 9.8, 0); // 添加随时间变化的Y轴速度 ``` 2. **粒子系统** - `Pop Network` 工作流程: ``` Emitter → Force → Collision → Solver → Render ``` --- ## 四、VEX编程基础 1. 数据类型 - 向量运:`vector pos = @P * {0.5, 1, 0.5};` - 矩阵应用:`@P *= ident();` 2. 常用函数 ```vex float noise = noise(@Time + @ptnum); // 柏林噪声 ``` --- ## 五、优化建议 1. **性能优化** - 使用`Divide`节点简化碰撞体 - 开启`Geometry Spreadsheet`监控属性 2. **项目管理** - 使用`Subnet`封装复杂节点组 - 通过`Null节点`控制最终输出 --- ## 六、学习资源推荐 1. 官方文档:SideFX官网文档库 2. 教程平台:Entagma(高级特效案例) 3. 社区资源:cgwiki(Houdini析) --- > **注**:建议配合实践案例学习,从简单粒子系统入手,逐步过渡到流体/布料等复杂模拟。遇到节点报错时,优先检查数据流连接顺序与属性命名一致性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值