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方法,好了,窗口的创建完成了。


原创粉丝点击