最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android?WindowManager深層理解view繪制實(shí)現(xiàn)流程

 更新時(shí)間:2022年11月14日 16:29:48   作者:devnn  
WindowManager是Android中一個(gè)重要的Service,是全局且唯一的。WindowManager繼承自ViewManager。WindowManager主要用來(lái)管理窗口的一些狀態(tài)、屬性、view增加、刪除、更新、窗口順序、消息收集和處理等

前言

又是一年一度的1024程序員節(jié)了,今天不寫點(diǎn)什么總感覺(jué)對(duì)不起這個(gè)節(jié)日。想來(lái)想去,就寫點(diǎn)關(guān)于View的繪制。本文不會(huì)重點(diǎn)講View繪制三大回調(diào)函數(shù):onMeasure、onLayout、onDraw,而是站在Android framework的角度去分析一下View的繪制。

  • View是如何被渲染到屏幕中的?
  • ViewRoot、DecorView、Activity、Window、WindowManager是什么關(guān)系?
  • View和Surface是什么關(guān)系?
  • View和SurfaceFlinger、OpenGL ES是什么關(guān)系?

計(jì)算機(jī)的圖像一般是需要經(jīng)過(guò)底層的圖像引擎輸出GPU需要的數(shù)據(jù)交給GPU,顯示器從GPU從不斷的取出渲染好的數(shù)據(jù)顯示到屏幕上。

熟悉Andriod體系架構(gòu)的人都知道,Android底層渲染圖像的引擎是OpenGL ES/Vulkan。那么View是被誰(shuí)渲染的呢?沒(méi)錯(cuò),View最終也是交給底層渲染引擎的,那么從View到OpenGL ES這中間經(jīng)歷了哪些過(guò)程呢?

setContentView()流程

在Activity的onCreate中我們一般會(huì)通過(guò)setContentView來(lái)給Activity設(shè)置界面布局。這個(gè)時(shí)候,Activity是否開始渲染了呢?并沒(méi)有,setContentView只是構(gòu)建整個(gè)DecorView的樹。

//android.app.Activity
 public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

setContentView是調(diào)用Window的setContentView,而PhoneWindow是Window的唯一實(shí)現(xiàn)類:

//com.android.internal.policy.PhoneWindow
 @Override
    public void setContentView(int layoutResID) {
         if (mContentParent == null) {
            installDecor();//1,安裝DecorView
        } 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);//2,解析layoutResID
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;

1處開始安裝DecorView,主要是new一個(gè)DecorView,并找到其中id等于content的布局,通過(guò)mContentParent引用。我們?cè)趚ml的寫的布局就是添加到這個(gè)mContentParent容器中。

//com.android.internal.policy.PhoneWindow
private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);//new一個(gè)DecorView
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
         if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);//找到id==content的ViewGroup
            ...
  }

2處通過(guò)LayoutInflator解析傳入的layoutResID,解析成View并添加到mContentParent中。mContentParent就是我們xml界面的中的id等于content的布局:

綜上分析,setContentView主要完成兩個(gè)功能:

1、構(gòu)建DecorView

2、解析自定義的xml布局文件,添加到DecorView的content中。

所以setContentView還沒(méi)有真正開始渲染圖像。

思考:如果我們沒(méi)有調(diào)用setContentView,Activity能正常啟動(dòng)嗎?為什么?

WindowManager.addView流程

Android的所有View都是通過(guò)WindowManager.addView添加到屏幕中的。那么Activity的DecorView是什么時(shí)調(diào)被添加到屏幕中的呢?

答案在ActivityThreadhandleResumeActivity方法中:

//android.app.ActivityThread
   @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
         ...
         //執(zhí)行Activity的onResume生命周期
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
         ...
		if (r.window == null && !a.mFinished && willBeVisible) {
	            r.window = r.activity.getWindow();
	            View decor = r.window.getDecorView();//1、調(diào)用了window.getDecorView()方法
	            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);//2、開始調(diào)用WindowManager.addView將view添加到屏幕
	                } else {
	                    // The activity will get a callback for this {@link LayoutParams} change
	                    // earlier. However, at that time the decor will not be set (this is set
	                    // in this method), so no action will be taken. This call ensures the
	                    // callback occurs with the decor set.
	                    a.onWindowAttributesChanged(l);
	                }
	            }
	            // If the window has already been added, but during resume
	            // we started another activity, then don't yet make the
	            // window visible.
	        } else if (!willBeVisible) {
	            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
	            r.hideForNow = true;
	        }

