Android窗口管理之一--创建窗口

      在android系统中窗口系统是一个很庞大很复杂的系统,对系统中的所有窗口进行管理是由窗口管理服务WindowManagerService来负责处理的,它是一个系统服务,扮演C/S模式中的服务端角色,而上层应用就是客户端,客户端负责请求创建窗口和使用窗口,服务端完成窗口的维护,窗口显示等。

一、什么是窗口?
我们通常所说的窗口是指屏幕上显示视图的一块区域,它主要负责展示界面及事件交互,比如说打开一个应用后看到的主界面就是一个窗口,手机上的顶部状态栏、底部导航栏及弹出的对话框、Taost提示框等都是一个个窗口。而我们在framework代码中看到的Window类及window类型的对象并不是我们所说的窗口概念。我们在手机屏幕上看到的显示效果通常会有多个窗口组成,有顶部状态栏、底部导航栏和上层应用的窗口,而这多个窗口都是通过WindowManagerService来布局和控制在z轴上的顺序的,然后交给SurfaceFlinger来对多个窗口内容进行合成,SurfaceFlinger把合成好的图像信息送给硬件帧缓冲区显示出来。

二、窗口类型
Framework在WindowManager中定义了三种窗口类型:
1、应用窗口。应用窗口是指该窗口对应着一个Activity,Activity是由AMS来统一调度的。Activity通过调用setContentView来加载自定义的视图结构,然后找到视图树的根DecorView,通过调用WindowManager.addView()来将此DecorView添加到WMS中,显示到窗口区域。
2、子窗口。子窗口指该窗口必须依赖一个父窗口,父窗口可以是一个应用类型的窗口,也可以是任何其他类型的窗口,子窗口和父窗口拥有相同的Token,父窗口被销毁时子窗口也必须被销毁。例如Menu菜单,Dialog等。
3、系统窗口。系统窗口不需要对应任何Activity,也不需要有父窗口,系统窗口是由系统进程创建出来的,例如有壁纸界面、顶部状态栏、底部导航栏,输入法窗口等,它们是通过直接调用WindowManager.addView()来把一个view添加到WindowManager中。
这里写图片描述

三、窗口管理
窗口管理基于C/S模式来控制的,Client端就是上层应用,Server端则是WindowManagerService,简称WMS。WMS是系统服务,运行在system_server进程里,Client端通过进程间通信的方式请求WMS创建窗口,由WMS向Client返回窗口相关的结果。所有应用的窗口都由WMS来管理和控制,WMS具有以下职责:1、窗口添加与删除。2、窗口显示与隐藏。3、z-order管理。4、焦点窗口管理。5、输入法与壁纸窗口管理。6、窗口动画。7、系统消息分发。下面看几个重要的概念。
1、WindowToken:它是一个句柄,保存了所有具有同一个token的WindowState。Activity是由AMS来管理和控制的,一个Activity会在AMS里生成一个对应的token对象,当应用请求添加窗口的时候,wms会包装这个token对象,生成一个WindowToken对象。所以,WindowState的token相同则使用同一个WindowToken。
2、AppWindowToken:继承于WindowToken,用于标识一个Activity。子窗口与父窗口拥有相同的AppWindowToken对象。
3、Session:表示一个客户端和服务端的交互会话。一般来说不同的应用通过不同的会话来和WindowManagerService交互,但是处于同一个进程的不同应用使用同一个Session来交互。
      由于窗口管理的职责较多,需要对每一个职责进行细化分析,今天我们首先来分析窗口是如何被创建出来的,在后面的文章里会继续分析WMS管理窗口的每一个职责。

四、应用窗口创建过程
启动一个应用,首先会执行主线程ActivityThread的main函数,通过Handler发送消息给主线程执行handleLaunchActivity函数。

private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {   
    Activity a = performLaunchActivity(r, customIntent);  
    if (a != null) {  
        r.createdConfig = new Configuration(mConfiguration);  
        Bundle oldState = r.state;  
        //调用Activity中的onResume方法  
        handleResumeActivity(r.token, false, r.isForward);  
        ...
}

继续查看performLaunchActivity方法。

private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
    // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");  
    Activity activity = null;  
    try {  
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();  
        activity = mInstrumentation.newActivity(  
                cl, component.getClassName(), r.intent);  
        r.intent.setExtrasClassLoader(cl);  
        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);  
        }  
    }  

    try {                
        activity.attach(appContext, this, getInstrumentation(), r.token,  
                r.ident, app, r.intent, r.activityInfo, title, r.parent,  
                r.embeddedID, r.lastNonConfigurationInstance,  
                r.lastNonConfigurationChildInstances, config);  

        //执行Activity的onCreate方法  
        mInstrumentation.callActivityOnCreate(activity, r.state);  
       ...  
    return activity;  
}  

过程很简单,首先利用反射生成一个Activity对象,接着调用Activity对象的attach方法,最后调用Activity对象的onCreate方法。

final void attach(参数很多) {  
    attachBaseContext(context);    
    mWindow = PolicyManager.makeNewWindow(this);    
    mWindow.setCallback(this);   
    mWindow.setWindowManager(null, mToken, mComponent.flattenToString());  
    mWindowManager = mWindow.getWindowManager();  
}

