Android 解析WindowManger

作者: 巴菲伟 | 来源:发表于2022-05-24 19:57 被阅读0次

仓库地址:http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/app/ActivityThread.java

一、Window的创建以及相关联类

image.png
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    .......
    Activity activity = null;
    try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }
      ......
      if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
                //Actvity和Window绑定在一起
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
        ........
      }
      .........
}
  • frameworks/base/core/java/android/app/Activity.java
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*/);
        // 创建Window的实例
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        //Activity和Window绑定在一起后,然后可以收到key的各种点击事件
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
}
  • frameworks/base/core/java/android/view/Window.java
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) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    // 创建WindowManagerImpl对象
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
  • frameworks/base/core/java/android/view/WindowManagerImpl
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
       return new WindowManagerImpl(mContext, parentWindow);
   }

二、Window的属性介绍

1、介绍Window常见属性

Window的属性有很多种,与应用开发最密切的有三种,它们分别是Type(Window的类型)、Flag(Window的标志)和SoftInputMode(软键盘相关模式),下面分别介绍这三种Window的属性。

2、Window的类型

Application Window(应用程序窗口)、Sub Windwow(子窗口)、System Window(系统窗口)三种类型,每种窗口又包含了很多种类型,它们都定义在WindowManager的静态内部类LayoutParams中,接下来我们分别对这三大类进行讲解。

  • 应用程序窗口

Activity就是一个典型的应用程序窗口,应用程序窗口的Type值范围为1到99。
frameworks/base/core/java/android/view/WindowManager.java

public static final int FIRST_APPLICATION_WINDOW = 1;
public static final int TYPE_BASE_APPLICATION   = 1;//窗口的基础值,其他的窗口值要大于这个值
public static final int TYPE_APPLICATION        = 2;//普通的应用程序窗口类型
public static final int TYPE_APPLICATION_STARTING = 3;//应用程序启动窗口类型,用于系统在应用程序窗口启动前显示的窗口。
public static final int TYPE_DRAWN_APPLICATION = 4;
public static final int LAST_APPLICATION_WINDOW = 99;
  • 子窗口

它不能独立的存在,需要附着在其他窗口才可以,子窗口的Type值范围为1000到1999。
frameworks/base/core/java/android/view/WindowManager.java

public static final int FIRST_SUB_WINDOW = 1000;//子窗口类型初始值
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4; 
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
public static final int LAST_SUB_WINDOW = 1999;//子窗口类型结束值

  • 系统窗口

Toast、输入法窗口、系统音量条窗口、系统错误窗口都属于系统窗口,系统窗口的Type值范围为2000到2999。
frameworks/base/core/java/android/view/WindowManager.java

public static final int FIRST_SYSTEM_WINDOW     = 2000;//系统窗口类型初始值
public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;//系统状态栏窗口
public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;//搜索条窗口
public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;//通话窗口
public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;//系统ALERT窗口
public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;//锁屏窗口
public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;//TOAST窗口
...

public static final int LAST_SYSTEM_WINDOW      = 2999;//系统窗口类型结束值

3、Window的标志

Window的标志也就是Flag,用于控制Window的显示,同样被定义在WindowManager的内部类LayoutParams中,一共有20多个,这里我们给出几个比较常用。


image.png
  • 第一种是通过Window的addFlags方法
Window mWindow =getWindow(); 
mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
  • 第二种通过Window的setFlags方法
Window mWindow =getWindow();            
mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
,WindowManager.LayoutParams.FLAG_FULLSCREEN);
  • 第三种则是给LayoutParams设置Flag,并通过WindowManager的addView方法进行添加
WindowManager.LayoutParams mWindowLayoutParams =
              new WindowManager.LayoutParams();
      mWindowLayoutParams.flags=WindowManager.LayoutParams.FLAG_FULLSCREEN;
      WindowManager mWindowManager =(WindowManager) getSystemService(Context.WINDOW_SERVICE);  
      TextView mTextView=new TextView(this);
      mWindowManager.addView(mTextView,mWindowLayoutParams);

