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创建的单文档界面中: (基于对话框的, ...
随机推荐
- 在Azure China用自定义镜像创建Azure VM Scale Set
在Azure China用自定义镜像创建Azure VM Scale Set 在此感谢世纪互联的工程师Johnny Lee和Lan,你们给了我很大的帮助.因为Azure China的官网没有给出完整的 ...
- canvas动画——粒子系统(1)
这个动画在很早之前就见过,当时就没迷住了.最近在学canavs动画,动手实现了一下.代码在这里.展示效果在这里. 这属于粒子系统的一种,粒子系统就是需要管理一堆粒子嘛,动画实现的关键在于,遍历这些粒子 ...
- Myeclipse8.6注册机代码,不用到处找注册机了
import java.io.*; public class MyEclipseGen { private static final String LL = "Decompiling thi ...
- nginx四层负载均衡配置
nginx四层负载均衡配置代理Mysql集群 环境如下: ip 192.168.6.203 Nginx ip 192.168.6.*(多台) Mysql 步骤一 查看Nginx是否安装stream模块 ...
- php简单的文件操作
(1)先要想好要操作哪个文件? (2)确定文件的路径? (3)要有什么文件管理功能? 一.先做一下简单的查看文件功能,文件中的文件和文件夹都显示,但是双击文件夹可以显示下一级子目录,双击"返 ...
- NLTK学习笔记(八):文法--词关系研究的工具
[TOC] 对于一门语言来说,一句话有无限可能.问题是我们只能通过有限的程序来分析结构和含义.尝试将"语言"理解为:仅仅是所有合乎文法的句子的大集合.在这个思路的基础上,类似于 w ...
- oracle删除数据后表空间仍过大问题解决方法
-----亲测有效------- --一.备份原始数据库库--1.备份空表--在plsql里面执行一下这句话 然后把结果集 再执行一把 再导数据select 'alter table '||table ...
- Qt使用MySQL笔记一
原始日期:2015-08-20 18:01 今天开发项目时,遇到一个问题,经过自己不断尝试,终于找到了解决办法,于是赶紧记下来,不然过段时间可能又忘了呵呵,从而重蹈覆辙,浪费时间~问题是这样的:在插入 ...
- Qt WebEngine版本要求
WebEngine是Qt5.4之后加入的新特性,用Qt WebEngine取代之前的Qt Webkit http://wiki.qt.io/QtWebEngine windows版本 windows版 ...
- yii 输出当前的sql语句
<?php namespace app\controllers; use yii\web\Controller; use yii\data\Pagination; use app\models\ ...