所谓的窗口(Window)就是一个显示在手机屏幕上可视化视图的一片区域。在Android中窗口是一个抽象的概念,每一个Activity就对应着一个窗口,而所有的窗口都是由视图(View)来呈现,而我们知道View构成的一个树形结构的视图就组成了一个Activity的界面了。在android系统中窗口分为三个类型:

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

android是如何创建应用窗口的呢,下面来逐步分析:

startActivty的启动过程这里就不分析了,具体可以参考http://gityuan.com/2016/03/12/start-activity/,

这里从创建Activity来分析。

1.每个应用窗口都对应一个Activity对象,因此,要创建一个应用窗口,都必须创建一个activity对象。当ActivityManagerService(下面简称AMS)准备启动一个Activty时,会去通知app进程,每个app进程都会对应一个ActivtyThread类,任何Activty都隶属于App进程的,于是,启动Activty的任务就交给了ActivtyThread,

启动一个Activty,首先得创建一个Activty的对象,这个工作是在ActivtyThread.performLaunchActivity中完成的,下面看一下源码:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//创建Activity对象
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.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);
}
}
... return activity;
}

删除了一些我不是很关注的代码。可以看出android是通过ClassLoader来加载所要启动的Activty类的,这就是为什么我们要在manifest文件中配置Activty的原因(ClassLoader加载class时,必须要知道class文件的名字)。下面是newActivity的实现,通过classname去程序文件中加载对应的activty类。

public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}

2.得到activty对象后,接着调研activity的attach方法,这个方法还是在ActivtyThread.performLaunchActivity中完成的:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
... 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);
...
return activity;
}

attach的作用是为创建好的activity设置内部变量,这些变量是以后对activity调度所必需的。

3.在attach内部除了设置变量外,还有另一个任务,就是创建一个PhoneWindow对象,

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) {
attachBaseContext(context); mFragments.attachHost(null /*parent*/); //创建PhoneWindow
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
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);
}
mUiThread = Thread.currentThread(); mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
} 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();
mCurrentConfig = config;
}

创建PhoneWindow后调运 mWindow.setCallback(this),将window的callback设置为当前activty,这就是为什么用户操作能传给activty。

4.创建完Window后,就是给Window对象中的mWindowManager赋值。mWindowManager是Window类中一个类型为WindowManager的对象,WindowManager是一个接口,它的实现类有WindowManagerImpl。通过(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)来设置这个对象。

5.配置好activty和window后,就开始给窗口添加view了,还是在ActivtyThread.performLaunchActivity中,在以上步骤完成之后,调运callActivityOnCreate方法,

activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
//Instrumentation.java
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}
//Activty.java
final void performCreate(Bundle icicle) {
restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}

可以看出这里就会调用Activty的onCreate,这个我们应该很熟悉了。后面会接着调:

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

setContenView实际上会调运

public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}

其实质上就是调运PhoneWindow的setContentView。

6.接着分析PhoneWindow的setContentView

@Override
public void setContentView(int layoutResID) {
// 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) {
installDecor();
} 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);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}

首先执行的是installDecor(),installDecor()为Window创建一个窗口修饰,所谓的窗口修饰就是界面上常见的标题栏,代码指点的layout.xml将被包含在窗口修饰中,称之为窗口内容。窗口修饰及其内部的窗口内容就是我们通常说的窗口。如图:

创建完后,就是把layout.xml设置个mContentParent,即id=content的FrameLayout。最后,调运cb.onContentChanged()来通知Activty窗口的内容发生了变化。cb是在之前提到的给Window设置的callback。

7.接下来ActivityThread调用了handleResumeActivity(),首先调用performResumeActivity(),辗转调用了Activity的onResume(),接着会调用Activity的makeVisible()。该方法及后续的各种调用将完成真正的把窗口添加进WmS之中。

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

8.之前说到WindowManager只是一个接口类,真正的实现是WindowManagerImpl,因此真正的addView操作就在此类中。然而它又交给了WindowManagerGlobal处理。

	private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

    @Overridepublic
void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}

9.WindowManagerGlobal的addView过程,一个应用程序内部无论有多少个Activity, 但只有一个WindowManagerGlobal对象在 WindowManagerGlobal类中维护三个集合,用于保存该应用程序中所拥有的窗口的状态。而后调用ViewRootImpl.setView(),完成最后的、真正意义上的添加工作。

private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null; synchronized (mLock) {
...
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) {
...
throw e;
}
}

10.ViewRootImpl.setView方法篇幅相对较长,其主要执行的操作有

  • 给 ViewRoot的重要变量赋值;
  • 调用requestLayout(),发出界面重绘请求;进而执行scheduleTraversals(),进行View的绘制工作。
  • 调用sWindowSession.addToDisplay(),通知WmS显示View。
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);

mWindowSession的类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是说Window的添加过程是一次IPC调用。添加请求交给WmS去处理了。

到此为止,从客户端的角度来讲,已经完成了窗口创建的全部工作。从而界面才会呈现到用户面前。可见在onCreate的时候View并没有做测量、布局、绘制操作,因此无法获取到宽高数据。

ViewRootImpl

  • 链接WindowManager和DecorView的纽带,更广一点可以说是Window和View之间的纽带。
  • 完成View的绘制过程,包括measure、layout、draw过程。
  • 向DecorView分发收到的用户发起的event事件,如按键,触屏等事件

