android窗口的创建、显示、管理

本文深入探讨了Android窗口的创建与显示,重点解析了DecorView作为窗口顶级视图的角色以及measure过程。measure函数用于计算View树的大小,设置每个View的布局尺寸。在ViewGroup中,通过重载onMeasure函数来递归测量子View,直至所有子View完成测量。MeasureSpec的三种模式——UNSPECIFIED、EXACTLY和AT_MOST,分别对应不同的尺寸约束。视图的LayoutParams决定了MeasureSpec的模式和尺寸大小。
摘要由CSDN通过智能技术生成


图1 打开一个android手机应用界面

把一个android手机拿在手里,点开Google play,滑动显示侧边栏,点击能打开新界面,这是我们十分熟悉的简单操作。
但如果要问,手机为什么会显示这样的画面?点击或滑动为什么会有相应效果?恐怕就不那么简单了。
随便想一想,这也涉及到了屏幕、内存、cpu、gpu、framework层的管理,app层的管理等方面。
这里我们重点关注窗口的管理、绘制以及对手势事件的处理,底层渲染和输入监听以及屏幕硬件等部分略过。

1.窗口创建


图1  Activity窗口及其子窗口、壁纸窗口、输入法窗口和状态栏的位置结构

android的窗口有不同的类型,可分为:
Application Window:  应用程序的窗口,如activity的窗口。
System Window:系统窗口,如状态栏、壁纸、输入法等。
Sub Window: 子窗口,如对话框、Menu菜单等。

由于窗口不仅涉及到应用内的界面切换,还涉及到app间的切换管理。所以,android设计的窗口管理分为前台应用进程的 客户端和后台全局的 服务端,客户端和服务端通过Binder机制实现高效的IPC通信。

图2 android窗口管理示意图


(1)客户端进程是如何管理窗口的?

我们首先分析一下 窗口都有哪些组成部分。打开eclipse,利用工具Hierachy View,可以看到类似图3的ViewTree.

图3 android基本的decorview viewtree(带actionbar的viewtree类似)

这里可以清晰的看到,我们在activity里setContentView的layout并不是界面全部,而只是id/content的FrameLayout的子视图。那一个窗口还包括哪些呢?请看下图:

图4 窗口的基本结构

PhoneWindow:Android中的最基本的窗口系统,每个activity都会创建一个PhoneWindow,是Activity和整个View系统交互的接口。
DecorView:每个PhoneWindow包含一个DecorView,DecorView继承自FrameLayout,是当前Activity所有View的祖先,管理system layout和contentView,activity中的findViewById就是通过decorview查找的。它还是PhoneWindow与ViewRoot之间的桥梁,负责分发事件、设置窗口属性等。

System Layout:DecorView有一个直接的子View,我们称之为System Layout,这个View是从系统的Layout.xml中解析出的,它包含当前UI的风格,如是否带title、是否带process bar等。我们将这个System Layout添加到DecorView中,目前android提供了8种预设风格的System Layout。(如图5)

图5 sytem layout与decorview关系

Content Parent:这个ViewGroup才是我们设置的ContentView的父视图,对应的是System Layout中的id为”content”的一个FrameLayout。
Activity Layout:这才是我们设置的Activity组件的UI-ContentView。

摸清楚了窗口的结构,我们再来看看 他们是怎么组织起来的

首先, Activity是怎么创建Window的呢?我们知道每个Activity组件都关联了一个Window,但不是所有的窗口都会有Window类,如输入法窗口就没有Window类修饰视图。Window是一个具有交互功能视图的抽象,PhoneWindow是其的唯一实现,表示一个应用窗口,持有一个DecorView。PhoneWindow对象是从Activity类的成员函数attach中创建的。

图6 activity创建window的过程

attach函数是个重要的函数!ActivityThread创建了activity和ContextImpl之后,就调用activity的attach函数进行真正的初始化。我们具体看下这个函数的有关Window的内容:

  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) {
          attachBaseContext(context);
           mWindow = PolicyManager.makeNewWindow( this);
          mWindow.setCallback( this);
          ...
          if (info. softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED ) {
            mWindow.setSoftInputMode(info. softInputMode);
        }
          ...
           mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE ),
                mToken, mComponent.flattenToString(),
                (info. flags & ActivityInfo. FLAG_HARDWARE_ACCELERATED ) != 0);
          ...
          mWindowManager = mWindow .getWindowManager();
     }

