WindowManagerService添加窗口流程简析---应用窗口的创建

本文详细解析了Android系统中应用窗口的创建流程,包括Activity如何创建Window并与WindowManager绑定,如何给Window添加View,以及Activity添加窗口Window的具体过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    Android系统中的窗体是屏幕上的一块用于绘制各种UI元素并能够响应应用户输入的一个矩形区域。从原理上来讲,窗体的概念是独自占有一个Surface实例的显示区域。比如Dialog、Activity的界面、壁纸、状态栏以及Toast等都是窗体。 


 一、Android窗口类型

  在WindowManager类的内部类LayoutParams中定义了三种窗口类型(Application windows,Sub-windows和System windows),所以Android系统中窗口分为三大类型:

  • 应用窗口:该窗口对应一个Activity,因此,要创建应用窗口就必须在Activity中完成了。
  • 子窗口:必须依附在某个父窗口之上。
  • 系统窗口:由系统进程创建,不依赖于任何应用或者不依附在任何父窗口之上。

    

二、Activity应用窗口的创

    

    1、Activity创建Window并与WindowManager进行绑定

    在Activity启动过程中会调用ActivityThread的performLaunchActivity方法,performLaunchActivity方法中又会调用Activity的attach方法。下面从Activity的attach方法开始入手,如下所示:

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) {

        .......
       //创建PhoneWindow,Window的实现类 ,一个Activity对应着一个Window也就是应用窗口。
        mWindow = new PhoneWindow(this, window, activityConfigCallback); 
        ......
       //调用PhoneWindow的setWindowManager方法,
       mWindow.setWindowManager((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(); //WindowManagerImpl对象
        ......
   }

    调用了PhoneWindow的setWindowManager方法,这个方法的具体的实现在PhoneWindow的父类Window中。

public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {

            //mContext.getSystemService(Context.WINDOW_SERVICE);返回的是WindowManagerImpl对象
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

         }

        //创建WindowManagerImpl并将window和WindowManagerImpl关联
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

    经过上面的setWindowManager方法,Window就和WindowManagerImpl进行了关联,这样WindowManagerImpl就持有了Window的引用,就可以对Window进行操作。

    2、Activity给Window添加View

    每个Activity都会调用setContextView方法来加载布局视图,而这其实就是视图View添加到Activity窗口上的一个过程。加载布局视图代码如下Activity#setContentView:

public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID); //getWindow()返回Activity的成员变量mWindow即PhoneWindow
        initWindowDecorActionBar(); //初始化ActionBarView
    }

2.1 查看PhoneWindow的SetContentView方法

public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();  //1----创建顶层视图DecorView,整个应用窗口的根View
        } 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);  //加载xml布局视图内容到视图容器mContentParent中
        }
    }

2.1.1 PhoneWindow的installDecor()的具体内容:

 private void installDecor() {
        if (mDecor == null) {  
            mDecor = generateDecor(-1); //1---生成DecorView对象,DecorView继承自FrameLayout
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }

        //2---mContentParent是id为android.R.id.content的FrameLayout,setContentView就是将布局设置在这里
        if (mContentParent == null) {  
            mContentParent = generateLayout(mDecor);
            .......
         } else {  //设置title
               mTitleView = findViewById(R.id.title);
                if (mTitleView != null) {
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        final View titleContainer = findViewById(R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        mContentParent.setForeground(null);
                   } else {
                        mTitleView.setText(mTitle);
                    }
                }
           }
}

    注释1中generateDecor方法通过new DecorView创建对象,在DecorView的构造方法中,传入了PhoneWindow对象,即将PhoneWindow和DecorView进行了关联。下面看下注释2中generateLayout主要内容:

2.1.2 PhoneWindow的generateLayout(mDecor)

