android中window和windowManager源码分析(android-api-23)

一、前言

在android中window无处不在,如activity、dialog、toast等。它是view所依附的载体,每个window都对应于一个View和一个ViewRootImpl。ViewRootImpl就是Window和view的连接纽带。windowManager顾名思义是实现对window的管理,事实上,是实现对依附于window中的view的管理。

二、从dialog开始

dialog是大家都很熟悉的东西,在实际的项目开发中几乎都能用到。因此很有必要了解下dialog的内部实现,以明白其实现原理。对于dialog的使用最简单的形式,就是下列这种形式:

new Dialog(MainActivity.this).show();
只需要上面简单的一句代码,即可出show出来一个对话框来,根据前言可知,这里显然涉及到了window的概念。因此,这里先分两个方向走,一个是dialog的构造方法,一个是show方法,来探究window的概念。

2.1 dialog的构造方法

dialog有多个构造方法,但最终都会调用下面的构造方法,如下所示。

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    if (createContextThemeWrapper) {
        if (themeResId == 0) {
            final TypedValue outValue = new TypedValue();
            context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
            themeResId = outValue.resourceId;
        }
        mContext = new ContextThemeWrapper(context, themeResId);
    } else {
        mContext = context;
    }
    //这里出现了我们将要探讨的windowManager,事实上无论是activity还是dialog都会持有windManager服务
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    //这里第一次出现了我们要探讨了window,即产生了一个实际的PhoneWindow对象,事实上,Window仅仅是个抽象类,
    //对于手机,一般是由PhoneWindow进行实现
    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    //从下面可以看出来,dialog有自己处理事件的能力,因为window在此设置了callback
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setWindowManager(mWindowManager, null, null);
    w.setGravity(Gravity.CENTER);

    mListenersHandler = new ListenersHandler(this);
}
上面的代码比较简单,核心是产生了一个WindowManager和Window对象,并设置了一些回调方法,以便进行window事件的相关处理。我们都知道 dialog是依附于activity而存在的,但能独立处理一些事件,上面的代码给出了答案。 其实从dialog的构造方法来看显然没有做什么有效的工作,so,真正的主题应该是在show方法中了。

2.2 dialog的show方法分析

public void show() {
//这里处理了已经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;    
//这里如果没有create则会调用dispatchOncreate,通过该方法,最终会调用实际的dialog中的onCreate方法
//之所以说是实际的,因为我们可能会继承dialog进而复写onCreate方法
    if (!mCreated) {
        dispatchOnCreate(null);
    }

//dialog所有的初始化工作建议在onstart去做
    onStart();
//这里,引入了decorView,这个是所有视图的底层布局,类型为FrameLayout
    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 {
//windowManager将decorView加入了window窗口,这里真正涉及到windowManager对window的管理了(实际上是对view进行管理)
        mWindowManager.addView(mDecor, l);
        mShowing = true;

//这里真正的去show了,不在讨论的范围了。
        sendShowMessage();
    } finally {
    }
}
上面代码清晰了展现出了dialog show的过程。主要关注几个点:dispathOnCreate、mWindow.getDecorView()以及
mWindowManager.addView( mDecor, l);在本章节我们只简单分析dispathOnCreate。这样也会对mDecor的由来有个认知。至于mWindowManager.addView( mDecor, l)分析将会真正引出本文的主题。
2.3 dispatOnCreate
在上一小节中,dialog show的过程中,调用了dispatOnCreate方法,改方法如下:
void dispatchOnCreate(Bundle savedInstanceState) {
    if (!mCreated) {
        onCreate(savedInstanceState);
        mCreated = true;
    }
}
即调用了onCreate方法,查看dialog的onCreate方法可知,该方法是个空方法,但是建议在该方法中初始化视图相关如,调用setContentView:
/**
 * Similar to {@link Activity#onCreate}, you should initialize your dialog
 * in this method, including calling {@link #setContentView}.
 * @param savedInstanceState If this dialog is being reinitalized after a
 *     the hosting activity was previously shut down, holds the result from
 *     the most recent call to {@link #onSaveInstanceState}, or null if this
 *     is the first time.
 */
protected void onCreate(Bundle savedInstanceState) {
}
因此,这里可以参考子类的实现,子类的实现方式可参考下面代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
//设置视图
    setContentView(mView);
//初始化dialog的一些数据
    initDialog();
}
因此这里主要分析setContentView,这个实际上是dialog内部方法,实现为:
/**
 * Set the screen content to an explicit view.  This view is placed
 * directly into the screen's view hierarchy.  It can itself be a complex
 * view hierarchy.
 * 
 * @param view The desired content to display.
 */
