Android创建窗口(一)创建应用窗口
所谓的窗口(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创建窗口(一)创建应用窗口的更多相关文章
- 创建一个弹出DIV窗口
创建一个弹出DIV窗口 摘自: http://www.cnblogs.com/TivonStone/archive/2012/03/20/2407919.html 创建一个弹出DIV窗口可能是现在 ...
- Duilib创建窗口双击标题栏禁止窗口最大化
使用Duilib创建窗口并禁止窗口最大化 第一步: XXXFrame.Create(NULL, _T("XXXFrame"), UI_WNDSTYLE_EX_FRAME, WS_E ...
- uCGUI窗口的创建过程分析
一.相关结构体和变量 窗口管理结构体 /* 窗口管理结构体 共30个字节 */ struct WM_Obj { GUI_RECT Rect; //窗口尺寸(x0,y0,x1,y1) 8个字节 GUI_ ...
- Cinder-2 窗口的创建过程
通过TinderBox生成的代码很简单,整个代码如下: #include "cinder/app/AppNative.h" #include "cinder/gl/gl. ...
- win32 api Windows窗口的创建
windows窗口的创建有以下几个步骤: 1.创建注册窗口类 2.创建窗口句柄 3.显示更新窗口 4.消息循环 1.创建注册窗口类 所谓创建窗口类就是定义一个WNDCLASS类对象,并将该对象进行初始 ...
- vc++窗口的创建过程(MFC消息机制的经典文章)
一.什么是窗口类 在Windows中运行的程序,大多数都有一个或几个可以看得见的窗口,而在这些窗口被创建起来之前,操作系统怎么知道该怎样创建该窗口,以及用户操作该窗口的各种消息交给谁处理呢?所以VC ...
- Windows窗口的创建
Windows窗口创建的基本代码: #include <Windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); i ...
- 有谁知道Delphi中"窗口"的创建过程?
求助:有谁知道Delphi中窗口的创建过程,此“窗口”不仅仅指 TForm 类型, 还包括一般的窗口控件,如TButton,TEdit等等,希望有能够十分详细的运作 过程,比如说CreatPara ...
- Qt学习之对话框与主窗口的创建
Qt中的信号与槽机制 qt中槽和普通的C++成员函数几乎是一样的--可以是虚函数,可以被重载,可以是共有的,保护的或者私有的. 槽可以和信号连接在一起,在这种情况下,每当发射这个信号的信号,就会自动调 ...
- 【MFC】如何在MFC创建的程序中更改主窗口的属性 与 父窗口 WS_CLIPCHILDREN 样式 对子窗口刷新的影响 与 窗体区域绘制问题WS_CLIPCHILDREN与WS_CLIPSIBLINGS
如何在MFC创建的程序中更改主窗口的属性 摘自:http://blog.sina.com.cn/s/blog_4bebc4830100aq1m.html 在MFC创建的单文档界面中: (基于对话框的, ...
随机推荐
- 自研框架wap.js实践
示例 使用分为3个步骤: 1, 配置模板渲染中心,方便别人可以看到你的模板渲染,请求是什么关系,复杂度怎样 2, 配置事件分发中心 方便观察事件分发,事件复杂度 3,写对应的请求方法.渲染方法. ...
- css3 linear-gradient渐变效果及兼容性处理
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- C#码农的大数据之路 - 使用C#编写MR作业
系列目录 写在前面 从Hadoop出现至今,大数据几乎就是Java平台专属一般.虽然Hadoop或Spark也提供了接口可以与其他语言一起使用,但作为基于JVM运行的框架,Java系语言有着天生优势. ...
- hdu3622
hdu3622 题意 每回合给定两个坐标点,可以选择一个放置炸弹,自己决定炸弹的半径,问 n 个回合后,使炸弹半径最小值最大. 分析 存在对立关系:每回合只能选择一个地方放置炸弹.i 表示 第一个位置 ...
- Java IO流--练习2
1)写一个Java程序,输入3个整数,并求出三个数的最大数和最小数 代码: package 第十二章IO流; import java.io.BufferedReader; import java.io ...
- IIC协议学习笔记
"移植"的重要性:并非所有的电路都得自己设计,到了一定阶段,"移植"也是一种学习能力.--CrazyBingo 转眼间期末又到了,最近开始了所谓的期末总预习,比 ...
- 用JQuery写的滚动条,可以改变样式哦!
很早之前在做项目的时候要用到自定义的滚动条,可是现在的CSS2只能改改颜色什么的,对于改变形状或者更高级的用法根本不可能实现,没办法只能自己写一个了.(好像CSS3可以该形状,不过没研究过有兴趣的童鞋 ...
- Java代码编写规范(不是标准规范,自行整理,无须纠结)
最近回过头来给以前的项目增加功能,发现之前写的注释非常不全,代码也非常的不整洁,有些地方写的''窝七八烂的,看着很不舒服:又恰好经理最近也经常跟我提起代码规范,我们就讨论了一下代码规范的重要性和必要性 ...
- asp net core 跨平台初体验
标: 在 ubuntu 16.04 上部署一个 asp.net core 站点,打开网站后显示一段文字. 安装 net core 运行环境:ubuntu 16.04 LTS 1.添加 apt 源 ...
- mac中使用 sourcetree 的快速配置和git服务器登录
问题: 1.mac中下载sourcetree配置仓库地址,一直在提示输入密码,无法登录成功,更无法获取源码. 2.找不到配置仓库时的账号密码,只看到地址. 场景: git服务器:自己的GIT服务器,非 ...