protected ViewGroup generateLayout(DecorView decor) {
  /*应用当前主题数据,加载的是 
    *TypedArray  mWindowStyle = mContext.obtainStyledAttributes(com.android.internal.R.styleable.Window);
    */
  TypedArray a = getWindowStyle();
  ......
  //加载DecorView布局,这些xml文件位于frameworks/base/core/res/res/layout下
  int layoutResource;
  int features = getLocalFeatures();
  if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
    if (mIsFloating) {
      TypedValue res = new TypedValue();
      getContext().getTheme().resolveAttribute(com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
      layoutResource = res.resourceId;
    } else {
      layoutResource = com.android.internal.R.layout.screen_title_icons;
  }
  removeFeature(FEATURE_ACTION_BAR);
  } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
  layoutResource = com.android.internal.R.layout.screen_progress;
  ......
  
  mDecor.startChanging();
  //inflate View,并添加到decorView中
  View in = mLayoutInflater.inflate(layoutResource, null);
  decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
  //id="@android:id/content"的View赋值给mContentParent
  ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  if (contentParent == null) {
    throw new RuntimeException("Window couldn't find content container view");
  }

2.2 initWindowDecorActionBar(); 

private void initWindowDecorActionBar() {
       Window window = getWindow();
        window.getDecorView();  //若DecorView为null,则创建DecorView
        if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
            return;
        }

        mActionBar = new WindowDecorActionBar(this); //创建ActionBar
        mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);

        mWindow.setDefaultIcon(mActivityInfo.getIconResource());
        mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
    }

三、Activity添加窗口Window的过程

    在setContentView中只是将需要添加的View的结构添加到DecorView中,此时DecorView并无显示内容,View还未进行绘制,所以在onCreate方法中是无法获取View的宽高等信息。

    在Activity创建完成之后,Ams利用Bindler进程间的通信机制通知客户端调用ActivityThread类中的handleResumeActivity方法来启动Activity。代码如下:

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        ........
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;
            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) { //参数设置
                r.window = r.activity.getWindow();//PhoneWindow
                View decor = r.window.getDecorView(); //窗口顶层视图DecorView
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager(); //Activity窗口的窗口管理器对象WindowManager
                WindowManager.LayoutParams l = r.window.getAttributes();//窗口参数

                a.mDecor = decor;

                //窗口的类型为基础应用窗口
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                //窗口的软输入法模式
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    //标记客户端Activity添加窗口成功
                    a.mWindowAdded = true;
                    //添加窗口
                    wm.addView(decor, l);
                }
    ........
        }
    }

    在这个过程中最主要的操作就是调用了WindowManager.addView方法,而WindowManager是个接口类,继承自ViewManager。ViewManager源码如下:

public interface ViewManager{

    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

    WindowManager继承自ViewManager,WindowManager窗口管理其实就是对窗口的视图View进行管理。WindowManager 源码如下:

public interface WindowManager extends ViewManager {
    ........
     public Display getDefaultDisplay();
     public void removeViewImmediate(View view);
     public static class LayoutParams extends ViewGroup.LayoutParams
            implements Parcelable {
    ........
    }
}

    WindowManager 的实现类WindowManagerImpl,其源码如下:

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

    private IBinder mDefaultToken;

    public WindowManagerImpl(Display display) {
        this(display, null);
    }

    private WindowManagerImpl(Display display, Window parentWindow) {
        mDisplay = display;
        mParentWindow = parentWindow;
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mDisplay, parentWindow);
    }

   @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);  //如果params.token为null,则token设置为系统默认的
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }
   @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

    @Override
    public Display getDefaultDisplay() {
        return mDisplay;
    }
}

    从源码中可以看出,WindowManagerImpl中并没有做实质性的操作,而是将具体操作全部交给了WindowManagerGlobal去处理,这里到了设计模式中的桥接模式,可以看出WindowManagerGlobal是一个单例,说明在一个进程中只有一个WindowManagerGlobal实例。下面查看WindowManagerGlobal的addView()方法。

public final class WindowManagerGlobal {
    private final ArrayList<View> mViews = new ArrayList<View>(); //保存当前应用所有窗口的DecorView
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); //保存当前应用所有ViewRootImpl
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>(); //保存当前应用所有窗口的布局参数
    ........
    //单例模式构造方法
    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

