Android中窗口的创建过程
来源:互联网 发布:python源码剖析笔记 编辑:程序博客网 时间:2024/06/06 01:06
在阅读本文之前建议先阅读我的另外一篇Java层Binder机制详解,因为里面有些地方涉及到通过进程间通过Binder机制通信。
在Android将窗口分为三类:应用窗口,子窗口,系统窗口
应用窗口一般需要和一个Activity对应,子窗口是有父窗口的窗口,系统窗口时由系统创建的窗口,用户是无法创建系统窗口的(Toast,输入法窗口,壁纸窗口除外)
这里我主要分析一下应用窗口的创建过程,如果不对的地方希望能提出。
上面说了应用窗口时和Activity对应的,所以应用窗口时伴随Activity创建而创建的,所以首先从Activity的创建开始,Android的所有的应用的入口函数是ActivityThread的main函数,经过辗转调用就会执行到handleLaunchActivity,那我们就从handleLaunchActivity开始吧
private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { //创建一个Activity 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创建一个Activity,然后调用Activity的onResume方法,那就进入到performLaunchActivity中看是如何创建一个Activity的
private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); Activity activity = null; try {//通过反射机制创建一个Activity 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 {//调用attach方法 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的attach方法,我们可以进入看看里面做了什么
final void attach(...) { attachBaseContext(context);//就是这里了,在这里创建了一个Widnow对象 mWindow = PolicyManager.makeNewWindow(this);//设置回调函数,使得Activity可以处理一些事件 mWindow.setCallback(this);//创建mWindow中的WindowManager mWindow.setWindowManager(null, mToken, mComponent.flattenToString()); mWindowManager = mWindow.getWindowManager(); }
这个函数非常关键,所以我们需要详细分析一下。
(1) 通过调用makeNewWindow函数创建了一个函数,我们进入看看这个函数
publicPhoneWindow makeNewWindow(Context context) {
return new PhoneWindow(context);
}
原来Activity中的mWindow变量保存的是一个PhoneWindow,
(2) 调用setWindowManager为mWindow设置WindowManage,同样看看这个函数
publicvoid setWindowManager(WindowManager wm,
if (wm == null) {
wm =WindowManagerImpl.getDefault();
}
mWindowManager = newLocalWindowManager(wm);
}
有点懵了吧,确实挺复杂,容我慢慢给你解释。这里出现了WindowManagerImpl,LocalWindowManager,通过阅读源码可以知道这两个类分别实现了WindowManager,其中LocalWindowManager是Window的内部类,WindowManagerImpl是一个独立的类,从上面的代码我们可以看出LocalWindowManager中一定有一个WindowManagerImpl对象,这个不是代理模式吗?这里先留个伏笔吧,到后面我们自然就会知道的。
我们现在退回performLaunchActivity吧,这里会调用Activity的onCreate方法。我们想一想在onCreate方法里面我们一定会调用什么函数?对就是setContentView函数,那么就到Activity的setContentView看看
publicvoid setContentView(View view, ViewGroup.LayoutParams params) {
getWindow().setContentView(view,params);
}
原来Activity中的setContentView是调用的mWindow中的setContnetView,由于mWindow本质上是PhoneWindow,那么我们到PhoneWindow中去看看这个函数:
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(); } }
这个函数比较关键,我们也得仔细分析一下:
(1) 判断mContentParent是否为空,如果为空就调用installDecor(),分析一下这个函数吧
privatevoid installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
}
if (mContentParent == null) {
mContentParent =generateLayout(mDecor);
}
}
这里涉及到一个mDecor变量,他是一个DecorView类型的,他是PhoneWindow的一个内部类,首先判断它是否为空,如果为空这创建它,然后利用这个mDecor创建mContentParent,我们进入到generateLayout函数中瞧瞧(部分重要源码,如果要看完整的,请下载Android2.3源码)
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. TypedArray a = getWindowStyle(); mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false); int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR) & (~getForcedWindowFlags()); 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; } mDecor.startChanging(); View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); return contentParent;}
在看函数之前我们先需要明白一个知识,我们想一个Activity没有标题栏是,我们又两种方法,一种是通过代码实现,另外一种是通过配置xml文件,在这里就对应了这两种方法:(明白为什么设置没有标题栏这个操作一定要在setContentView之前没)
TypedArraya = getWindowStyle();(从xml文件读取)
intfeatures = getLocalFeatures();(从代码中设置)
通过这些特性选择使用那些xml文件,这个xml文件的路径在:
\frameworks\base\core\res\res\layout
这个xml文件中必须有一个FrameLayout它的id是ID_ANDROID_CONTENT,也就是上面的mContentParent。
写到这里我先小小的总结一下吧。
一个Activity在创建的同时,会创建一个Window对象,这个Window对象的真实类型是Phonewindow。Activity调用setContentView其实是调用的PhoneWindow的setContentView,在setContentView中会创建一个DecorView对象,DecorView对象通过调用addView方法讲一个使用xml文件转化过来的添加到DecorView中,而这个xml文件中包含一个id为ID_ANDROID_CONTENT的FrameLayout。所以一个Activity的界面其实如下:
然后我们通过mContentParent.addView(view, params);将我们的View添加到mContentParent中。
好吧,那我们接着进行分析吧
经过辗转的调用,下面就会调用到Activity中的
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
还记得vm真实类型是什么吗?LocalManagerService,我们进入到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; CharSequence curTitle = wp.getTitle(); if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { if (wp.token == null) { View decor = peekDecorView(); if (decor != null) { wp.token = decor.getWindowToken(); } } ... if (wp.packageName == null) { wp.packageName = mContext.getPackageName(); } mWindowManager.addView(view, params); }
在这个函数中,经过某些判断后调用mWindowManager的addView,mWindowManager是WindowManagerImpl类型的,所以真的是代理模式吧
那继续跟进,进入WindowManagerImpl中的addView函数看看
private void addView(View view, ViewGroup.LayoutParams params, boolean nest) { ViewRoot root; View panelParentView = null; // do this last because it fires off messages to start doing things root.setView(view, wparams, panelParentView);}
这个里面出现了一个ViewRoot类型的变量,然后调用root.setView函数,记住这个参数view是上面创建的mDecor变量。
挺复杂的吧,呵呵。我第一次分析的时候也是头被弄大了,多看几次就通了。
进入Viewroot中的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(); } } } }
这个里面就是调用sWindowSession.add方法,sWindowSession是一个IwindowSession类型,他是在ViewRoot的构造函数通过getWindowSession初始化的,看看他是怎么初始化的吧,在看这个函数之前,最好看看我的另外一篇文章《Java层Binder机制详解》
public static IWindowSession getWindowSession(Looper mainLooper) { synchronized (mStaticInit) { if (!mInitialized) { try { InputMethodManager imm = InputMethodManager.getInstance(mainLooper); sWindowSession = IWindowManager.Stub.asInterface( ServiceManager.getService("window")) .openSession(imm.getClient(), imm.getInputContext()); mInitialized = true; } catch (RemoteException e) { } } return sWindowSession; } }
也就是说其实调用的是WindowManagerService.Session.add的方法,而在WindowManagerService.Session.add中是调用的WindowManagerService.addWindow方法,好了,窗口的创建完成了。
- Android中窗口的创建过程
- Android中窗口的创建过程
- android 创建窗口的过程
- 【Android】创建窗口的过程
- Android窗口创建过程
- Android应用程序窗口View的创建过程
- VC中窗口的创建过程
- 创建窗口的过程
- 窗口的创建过程
- 窗口的创建过程:
- 6.Android内核 创建窗口的过程(一)
- 7.Android内核 创建窗口的过程(二)
- Android Activity应用窗口的创建过程分析
- Android学习笔记-Activity窗口的创建过程
- MFC中窗口的创建及子类化过程
- 黑窗口中创建mysql存储过程时的问题
- 有谁知道Delphi中"窗口"的创建过程?
- 创建一个窗口的过程
- window7下激活DXP 2004 sp2
- 转型中面临痛苦与迷茫
- 操作系统--Linux常用命令(1)
- Linux 内存 buffer 和 cache 的区别
- 链表list<>中sort()和unique()的用法
- Android中窗口的创建过程
- C#中线程的学习
- 动态规划——导弹问题
- 取代哦ubuntu的菜单栏
- 深入浅出CChart 每日一课——快乐高四第二课 富丽堂皇,没落贵族宝贵遗产之QT篇
- socket编程基础
- poj 1789 Truck History (最小生成树)
- IOS中获取各种文件的目录路径的方法
- Linux TCP多进程并发服务器