public PhoneWindow makeNewWindow(Context context) {
    return new PhoneWindow(context);
}

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);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

首先调用PolicyManager创建一个Window对象,它其实是一个PhoneWindow类型的对象,然后给Window对象设置回调函数,使Activity可以接收WMS分发的消息。接着创建Window对象里的WindowManager,这里的WindowManager是LocalWindowManager类型的对象,它是Window的内部类。
接着会调用Activity的onCreate方法,在onCreate方法里调用setContentView设置layout布局。我们继续查看setContentView函数

public void setContentView(View view, ViewGroup.LayoutParams params) {  
    if (mContentParent == null) {  
        installDecor();  
    } else {  
        mContentParent.removeAllViews();  
    }  
    mContentParent.addView(view, params);  
    final Callback cb = getCallback();  
    if (cb != null) {  
        cb.onContentChanged();  
    }  
}

这个方法在之前的文章里介绍过,主要是加载UI布局并得到视图树根节点DecorView和装载应用视图的容器mContentParent。
接着主线程里调用Activity的onResume方法,在执行了onResume方法后便调用activity.makeVisible()方法显示窗口。

void makeVisible() {  
    if (!mWindowAdded) {  
        ViewManager wm = getWindowManager();  
        wm.addView(mDecor, getWindow().getAttributes());  
        mWindowAdded = true;  
    }  
    mDecor.setVisibility(View.VISIBLE);  
}

方法很简单,得到WindowManager,把根视图DecorView添加到WindowManager里,并设置DecorView为可见状态。此处的wm是LocalManagerService类型的实例,我们来查看它的addView方法。

public final void addView(View view, ViewGroup.LayoutParams params) {  
    // Let this throw an exception on a bad params.  
    WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;  
    // 校验token等参数 
      ...  
    if (wp.packageName == null) {  
        wp.packageName = mContext.getPackageName();  
    }
    // 这儿的mWindowManager是WMS在客户端的代理对象WindowManagerImpl
    mWindowManager.addView(view, params);  
}

WindowManagerImpl实际上都是调用WindowManagerGlobal里面的方法,继续进入WindowManagerGlobal中的addView函数

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    ...
    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.
        synchronized (mLock) {
            final int index = findViewLocked(view, false);
            if (index >= 0) {
                removeViewLocked(index, true);
            }
        }
        throw e;
    }
}

最主要是构造ViewRootImpl对象,然后调用它的setView方法。那么ViewRoot是什么呢?它其实不是一个View,但它充当了视图树的根,所有的事件和消息都从它这儿往下分发。
接着看setView方法:

public void setView(View view, WindowManager.LayoutParams attrs,  
            View panelParentView) {  

    try {  
        res = sWindowSession.add(mWindow, mWindowAttributes,  
                getHostVisibility(), mAttachInfo.mContentInsets,  
                mInputChannel);  
    } catch (RemoteException e) {  

    } finally {  
        if (restore) {  
            attrs.restore();  
        }  
    }     
}

WindowSession是与WMS通信的对象,这里调用sWindowSession.add其实是在调用Session.add方法,在Session类中,add方法调用mService.addWindow方法,这儿的mService就是WMS。
我们继续看WMS的addWindow方法。

boolean reportNewConfig = false;
WindowState attachedWindow = null;
WindowState win = null;
long origId;
final int type = attrs.type;

// 根据窗口类型做判断

win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
if (win.mDeathRecipient == null) {
    // Client has apparently died, so there is no reason to
    // continue.
    Slog.w(TAG, "Adding window client " + client.asBinder()
            + " that is dead, aborting.");
    return WindowManagerGlobal.ADD_APP_EXITING;
}

mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

res = mPolicy.prepareAddWindowLw(win, attrs);

...

这样在服务端完成了与客户端对应的WindowState对象的初始化,并调用mPolicy调整窗口的属性,到此就实现了窗口的创建过程。

五、窗口创建过程总结
启动一个应用,应用进程初始化完成后,会执行主线程ActivityThread的main函数,在main函数里面给Handler发送消息执行LaunchActivity,首先生成一个Activity对象,此时该Activity对象还没有属于它的窗口。接着调用attach函数,在attach函数里面执行PolicyManager.makeNewWindow()创建一个PhoneWindow对象。然后Activity调用setContentView方法加载UI视图结构,将此视图结构放进一个framework预定义好的layout里,找到这个layout的根节点,也就是整个activity显示界面的根节点,赋值给PhoneWindow里面的mDecorView变量。最后在Activity显示出来之前,通过调用getWindow().getDecorView()得到DecorView对象,接着调用WindowManager.addView()将DecorView添加到WindowManager中。在WindowManager内部通过该Activity的token和该View关联起来,向WindowManagerService申请创建窗口的时候,再把token传递给WindowManagerService。WMS通过TokenMap查询该token的AppWindowToken,如果为空则抛出异常,否则创建一个WindowState并做相应的参数调整。WindowState申请一个显示缓存,交给SurfaceFlinger来与其他的Window合成处理,把合成好之后的图像信息送到硬件帧缓冲区打印显示出来。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值