public void setContentView(View view) {
//这里显然调用了mWindow进行视图设置,这里的mWindow实际上就是构造方法中的PhoneWindow对象。
//最终将contentView依附于window之上,但这个是用户自定义内容的视图。
    mWindow.setContentView(view);
}
//mWindow实际上PhoneWindow对象。因此setContentView的实现就是PhoneWindow中的setContentView实现
//setContentView最终会调用下面的代码
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
    // 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) {
//这里就是生成decorView,dialog show方法中的mWindow.getDecor方法获取的实际上就是在此时生成的。
//decorview实际上是android中最顶层的一个布局,其继承于FrameLayout,是所有view的viewGroup,所有的view都将包括于该ViewGroup中,
//包括刚刚我们setContentView中的展示的自定义view
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        view.setLayoutParams(params);
        final Scene newScene = new Scene(mContentParent, view);
        transitionTo(newScene);
    } else {
//从这里可以看出,我们刚刚设置的contentView会被设置到content区域,这个mContentParent实际上是通过下面代码获取的
//ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);从代码可以看出,这里获取了布局中的
//内容区域
        mContentParent.addView(view, params);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}
至此,我们了解到了整个view的生成过程,接下来就是真正引入主题,即将view添加至window中,并交给windowManager进行管理。
三、我们的主角window和windowManager
3.1 从addView说起
在对dialog实现源代码进行分析的时候,在show方法的最后重要的一步就是执行了mWindowManager. addView( mDecor, l);方法,so,就从这里开始。经过对addView的追溯发现,其实际上是ViewManager中定义的方法,定义如下所示:
public interface ViewManager
{
    /**
     * Assign the passed LayoutParams to the passed View and add the view to the window.
     * 

Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. *

Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a * secondary {@link Display} and the specified display can't be found * (see {@link android.app.Presentation}). * @param view The view to be added to this window. * @param params The LayoutParams to assign to view. */ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }

顾名思义,从上述代码中可以看出,该接口实际上提供了对view的增、删、改的工作,那么这为什么和window的管理连接到一起呢?实际上WindowManager继承了该接口,查看WindowManager的实现代码可知:
public interface WindowManager extends ViewManager
而Dialog在构造方法中获取的mWindow类型正是WindowManager,所以自然可以拥有对view的增、删、改的功能,同时也说明了,windowManager本质上是对View进行管理。但是WindowManager显然依然是个接口,其具体实现在哪儿?其实是在WindowManagerImpl中,进入其addView方法就可以明白相关实现。WindowManagerImpl中的addView方法:
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
//显然,这里全部委托给了mGlobal来进行实现
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}
该方法的实现很简单,重要的mGlobal的实现,跟踪发现mGlobal是一个WindowManagerGlobal类型的对象,其addView的实现方法为:
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    
//这里省略一些检查,如view是否为null等....
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//1、这里有个parentWindow判断,为什么?
    if (parentWindow != null) {
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        // If there's no parent, then hardware acceleration for this view is
        // set from the application's hardware acceleration setting.
        final Context context = view.getContext();
        if (context != null
                && (context.getApplicationInfo().flags
                        & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
            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);
                }
            }
        }

//2、这里产生了ViewRootImpl类型的root对象,我们终于见到前言中提到的这个角色了
//这个对象很重要,前言中说这个是view和window中的连接纽带,那么ViewRootImpl具体是干什么的?
        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);
//3、这里出现了多个add调用,那么这些层意又是什么?
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }

    // do this last because it fires off messages to start doing things
    try {
//显然,这里将传入的view通过root的setView完成了设置,上面提到root的类型是ViewRootImpl,
//因此很有必要探究下ViewRootImpl的setView实现。在这里真正完成了视图的展现。
        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;
    }
}
重要的代码注释我都已经标明,首先分析下注释中的几个问题:
(1)window实际上有三种不同的类型,分别是系统window、应用window以及子window,而且不同类型的window有层级的概念,类似于html中的z-index,因此我们能看到系统窗口可以覆盖到应用window之上等。Toast就是系统window,activity(activity是基窗口,其它所有的应用窗口都依赖于改窗口)、dialog就是普通的应用window(对于dialog而言子类不那么绝对,因为很多子类都可以修改其window类型),而popupWindow则是子window。
(2)ViewRootImpl这个是Window和view之间的纽带,这里暂不分析,下节分析
(3)对于这些容器可参见WindowManagerGlobal中的定义
//存储的是所有Window对应的View
private final ArrayList
  
  
   
    mViews = new ArrayList
   
   
    
    ();
//存储的是所有Window所对应的ViewRootImpl
private final ArrayList
    
    
     
      mRoots = new ArrayList
     
     
      
      ();
//存储所有window对应的布局参数
private final ArrayList
      
      
        mParams = new ArrayList 
       
         (); //存储所有被removeView方法中删除的view private final ArraySet 
        
          mDyingViews = new ArraySet 
         
           (); 
          
         
        
      
     
     
    
    
   
   
  
  