4、软键盘相关模式

WindowManager的静态内部类LayoutParams中定义了软键盘相关模式,这里给出常用的几个:


image.png

从上面给出的SoftInputMode ,可以发现,它们与AndroidManifest中Activity的属性android:windowSoftInputMode是对应的。因此,除了在AndroidMainfest中为Activity设置android:windowSoftInputMode以外还可以在Java代码中为Window设置SoftInputMode:

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

三、系统添加View的流程

  • 系统添加view的流程


    image.png

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java

public void add(View statusBarView, int barHeight) {
    mLp = new WindowManager.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            barHeight,
            WindowManager.LayoutParams.TYPE_STATUS_BAR,//1
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
            PixelFormat.TRANSLUCENT);
    mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
    mLp.gravity = Gravity.TOP;
    mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
    mLp.setTitle("StatusBar");
    mLp.packageName = mContext.getPackageName();
    mStatusBarView = statusBarView;
    mBarHeight = barHeight;
    //添加当前系统view到WindowManagerImpl中
    mWindowManager.addView(mStatusBarView, mLp);//2
    mLpChanged = new WindowManager.LayoutParams();
    mLpChanged.copyFrom(mLp);
}

frameworks/base/core/java/android/WindowManagerImpl.java

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

frameworks/base/core/java/android/view/WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params,
          Display display, Window parentWindow) {
    ...//参数检查
      final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
      if (parentWindow != null) {
          //如果当前窗口要作为子窗口,就会根据父窗口对子窗口的WindowManager.LayoutParams类型的wparams对象进行相应调整
          parentWindow.adjustLayoutParamsForSubWindow(wparams);
      } else {
      ...
      }

      ViewRootImpl root;
      View panelParentView = null;
       ...
          //创建ViewRootImpl对象
          root = new ViewRootImpl(view.getContext(), display);
          view.setLayoutParams(wparams);
          //mViews存储窗口的view对象
          mViews.add(view);
          //mRoots存储ViewRootImpl所有元素
          mRoots.add(root);
          //WindowManager.LayoutParams类型的wparams对象
          mParams.add(wparams);
      }
      try {
          root.setView(view, wparams, panelParentView);
      } catch (RuntimeException e) {
         ...
      }
  }

frameworks/base/core/java/android/view/ViewRootImpl.java

  • ViewRootImpl身负了很多职责:
  • View树的根并管理View树
  • 触发View的测量、布局和绘制
  • 输入事件的中转站
  • 管理Surface
  • 负责与WMS进行进程间通信
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
       synchronized (this) {
          ...
               try {
                   mOrigWindowType = mWindowAttributes.type;
                   mAttachInfo.mRecomputeGlobalAttributes = true;
                   collectViewAttributes();
                  //mWindowSession是IWindowSession类型的,它是一个Binder对象,用于进行进程间通信,IWindowSession是Client端的代理,
                  //它的Server端的实现为Session
                   res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                           getHostVisibility(), mDisplay.getDisplayId(),
                           mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                           mAttachInfo.mOutsets, mInputChannel);
               } 
               ...
   }

frameworks/base/services/core/java/com/android/server/wm/Session.java

addToDisplay方法中会调用了WMS的addWindow方法,并将自身也就是Session,作为参数传了进去,每个应用程序进程都会对应一个Session,WMS会用ArrayList来保存这些Session。这样剩下的工作就交给WMS来处理,在WMS中会为这个添加的窗口分配Surface,并确定窗口显示次序,可见负责显示界面的是画布Surface,而不是窗口本身。WMS会将它所管理的Surface交由SurfaceFlinger处理,SurfaceFlinger会将这些Surface混合并绘制到屏幕上,后续会接着分析WMS的addWindow方法

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

相关文章

网友评论

    本文标题:Android 解析WindowManger

    本文链接:https://www.haomeiwen.com/subject/srmtprtx.html