//添加view
 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
     ......
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
           parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // 如果没有父级,那么此视图的硬件加速从应用程序的硬件加速设置中设置。
           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) {
            // 监听系统属性变更
            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)) { //如果该view处于正在删除中,则直接删除
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else { //不能重复添加同一个view对象
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // 如果是子Window,则根据token获取对应的父窗口Window
            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); //每一个窗口对应一个ViewRootImpl

            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);  //ViewRootImpl 完成绘制view操作
           } catch (RuntimeException e) {
               // 出现异常,则清除该View
               if (index >= 0) {
                   removeViewLocked(index, true);
                }
                throw e;
           }
        }
   }
//主要更新当前窗口的参数LayoutParams
 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false); //绘制操作由ViewRootImpl完成
        }
    }
    //从三个数组里面分别移除DecorView对象,ViewRootIpl对象,WindowManager.LayoutParams对象
    public void removeView(View view, boolean immediate) { 
        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }
        }
    }
    在WindowManagerGlobal的方法中最终调用ViewRootImpl的setView来完成View的添加和绘制,ViewRootImpl是一个视图层次结构的顶部,它实现了View与WindowManager之间所需要的协议,作为WindowManagerGlobal中大部分的内部实现。这个好理解,在WindowManagerGlobal中实现方法中,都可以见到ViewRootImpl,也就说WindowManagerGlobal方法最后还是调用到了ViewRootImpl。addView,removeView,update调用顺序

WindowManagerImpl -> WindowManagerGlobal -> ViewRootImpl 

    下面查看ViewRootImpl的setView的主要内容:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                ...
                // 在添加到WindowManager中之前,先进行绘制view操作,绘制完毕后才能接受事件
                requestLayout();
                ......
                try {
                ......
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);  
                } 
    }

requestlayout()进行布局的绘制,绘制结束后调用mWindowSession.addToDisplay添加和显示view。mWindowSession是Session对象,继承自IWindowSession.Stub(是一个Binder对象),也就是说这其实是一次IPC过程,远程调用了Session中的addToDisPlay方法。下面查看下Session中的addToDisPlay方法:

@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);
    }

    其中mService就是WindowManagerService,也就是说Window的添加请求,最终是通过WindowManagerService来添加的。下面查看下WindowManagerService的具体方法。