wm.addView才是開始DecorView渲染的入口。而它的觸發(fā)時(shí)機(jī)是在Activity的onResume生命周期之后,所以說(shuō)onResume之后View才會(huì)顯示在屏幕上,并且渲染完成才可以獲取到View的寬度。

主要看下1處調(diào)用了window的getDecorView()方法:

//com.android.internal.policy.PhoneWindow
   @Override
    public final @NonNull View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

這里可以看出,即使我們沒(méi)有調(diào)用setContentView,DecorView也會(huì)初始化,只是會(huì)顯示空白頁(yè)面。

然后我們重點(diǎn)看下2處的代碼,通過(guò)WindowManager的addView方法將DecorView添加到window中了:wm.addView(decor, l)

繼續(xù)分析addView之前先梳理一下必要的基本知識(shí)。

上面的wm雖然是ViewManager類型的,它實(shí)際就是WindowManager。

WindowManager是一個(gè)接口,它繼承自ViewManager。

public interface WindowManager extends ViewManager {
...
}

可以看到WindowManager的實(shí)現(xiàn)類是WindowManagerImpl,后面WindowManager的功能都是靠WindowManagerImpl來(lái)實(shí)現(xiàn)的。

Window是抽象類,PhoneWindow是它的實(shí)現(xiàn)。WindowManager是Window的成員變量,Window和WindowManager都是在Activity的attach方法中初始化的:

//android.app.Activity
  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, IBinder assistToken) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window, activityConfigCallback);//1,初始化window
        ...省略無(wú)關(guān)代碼

        mWindow.setWindowManager(  //2、給window初始化windowManager
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();//3、Activity通過(guò)mWindowManager引用window中的WindowManager,兩個(gè)wm是同一個(gè)東西。
        mCurrentConfig = config;
        mWindow.setColorMode(info.colorMode);
        setAutofillOptions(application.getAutofillOptions());
        setContentCaptureOptions(application.getContentCaptureOptions());
    }

1處開始初始化window,并賦值給Activity的成員變量mWindow

2處給window設(shè)置windowManager

3處Activity通過(guò)mWindowManager引用window中的WindowManager,兩個(gè)wm是同一個(gè)東西。

然后重點(diǎn)看下setWindowManager方法的實(shí)現(xiàn):

//android.view.Window
   public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

梳理完基本關(guān)系,再回頭看下wm.addView過(guò)程。

//android.view.WindowManagerImpl
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

可以看到wm.addView交給了mGolbal對(duì)象。

mGolbal是WindowManagerGlobal類型的全局單例:

public final class WindowManagerImpl implements WindowManager {
   @UnsupportedAppUsage
   private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
   ...

繼續(xù)看WindowManagerGlobal.addView是如何實(shí)現(xiàn)的。

//android.view.WindowManagerGlobal
 public void addView(View view, ViewGroup.LayoutParams params,
           Display display, Window parentWindow) {
 	   		// ...省略無(wú)關(guān)代碼
   			ViewRootImpl root;
       		View panelParentView = null;
       	 	// ...省略無(wú)關(guān)代碼
           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.
               if (index >= 0) {
                   removeViewLocked(index, true);
               }
               throw e;
           }
       }
   }

可以看到,這里創(chuàng)建了一個(gè)ViewRootImpl對(duì)象root,并將view、root、wparams保存到了集合中。最后調(diào)用了ViewRootImpl的setView方法設(shè)置視圖。

繼續(xù)跟蹤ViewRootImplsetView方法。

//android.view.ViewRootImpl
  public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
				//...省略不重要代碼
                mAdded = true;
                int res; /* = WindowManagerImpl.ADD_OKAY; */
                // 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();
                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(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
                    setFrame(mTmpFrame);
                } 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();
                    }
                }
              //...省略不重要代碼
                if (view instanceof RootViewSurfaceTaker) {
                    mInputQueueCallback =
                        ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
                }
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
                view.assignParent(this);
                //...省略不重要代碼
            }
        }
    }

WMS是Android窗口管理系統(tǒng),在將View樹注冊(cè)到WMS之前,必須先執(zhí)行一次layout,WMS除了窗口管理之外,還負(fù)責(zé)各種事件的派發(fā),所以在向WMS注冊(cè)前app在確保這棵view樹做好了接收事件準(zhǔn)備。