ViewRootImpl中的setView实现ViewRootImpl的setView方法,比较长,这里粘贴一些必要的代码:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
//省略部分代码....
requestLayout();
//省略部分代码....
//这里调用了mWindowSession的addToDisplay方法,完成真正的window添加
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
        getHostVisibility(), mDisplay.getDisplayId(),
        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
        mAttachInfo.mOutsets, mInputChannel);
//省略部分代码....
}}}
这里但最后会调用一个方法:requestLayout();该方法真正完成视图的异步刷新请求,起代码如下:
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
由此可知具体实现在scheduleTraversals,这里就是view的绘制入口,此处不再深入,主要看window的添加。从上面代码中可以看出window的添加是有mWindowSession来完成的,mWindowSession类型是IWindowSession,它是个Binder对象,真正的实现类是Session,Binder大家应该熟悉,是android提供的一种ipc通信机制,因此,window的添加过程实际上是一次ipc的调用。Sessin中的addToDisplay代码如下:
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
//这里调用了mService的addWindow方法,mService的类型是WindowManagerService,因此具体实现就再这个类中
//这里会根据传入的参数生成windstate,并对一些token进行处理,至此整个window完成了添加
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
至此,我们以Dialog为引子,完成的看到了整个window的添加流程。从这个流程可以看出,dialog(当然也包括其它视图)中的视图首先会以DecorView为根布局而存在,而decorView 依附于window上。在android中window的增、删、改,实际上都是基于view进行操作的,这也说明window是个相对虚拟的概念。window的添加完成了,或许会有很多人想知道window是怎么样被remove的呢?
3.2 从removeViewImmediate结束
或许读者会有疑问,为什么从removeViewImmediate结束?且等我慢慢道来。我们依然从dialog入手。在上章节中,我们知道dialog的show过程实际上就是window的添加过程,从dialog的show开始,到dialog消失之前显然没有任何机会看到window的remove过程,因为window显然一直存在。所以,所有的神秘应该隐藏dialog的dismiss方法中。dialog的dismiss方法中基本没有做什么事情,就是调用了dismissDialog,故直接看dismissDialog的代码:
void dismissDialog() {
    if (mDecor == null || !mShowing) {
        return;
    }

    if (mWindow.isDestroyed()) {
        Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
        return;
    }

    try {
//重点在这里,可以看到整个dismiss的过程就这里在remove了。
        mWindowManager.removeViewImmediate(mDecor);
    } finally {
        if (mActionMode != null) {
            mActionMode.finish();
        }
        mDecor = null;
        mWindow.closeAllPanels();
        onStop();
        mShowing = false;

        sendDismissMessage();
    }
}
代码中的mWindowManager我们已经很熟悉了,就是针对window进行管理的一个接口,其具体实现在WindowManagerImpl中:
public void removeViewImmediate(View view) {
    mGlobal.removeView(view, true);
}
不出所料,这里依然交给了mGlobal进行view管理,mGlobal是WindowManagerGlobal对象,其removeView实现如下:
public void removeView(View view, boolean immediate) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }

    synchronized (mLock) {
//待remove view的索引
        int index = findViewLocked(view, true);
//mRoots前文中介绍过,保存着每一个viewRootImpl对象
        View curView = mRoots.get(index).getView();
//重点是这里,真正对view进行了remove操作
        removeViewLocked(index, immediate);
        if (curView == view) {
            return;
        }

        throw new IllegalStateException("Calling with view " + view
                + " but the ViewAncestor is attached to " + curView);
    }
}
removeViewLocked的实现方法如下:
private void removeViewLocked(int index, boolean immediate) {
    ViewRootImpl root = mRoots.get(index);
    View view = root.getView();

    if (view != null) {
        InputMethodManager imm = InputMethodManager.getInstance();
        if (imm != null) {
            imm.windowDismissed(mViews.get(index).getWindowToken());
        }
    }
//重点在ViewRootImpl中的die方法中
    boolean deferred = root.die(immediate);
    if (view != null) {
        view.assignParent(null);
        if (deferred) {
//前文提到过,这个暂存remove过的view,就在这里,因为remove需要一个过程
//那么这里最后在哪儿删除?下文将有交代
            mDyingViews.add(view);
        }
    }
}