四、WindowManagerService的addWindow方法
public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp); //检查权限,系统窗口需要对应的系统权限
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }

        boolean reportNewConfig = false;
        WindowState parentWindow = null;
        long origId;
        final int callingUid = Binder.getCallingUid();
        final int type = attrs.type; //窗体的类型

        synchronized(mWindowMap) {
            if (!mDisplayReady) {
                throw new IllegalStateException("Display has not been initialialized");
            }
	    //通过displayId參数指定加入到哪一个DisplayContent,默认是DEFAULT_DISPLAY手机屏幕
            final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
            if (displayContent == null) {
                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                        + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            if (!displayContent.hasAccess(session.mUid)
                    && !mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) {
                Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
                        + "does not have access: " + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            if (mWindowMap.containsKey(client.asBinder())) { //判断是否重复添加Window
                Slog.w(TAG_WM, "Window " + client + " is already added");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }

	    //子窗口必须依附于父窗口,对于子窗体来说,attrs.token表示了父窗体
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                parentWindow = windowForClientLocked(null, attrs.token, false);
                if (parentWindow == null) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) { //窗体嵌套只能是两层,子窗口是不能在添加窗口的
                    Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }

            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
                Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display.  Aborting.");
                return WindowManagerGlobal.ADD_PERMISSION_DENIED;
            }

            AppWindowToken atoken = null; //应用窗口token
            final boolean hasParent = parentWindow != null;
            // 子窗口使用父窗口策略
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            // If this is a child window, we want to apply the same type checking rules as the
            // parent window type.
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;

            boolean addToastWindowRequiresToken = false;

            if (token == null) { //token异常判断
               ......
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow); //创建系统WindowToken
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { //应用窗口类型
                atoken = token.asAppWindowToken();
                ......
            } else if (rootType == TYPE_INPUT_METHOD) {
                if (token.windowType != TYPE_INPUT_METHOD) {
                    Slog.w(TAG_WM, "Attempted to add input method window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_VOICE_INTERACTION) {
                if (token.windowType != TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_WALLPAPER) {
                if (token.windowType != TYPE_WALLPAPER) {
                    Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_DREAM) {
                if (token.windowType != TYPE_DREAM) {
                    Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
                if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
                    Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_TOAST) {
                // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
                addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
                        callingUid, parentWindow);
                if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
                    Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_QS_DIALOG) {
                if (token.windowType != TYPE_QS_DIALOG) {
                    Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (token.asAppWindowToken() != null) {
                Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
                // It is not valid to use an app token with other system types; we will
                // instead make a new token for it (as if null had been passed in for the token).
                attrs.token = null;
                token = new WindowToken(this, client.asBinder(), type, false, displayContent,
                        session.mCanAddInternalSystemWindow);//创建系统窗口类型的token
            }
	    //WMS为要加入的窗体创建了一个WindowState对象,这个对象维护了一个窗体的全部状态信息
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            if (win.mDeathRecipient == null) {
                // Client has apparently died, so there is no reason to
                // continue.
                Slog.w(TAG_WM, "Adding window client " + client.asBinder()
                        + " that is dead, aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

            if (win.getDisplayContent() == null) {
                Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

	    //WindowManagerPolicy(实现类PhoneWindowManager),这个函数的调用会调整LayoutParams的一些成员的取值
            mPolicy.adjustWindowParamsLw(win.mAttrs); 
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }

            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
	    ......

            win.attach();
	    // 然后将WindowState对象加入到mWindowMap中
            mWindowMap.put(client.asBinder(), win);
            if (win.mAppOp != AppOpsManager.OP_NONE) {
                int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
                        win.getOwningPackage());
                if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
                        (startOpResult != AppOpsManager.MODE_DEFAULT)) {
                    win.setAppOpVisibilityLw(false);
                }
            }

            final AppWindowToken aToken = token.asAppWindowToken(); //应用窗口windowToken
            if (type == TYPE_APPLICATION_STARTING && aToken != null) {
                aToken.startingWindow = win;
                if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
                        + " startingWindow=" + win);
            }

            boolean imMayMove = true;

            win.mToken.addWindow(win);
            if (type == TYPE_INPUT_METHOD) {
                win.mGivenInsetsPending = true;
                setInputMethodWindowLocked(win);
                imMayMove = false;
            } else if (type == TYPE_INPUT_METHOD_DIALOG) {
                displayContent.computeImeTarget(true /* updateImeTarget */);
                imMayMove = false;
            } else {
                if (type == TYPE_WALLPAPER) {
                    displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
                    // If there is currently a wallpaper being shown, and
                    // the base layer of the new window is below the current
                    // layer of the target window, then adjust the wallpaper.
                    // This is to avoid a new window being placed between the
                    // wallpaper and its target.
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                }
            }

            // If the window is being added to a stack that's currently adjusted for IME,
            // make sure to apply the same adjust to this new window.
            win.applyAdjustForImeIfNeeded();

            if (type == TYPE_DOCK_DIVIDER) {
                mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win);
            }

            final WindowStateAnimator winAnimator = win.mWinAnimator;
            winAnimator.mEnterAnimationPending = true;
            winAnimator.mEnteringAnimation = true;
            // Check if we need to prepare a transition for replacing window first.
            if (atoken != null && atoken.isVisible()
                    && !prepareWindowReplacementTransition(atoken)) {
                // If not, check if need to set up a dummy transition during display freeze
                // so that the unfreeze wait for the apps to draw. This might be needed if
                // the app is relaunching.
                prepareNoneTransitionForRelaunching(atoken);
            }

            if (displayContent.isDefaultDisplay) {
                final DisplayInfo displayInfo = displayContent.getDisplayInfo();
                final Rect taskBounds;
                if (atoken != null && atoken.getTask() != null) {
                    taskBounds = mTmpRect;
                    atoken.getTask().getBounds(mTmpRect);
                } else {
                    taskBounds = null;
                }
                if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayInfo.rotation,
                        displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,
                        outStableInsets, outOutsets)) {
                    res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
                }
            } else {
                outContentInsets.setEmpty();
                outStableInsets.setEmpty();
            }

            if (mInTouchMode) {
                res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
            }
            if (win.mAppToken == null || !win.mAppToken.isClientHidden()) { //设置应用窗体可见
                res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
            }

            mInputMonitor.setUpdateInputWindowsNeededLw(); //以下和输入法窗体有关

            boolean focusChanged = false;
            if (win.canReceiveKeys()) {
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }

            if (imMayMove) {
                displayContent.computeImeTarget(true /* updateImeTarget */);
            }

            // Don't do layout here, the window must call
            // relayout to be displayed, so we'll do it there.
            displayContent.assignWindowLayers(false /* setLayoutNeeded */);

            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);

            if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
                    + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));

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

        if (reportNewConfig) {
            sendNewConfiguration(displayId);
        }

        Binder.restoreCallingIdentity(origId);

        return res;
    }