ViewRoot起到中介的作用,它是View樹的管理者,同時(shí)也兼任與WMS通信的功能。

mWindowSession.addToDisplay將View的渲染交給了WindowManagerService。

mWindowSession是IWindowSession類型的變量,在服務(wù)端的實(shí)現(xiàn)類是Session.java,它是一個(gè)Binder對(duì)象。

  @UnsupportedAppUsage
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    // Emulate the legacy behavior.  The global instance of InputMethodManager
                    // was instantiated here.
                    // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
 @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

可以看到最終是通過(guò)WindowManagerService完成了Window的添加。

到此這篇關(guān)于Android WindowManager深層理解view繪制實(shí)現(xiàn)流程的文章就介紹到這了,更多相關(guān)Android view繪制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android通過(guò)AIDL在兩個(gè)APP之間Service通信

    Android通過(guò)AIDL在兩個(gè)APP之間Service通信

    這篇文章主要為大家詳細(xì)介紹了Android通過(guò)AIDL在兩個(gè)APP之間Service通信,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • android實(shí)現(xiàn)上滑屏幕隱藏底部菜單欄的示例

    android實(shí)現(xiàn)上滑屏幕隱藏底部菜單欄的示例

    這篇文章主要介紹了android實(shí)現(xiàn)上滑屏幕隱藏底部菜單欄的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-02-02
  • Android Studio多渠道打包的配置方法

    Android Studio多渠道打包的配置方法

    今天小編就為大家分享一篇關(guān)于Android Studio多渠道打包的配置方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • Android ijkplayer的使用方法解析

    Android ijkplayer的使用方法解析

    這篇文章主要介紹了Android ijkplayer的使用方法解析,ijkplayer是Bilibili基于ffmpeg開發(fā)并開源的輕量級(jí)視頻播放器,有興趣的可以了解一下
    2017-10-10
  • 淺談Okhttp去除請(qǐng)求頭user-agent

    淺談Okhttp去除請(qǐng)求頭user-agent

    本篇文章主要介紹了淺談Okhttp去除請(qǐng)求頭user-agent,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • Android?Studio中使用SQLite的操作方法

    Android?Studio中使用SQLite的操作方法

    這篇文章主要介紹了Android?Studio中使用SQLite的操作方法的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • Android編程之菜單實(shí)現(xiàn)方法

    Android編程之菜單實(shí)現(xiàn)方法

    這篇文章主要介紹了Android編程之菜單實(shí)現(xiàn)方法,以實(shí)例形式較為詳細(xì)的分析了Android編程實(shí)現(xiàn)菜單的布局及功能相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-11-11
  • Linux系統(tǒng)下安裝android sdk的方法步驟

    Linux系統(tǒng)下安裝android sdk的方法步驟

    這篇文章主要介紹了Linux系統(tǒng)下安裝android sdk的方法步驟,文中介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友可以們下面來(lái)一起看看吧。
    2017-03-03
  • Android異步上傳圖片到PHP服務(wù)器

    Android異步上傳圖片到PHP服務(wù)器

    這篇文章主要介紹了Android異步上傳圖片到PHP服務(wù)器的相關(guān)資料,需要的朋友可以參考下
    2015-12-12
  • Android發(fā)送xml數(shù)據(jù)給服務(wù)器的方法

    Android發(fā)送xml數(shù)據(jù)給服務(wù)器的方法

    這篇文章主要介紹了Android發(fā)送xml數(shù)據(jù)給服務(wù)器的方法,以實(shí)例形式較為詳細(xì)的分析了Android發(fā)送XML數(shù)據(jù)及接收XML數(shù)據(jù)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09

最新評(píng)論

淮阳县| 延安市| 温宿县| 锡林浩特市| 安国市| 来安县| 那曲县| 太谷县| 观塘区| 天津市| 高邮市| 昌邑市| 新郑市| 砀山县| 萝北县| 克什克腾旗| 芜湖县| 靖江市| 铜川市| 鹤庆县| 万山特区| 临沧市| 蓬溪县| 华坪县| 余庆县| 婺源县| 呼和浩特市| 郑州市| 怀远县| 南宁市| 商河县| 孝义市| 乐业县| 平湖市| 临猗县| 新宁县| 日喀则市| 徐闻县| 祁连县| 东乡县| 江城|