ViewRootImpl中的die方法实现如下:
boolean die(boolean immediate) {
    // Make sure we do execute immediately if we are in the middle of a traversal or the damage
    // done by dispatchDetachedFromWindow will cause havoc on return.
    if (immediate && !mIsInTraversal) {
        doDie();
        return false;
    }

    if (!mIsDrawing) {
        destroyHardwareRenderer();
    } else {
        Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
                "  window=" + this + ", title=" + mWindowAttributes.getTitle());
    }
    mHandler.sendEmptyMessage(MSG_DIE);
    return true;
}
为便于分析这里假设立即删除(即同步删除,实际上多数情况下都是异步删除,即发个MGS_DIE的消息即可返回了),直接看ViewRootImpl中的doDie()方法:
void doDie() {
    checkThread();
    if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
    synchronized (this) {
        if (mRemoved) {
            return;
        }
        mRemoved = true;
        if (mAdded) {
//这里是移除工作的重点,下文将进行分析
            dispatchDetachedFromWindow();
        }

        if (mAdded && !mFirst) {
//硬件渲染destroy
            destroyHardwareRenderer();

            if (mView != null) {
                int viewVisibility = mView.getVisibility();
                boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
                if (mWindowAttributesChanged || viewVisibilityChanged) {
                    // If layout params have been changed, first give them
                    // to the window manager to make sure it has the correct
                    // animation info.
                    try {
                        if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
                                & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                            mWindowSession.finishDrawing(mWindow);
                        }
                    } catch (RemoteException e) {
                    }
                }
//这个大家应该都清楚,视图渲染时需要的,这里释放内存占用内存
                mSurface.release();
            }
        }

        mAdded = false;
    }
//这里将解释上文中view到底什么时候被彻底remove
    WindowManagerGlobal.getInstance().doRemoveView(this);
}
在解析dispatchDetachedFromWindow之前,先看下doRemoveView方法,该方法在WindowManagerGlobal中,具体实现如下:
void doRemoveView(ViewRootImpl root) {
    synchronized (mLock) {
        final int index = mRoots.indexOf(root);
        if (index >= 0) {
//以下语句,正式完成了待删除的view的remove工作
            mRoots.remove(index);
            mParams.remove(index);
            final View view = mViews.remove(index);
            mDyingViews.remove(view);
        }
    }
    if (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) {
        doTrimForeground();
    }
}
最后看remove过程的重点,ViewRootImpl中的dispatchDetachedFromWindow实现如下:
void dispatchDetachedFromWindow() {
    if (mView != null && mView.mAttachInfo != null) {
        mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
//将视图从window中移除,此方法会回调onDetachedFromWindow方法,从该方法的说明来看,
//这个时候已经没有用于绘制的surface了,因此这里可以做些资源回收工作
        mView.dispatchDetachedFromWindow();
    }

    mAccessibilityInteractionConnectionManager.ensureNoConnection();
    mAccessibilityManager.removeAccessibilityStateChangeListener(
            mAccessibilityInteractionConnectionManager);
    mAccessibilityManager.removeHighTextContrastStateChangeListener(
            mHighContrastTextManager);
    removeSendWindowContentChangedCallback();

    destroyHardwareRenderer();

    setAccessibilityFocus(null, null);

    mView.assignParent(null);
    mView = null;
    mAttachInfo.mRootView = null;

    mSurface.release();

    if (mInputQueueCallback != null && mInputQueue != null) {
        mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
        mInputQueue.dispose();
        mInputQueueCallback = null;
        mInputQueue = null;
    }
    if (mInputEventReceiver != null) {
        mInputEventReceiver.dispose();
        mInputEventReceiver = null;
    }
    try {
//这里是重点,很显然这里真正的完成了我们window的remove工作,下面会分析。
       mWindowSession.remove(mWindow);
    } catch (RemoteException e) {
    }

    // Dispose the input channel after removing the window so the Window Manager
    // doesn't interpret the input channel being closed as an abnormal termination.
    if (mInputChannel != null) {
        mInputChannel.dispose();
        mInputChannel = null;
    }

    mDisplayManager.unregisterDisplayListener(mDisplayListener);

    unscheduleTraversals();
}
代码中最重要的部分便是mWindowSession.remove方法,mWindowSession在前文已经提到过。在view的添加addView的过程中,最终还是通过mWindowSession的addToDisplay方法来完成的,在这个方法中,addToDisplay调用了WindowManagerService中的addWindow方法完成了window的添加工作,这个过程是一个ipc的过程。同样的,mWindowSession.remove方法最后依然会调用WindowManagerService中的removeWindow方法,实现如下:
public void removeWindow(Session session, IWindow client) {
    synchronized(mWindowMap) {
//这里和addWindow时相对应,windowstate正是在那个时候生成的。
        WindowState win = windowForClientLocked(session, client, false);
        if (win == null) {
            return;
        }
//根据windowstate对window进行真正的移除操作
        removeWindowLocked(win);
    }
}
至此,window的remove正式完成。
四、总结
本文以dialog的展示和消失为切入点,分析了android中window和windowManager的实现机制,这个过程的细节一定是比较复杂的,但是我们只关心这条主线,从而对window的添加和删除过程有个清晰的过程。基于dialog的分析实现,同样适用于activity、toast中window的实现,虽有不同,但大同小异。

最后,本文的文字纯手动打字,如若出现错别字还请谅解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值