函数首先调用PolicyManager类的静态成员函数makeNewWindow来创建一个类型为PhoneWindow的应用程序窗口,并且保存在Activity类的成员变量mWindow中。有了这个类型为PhoneWindow的应用程序窗口,函数接下来还会调用它的成员函数setCallback、setSoftInputMode和setWindowManager来设置窗口回调接口、软键盘输入区域的显示模式和本地窗口管理器。
之后,Activity给这个PhoneWindow设置一个窗口管理者WindowManager。WindowManager是一个全局的唯一的服务,它的作用是将窗口添加到后台的WindowManagerService,主要方法有addView(),removeView(),updateViewLayout(),还有一个重要的内部类WindowManager.LayoutParams。我们看 mWindow.setWindowManager()到底做了什么。

public abstract class Window { 
    ...... 
    private WindowManager mWindowManager; 
    private IBinder mAppToken; 
    private String mAppName; 
    ......  
     public void  setWindowManager( WindowManager wm, 
        IBinder appToken, String appName) { 
        mAppToken = appToken; 
        mAppName = appName; 
        if (wm == null) { 
            wm = WindowManagerImpl.getDefault(); 
        } 
        mWindowManager =  new LocalWindowManager(wm); 
    }
...
}

实际上是它最终创建了一个本地窗口管理器——LocalWindowManager对象。 LocalWindowManager类的构造函数首先将参数wm所描述的一个WindowManagerImpl对象保存它的成员变量mWindowManager中,这样以后就将窗口管理工作交给它来处理。
WindowManagerImpl 是客户端WindowManager管理接口的实现,WindowManagerImpl内部维护一个单例的WindowManagerGlobal对象,WindowManagerImpl通过该对象转发客户端的窗口管理请求。WindowManagerGlobal对象内部维护一个ViewRootImpl实例数组和一个View视图对象数组,每次客户端添加一个视图时都新创建一个ViewRootImpl对象,并把要添加的视图和新创建的ViewRootImpl对象添加到相应数组中,并调用新创建的ViewRootImpl对象的setView函数。

弄清了Window的创建和管理之后,我们再来看看View究竟是怎么创建的

activity启动过程中,ActivityThread类的成员函数handleLaunchActivity会先调用Activity的onCreate()方法。一般,我们会在这里调用 Activity的setContentView().

class  Activity{
          ...
     public  void  setContentView(  int  layoutResID) {
        getWindow().setContentView(layoutResID);
        initActionBar();
    }
    void  makeVisible() {
            ViewManager wm = getWindowManager();//windowmanager拿着DecorView,建立ViewRoot,并发给后台WMS绘制
            wm.addView(  mDecor , getWindow().getAttributes());
      }
     public  boolean  dispatchKeyEvent(KeyEvent event) {
        onUserInteraction();
        Window win = getWindow();
         if  (win.superDispatchKeyEvent(event)) {
             return  true ;
        }
        View decor =  mDecor ;
         if  (decor ==  null ) decor = win.getDecorView();
         return  event.dispatch(  this , decor !=  null
                ? decor.getKeyDispatcherState() :  null ,  this );
    }
...
}

原来,Activity的setContentView()方法调用了Phone W indow的setContentView()方法:

class PhoneWindow extends Window implements MenuBuilder.Callback {
 @Override
    public void  setContentView(int layoutResID) {
        if (mContentParent == null) { //是否是第一次调用setContentView方法, 如果是第一次调用,则mDecor和mContentParent对象都为空 
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
         mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
private void  installDecor() { 
    if (mDecor == null) { 
        //mDecor为空,则创建一个Decor对象 
        mDecor = generateDecor(); 
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 
        mDecor.setIsRootNamespace(true); 
    } 
    if (mContentParent == null) { 
        //generateLayout()方法会根据窗口的风格修饰,选择对应的修饰布局文件 
        //并且将id为content(android:id="@+id/content")的FrameLayout赋值给mContentParent 
        mContentParent = generateLayout(mDecor); 
         
        //... 
}
 protected DecorView generateDecor() {  
    return new DecorView(getContext(), -1); 
}
protected ViewGroup generateLayout(DecorView decor) { 
    // Apply data from current theme. 
 
    //...1、根据requestFreature()和Activity节点的android:theme="" 设置好 features值 
     
    //2 根据设定好的 features值,即特定风格属性,选择不同的窗口修饰布局文件 
    int layoutResource;  //窗口修饰布局文件   
    int features = getLocalFeatures(); 
    // System.out.println("Features: 0x" + Integer.toHexString(features)); 
    if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { 
        if (mIsFloating) { 
            layoutResource = com.android.internal.R.layout.dialog_title_icons; 
        } else { 
            layoutResource = com.android.internal.R.layout.screen_title_icons; 
        } 
        // System.out.println("Title Icons!"); 
    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMI
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值