总结:以上是添加应用窗口的简析,从代码中可以看到还是有点复杂的,这里只是简单介绍添加流程,希望有所帮助。








<think>我们正在讨论如何通过添加日志来调试和理解Android窗口添加流程。根据引用内容,我们可以使用Log类在代码中插入日志消息,然后通过Logcat查看这些日志。以下是一个分步指南: 1. **确定关键代码位置**: 在Android窗口管理相关的代码中,通常需要关注以下类和方法: - `WindowManagerService` (系统服务,管理所有窗口) - `Activity` 的 `onCreate` 方法中调用 `setContentView` 或 `getWindowManager().addView` - `View` 的 `onAttachedToWindow` 和 `onDetachedFromWindow` 方法 - 自定义 `View` 或 `Window` 的添加过程 2. **添加日志语句**: 使用 `Log` 类在关键位置添加日志。例如: ```java import android.util.Log; public class MyActivity extends Activity { private static final String TAG = "WindowDebug"; // 自定义TAG便于过滤 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "MyActivity onCreate: starting window addition"); // [^3] setContentView(R.layout.main); // 其他代码 } } // 在自定义View中 public class CustomView extends View { public CustomView(Context context) { super(context); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); Log.d(TAG, "CustomView attached to window"); // [^3] } } ``` 3. **使用Logcat查看日志**: - 连接设备并确保开启USB调试 - 在Android Studio中打开Logcat,或使用命令行 `adb logcat` - 根据设置的TAG过滤日志,例如在Android Studio的Logcat过滤框中输入 `WindowDebug`,或使用命令行: ```bash adb logcat -s WindowDebug:D ``` 4. **添加更详细的日志**: 为了理解窗口添加的整个流程,可以在以下关键点添加日志: - 在 `WindowManager.addView()` 方法调用前后 - 在 `ViewRootImpl` 的相关方法中(如 `setView`) - 在 `WindowManagerService` 的相关方法中(需要系统权限,通常只适用于系统应用或ROM开发者) 例如,在 `WindowManagerService` 中(需要系统源码环境): ```java // 在 frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java public int addWindow(...) { if (DEBUG) Slog.d(TAG_WM, "Adding window " + window + " at " + window.mAttrs.getTitle()); // ... } ``` 5. **日志级别选择**: - 使用 `Log.v()` 输出最详细的信息(Verbose) - 使用 `Log.d()` 输出调试信息(Debug) - 使用 `Log.i()` 输出重要流程信息(Info) 6. **注意事项**: - 避免在性能敏感路径上添加过多日志,以免影响性能 - 对于系统级代码的修改,需要重新编译系统并刷机(仅适用于系统开发者) - 应用层开发只需在应用代码中添加日志 7. **高级技巧**: - 使用 `adb logcat -v threadtime` 查看线程和时间信息 - 结合 `grep` 进行多条件过滤,例如:`adb logcat | grep -E "WindowDebug|Choreographer"` - 保存日志到文件:`adb logcat -d > log.txt` 通过以上步骤,你可以清晰地跟踪窗口添加流程,包括窗口创建、视图附加、布局和绘制等阶段。 §§相关问题§§ 1. 如何过滤包含特定关键字的 adb logcat 输出? 2. 如何在Android系统源码中添加日志并重新编译? 3. 如何通过日志分析窗口添加过程中的性能问题? 4. 除了Logcat,还有哪些工具可以调试Android窗口系统?</think>### 通过添加日志调试Android窗口添加流程指南 在Android系统中调试窗口添加流程(如`WindowManager.addView()`)时,可通过以下步骤添加日志: #### 一、关键位置添加日志 1. **窗口创建入口** 在调用`WindowManager.addView()`前后添加日志: ```java // 示例:Activity或Service中 public void addCustomWindow() { Log.d("WINDOW_DEBUG", "开始添加窗口 | 线程:" + Thread.currentThread().getName()); // [^3] View view = LayoutInflater.from(context).inflate(R.layout.custom_window, null); WindowManager.LayoutParams params = new WindowManager.LayoutParams(...); windowManager.addView(view, params); // 核心调用点 Log.i("WINDOW_DEBUG", "窗口添加完成 | 视图ID: " + view.getId()); // [^3] } ``` 2. **窗口生命周期回调** 在自定义View中覆盖关键方法: ```java public class CustomView extends View { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); Log.w("WINDOW_DEBUG", "视图已附加到窗口 | 哈希: " + this.hashCode()); // [^3] } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); Log.e("WINDOW_DEBUG", "视图从窗口分离"); // [^3] } } ``` 3. **系统服务层跟踪** 对于系统源码开发者,在`WindowManagerService`中添加: ```java // frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java public int addWindow(...) { Slog.d("WMS_DEBUG", "添加窗口: " + window + " | 类型: " + attrs.type); // [^1] // ...系统代码 } ``` #### 二、日志过滤与分析 1. **抓取专用TAG日志** ```bash adb logcat -s WINDOW_DEBUG:W WMS_DEBUG:D # 同时抓取应用层和系统层日志 ``` 2. **关键流程标记** 在日志中添加流程阶段标识: ```java Log.d("WINDOW_FLOW", "阶段1/5: 参数校验完成"); Log.d("WINDOW_FLOW", "阶段2/5: 视图测量完成 | 尺寸: " + view.getMeasuredWidth() + "x" + view.getMeasuredHeight()); ``` 3. **异常捕获** 在`addView()`外围添加异常日志: ```java try { windowManager.addView(view, params); } catch (WindowManager.BadTokenException e) { Log.e("WINDOW_CRASH", "无效的WindowToken | 原因: " + e.getMessage()); } ``` #### 三、高级调试技巧 1. **添加窗口操作溯源** 在日志中输出调用栈: ```java Log.d("WINDOW_STACK", "调用路径:", new Throwable()); ``` 2. **性能监控** 记录关键操作耗时: ```java long startTime = SystemClock.uptimeMillis(); windowManager.addView(view, params); Log.i("PERF_WINDOW", "添加耗时: " + (SystemClock.uptimeMillis() - startTime) + "ms"); ``` 3. **结合Toast实时提示** 在关键节点添加Toast提示(仅调试版): ```java Toast.makeText(context, "窗口参数校验通过", Toast.LENGTH_SHORT).show(); // [^2] ``` #### 四、注意事项 1. 使用不同日志级别: - `Verbose`:详细流程节点 - `Debug`:关键参数值 - `Error`:异常捕获 2. 正式发布前通过`BuildConfig.DEBUG`移除调试日志 3. 系统级日志需要修改AOSP源码并重新编译ROM > 示例日志输出: > `D/WINDOW_DEBUG: 开始添加窗口 | 线程:main` > `I/WINDOW_FLOW: 阶段2/5: 视图测量完成 | 尺寸:1080x2200` > `E/WINDOW_CRASH: 无效的WindowToken | 原因:token null is not valid`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值