Android创建窗口(一)创建应用窗口的更多相关文章

  1. 创建一个弹出DIV窗口

    创建一个弹出DIV窗口 摘自:   http://www.cnblogs.com/TivonStone/archive/2012/03/20/2407919.html 创建一个弹出DIV窗口可能是现在 ...

  2. Duilib创建窗口双击标题栏禁止窗口最大化

    使用Duilib创建窗口并禁止窗口最大化 第一步: XXXFrame.Create(NULL, _T("XXXFrame"), UI_WNDSTYLE_EX_FRAME, WS_E ...

  3. uCGUI窗口的创建过程分析

    一.相关结构体和变量 窗口管理结构体 /* 窗口管理结构体 共30个字节 */ struct WM_Obj { GUI_RECT Rect; //窗口尺寸(x0,y0,x1,y1) 8个字节 GUI_ ...

  4. Cinder-2 窗口的创建过程

    通过TinderBox生成的代码很简单,整个代码如下: #include "cinder/app/AppNative.h" #include "cinder/gl/gl. ...

  5. win32 api Windows窗口的创建

    windows窗口的创建有以下几个步骤: 1.创建注册窗口类 2.创建窗口句柄 3.显示更新窗口 4.消息循环 1.创建注册窗口类 所谓创建窗口类就是定义一个WNDCLASS类对象,并将该对象进行初始 ...

  6. vc++窗口的创建过程(MFC消息机制的经典文章)

    一.什么是窗口类  在Windows中运行的程序,大多数都有一个或几个可以看得见的窗口,而在这些窗口被创建起来之前,操作系统怎么知道该怎样创建该窗口,以及用户操作该窗口的各种消息交给谁处理呢?所以VC ...

  7. Windows窗口的创建

    Windows窗口创建的基本代码: #include <Windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); i ...

  8. 有谁知道Delphi中"窗口"的创建过程?

      求助:有谁知道Delphi中窗口的创建过程,此“窗口”不仅仅指 TForm 类型, 还包括一般的窗口控件,如TButton,TEdit等等,希望有能够十分详细的运作 过程,比如说CreatPara ...

  9. Qt学习之对话框与主窗口的创建

    Qt中的信号与槽机制 qt中槽和普通的C++成员函数几乎是一样的--可以是虚函数,可以被重载,可以是共有的,保护的或者私有的. 槽可以和信号连接在一起,在这种情况下,每当发射这个信号的信号,就会自动调 ...

  10. 【MFC】如何在MFC创建的程序中更改主窗口的属性 与 父窗口 WS_CLIPCHILDREN 样式 对子窗口刷新的影响 与 窗体区域绘制问题WS_CLIPCHILDREN与WS_CLIPSIBLINGS

    如何在MFC创建的程序中更改主窗口的属性 摘自:http://blog.sina.com.cn/s/blog_4bebc4830100aq1m.html 在MFC创建的单文档界面中: (基于对话框的, ...

随机推荐

  1. Akka(4): Routers - 智能任务分配

    Actor模式最大的优点就是每个Actor都是一个独立的任务运算器.这种模式让我们很方便地把一项大型的任务分割成若干细小任务然后分配给不同的Actor去完成.优点是在设计时可以专注实现每个Actor的 ...

  2. Scrapy教程--豆瓣电影图片爬取

    一.先上效果 二.安装Scrapy和使用 官方网址:https://scrapy.org/. 安装命令:pip install Scrapy 安装完成,使用默认模板新建一个项目,命令:scrapy s ...

  3. 《物联网框架ServerSuperIO教程》-20.网络通讯控制器分组,提高交互的负载平衡能力。v3.6.6 版本发布

    20.1     概述 ServerSuperIO原来在网络通讯模式下,只有一个网络控制器,在自控模式.并发模式和单例模式下时都是异步处理返回的数据,并不会出现性能问题.但是在轮询模式下,一个网络控制 ...

  4. Chrome 开发者工具断点调试(视频教程)

    很多人不了解 Chrome Dev Tools (开发者工具)的使用方法和技巧. 其中很多技能,无论是前端开发从业者,还是普通用户,了解一些还是对日常很有帮助的. 本猿定期录制.甚至根据您的需求来订制 ...

  5. 使用MySQLdb操作Mysql数据库

    MySQLdb简介 MySQL是一个小型关系型数据库管理系统,开发者为瑞典MySQLAB公司.在2008年1月16号被Sun公司收购.目前MySQL被广泛地应用在Internet上的中小型网站中.由于 ...

  6. 每天一个JS 小demo之留言板。主要知识点:DOM方法的理解和运用

    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"& ...

  7. 解决ionic2各种坑文章收集

    小白最近打算用ionic2做一个APP,无奈没有大神指点,一路坎坷遇到数不清的坑(主要是墙的问题).这里整理一些大神的帖子链接,用以指路. 新建/打包: Ionic2+Angular2创建项目打包An ...

  8. 基于C#的接口自动化测试(一)

    其实就是找个地方然后给关键的代码做个笔记什么的-- 字符串访问API接口,访问方法为POST: string url = URL; string RequestParam = Param; strin ...

  9. 创建单页web app, 如何在chrome中隐藏工具栏 地址栏 标签栏?

    问题描述: 为使用更大的屏幕空间,在访问web应用的使用,如何隐藏地址栏.工具栏? 解决办法: 1. chrome的application mode 选项--->更多工具---->添加到桌 ...

  10. python 最佳实践与资源汇总

    python 最佳实践 (部分) 一. 结构化工程 文件 功能 README.rst readme LICENSE 许可证 setup.py 打包和发布管理 requirements.txt 开发依赖 ...