7.Android内核 创建窗口的过程(二)






创建应用窗口


1.每个应用类窗口都对应一个Activity对象,所以,创建应用类窗口首先需要创建一个Activity对象。

当AmS决定启动某个Activity时,会通知客户端进程,而每个客户端进程都对应一个ActivityThread

类,任何Activity都必须隶属于一个应用程序,因此,启动Activity的任务最终由ActivityThread 完成。


启动某个Activity的代码本质是构造一个Activity对象,看看ActivityThread里的

performLaunchActivity()方法源码


    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        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);
            }
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.voiceInteractor);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
                if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onPostCreate()");
                    }
                }
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }


以上,使用了ClassLoader从程序文件中装载指定的Activity对应的class文件


2.构造好指定的Activity对象后,接着调用Activity的attach()方法:


   attach()的作用是为刚刚构造好的Activity设置内部变量,这些变量是以后进行Activity调度所必

   须的。这些重要的变量包含:

        appContext:该对象将作为Activity的BaseContext。Activity的本质就是一个Context,而同时Activity

        有时继承于ContextWrapper,该类需要一个真正的Context对象,而这就是appContext,该对象使用

        new ContextImpl()方法创建。


        this:这就是指的是当前的ActivityThread对象,Activity对象内部可能会需要主程序的引用。


        r.token:r是一个ActivityRecord对象,其内部变量token的含义是AmS中的一个HistoryRecord对象。


        r.parent:一个Activity可以有一个父Activity,这种理念是为了允许把一个Activity嵌入到另一个Activity内部

        执行。在应用程序使用时,常用ActivityGroup类,而ActivityGroup功能的内部支持的正是该变量。




3.在attach()方法内部,除了进行重要变量赋值外,另一件重要的事情就是为该Activity创建Window对

,这是通过调用PolicyManager的静态方法makeNewWindow()完成的。


PolicyManager会根据com.android.intemal.policy.impLPolicy的配置创建不同产品类型的窗口。这仅

仅是一种程序设计的灵活性,其代码归根到底是创建了一个PhoneWindow对象而已。当前的Framework

中仅仅定义有两种Window的具体实现,一种是MidWindow类,另一种是PhoneWindow类,而前者基本

上没有使用。从名称不难看出,Android的设计之初所设想的两种应用,其中一种是手机,另一种是便携上

网设备(Mobile Internet Device)。


***创建好Window对象后,将其赋值给Activity的内部变量mWindow,并设置该Window的

Callback接口为当前的Activity对象这就是为什么用户消息能够传递到Activity中的原因。那么

我们来看一看Activity中的attach()方法源码


    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, IVoiceInteractor voiceInteractor) {
        attachBaseContext(context);

        mFragments.attachActivity(this, mContainer, null);

        mWindow = PolicyManager.makeNewWindow(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;
        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;
    }



4.创建好Window对象后,需要给Window对象中的mWindowMananger变量赋值,该变量的类型

是WindowManager类。每一个Window内部都有一个WindowManager对象,WindowManager

类是一个重量级的类,如果每一个Window中都包含一个WindowManager的引用会不会是一种浪费?

但是实际上,WindowManager类仅仅是一个interface类而已,真正实现该接口的有两个类,一个是

Window.LocalWindowManager子类,另一个是WindowManagerImpl类LocalWindowManager

仅仅是一个壳而已,这就跟ContextWrapper类似,本身虽然提供了WindowManager接口的全部功

能,然而真正实现这些功能的却是壳里面的WindowManager对象这就是WindowManagerImpl类


7.Android内核 创建窗口的过程(二)_第1张图片


也正是因为以上关系,所以,每个Window内部才包含一个WindowManager的壳以便进行其他操

作,在Activity里的attach()给mWindowManager赋值了:


        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();


这里调用了Window.setWindowManager方法去设置该Activity对应的WindowManager,那我们

接着来看看Window.setWindowManager的源代码


    /**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is <em>not</em> used for displaying the
     * Window itself -- that must be done by the client.
     *
     * @param wm The window manager for adding new windows.
     */
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }


结合以上两段源代码来分析,setWindowManager()方法的第一个参数

(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),显而易见的

是,这里直接通过Context.getSystemService(String systemName)的方式取得了

WindowManager第二个参数是mToken,这正是AmS中Activity里对应的HistoryRecord的

Binder引用该变量将会作为Window中的mAppToken的值被保存下来


到了Window.setWindowManager中,还进行了一次if(wm==null)的判断,如果为空,则

继续用Context.getSystemService(String systemName)的方式再取一次WindowManager

但是通过mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this)

可以知道,传进来的无论是否是null的WindowManager引用,其实都是备胎都会转换成

WindowManagerImpl然后再去构造一个LocalWindowManager对象,最后保存在

mWindowManager中


*** 所以每一个Activity内部都有一个mWindowManager对象,这就是LocalWindowManager,和

Window类中的同名变量相同。





5.配置好了Activity和Window对象后,接下来就需要给该窗口添加真正的显示元素View或者

ViewGroup。这是从Activity.performLaunchActivity()内部调用callActivityOnCreate()开始的 

并会辗转到我们熟悉的Activity.onCreate()中。


我们都可以知道给Activity添加界面是在onCreate()方法中调用setContentView()方法,而该方

实际上却又调用到了其所对应的Window对象的setContentView方法,对此我们可以看看

Activity.setContentView()的源代码


    /**
     * Set the activity content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the activity.
     *
     * @param layoutResID Resource ID to be inflated.
     *
     * @see #setContentView(android.view.View)
     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
     */
    public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }


这里,可以看到我们传进去的Layout对应的Id又被对应的Window拿去执行了setContentView了。




6.PhoneWindow.setContentView()源代码:


    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }
    }


首先调用installDecor()方法Window类安装一个窗口装饰,所谓的窗口修饰就是界面上常见的

标题栏,程序中指定的layout.xml界面将被包含在窗口修饰中,称为窗口内容。窗口修饰也是一个

ViewGroup,窗口修饰及其内部的窗口内容就是我们所说的窗口,或者叫做Window的界面。


7.Android内核 创建窗口的过程(二)_第2张图片



Framework中定义了多种窗口装饰,installDecor()源代码


    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setIsRootNamespace(true);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
 
            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
            if (mTitleView != null) {
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                    View titleContainer = findViewById(com.android.internal.R.id.title_container);
                    if (titleContainer != null) {
                        titleContainer.setVisibility(View.GONE);
                    } else {
                        mTitleView.setVisibility(View.GONE);
                    }
                    if (mContentParent instanceof FrameLayout) {
                        ((FrameLayout)mContentParent).setForeground(null);
                    }
                } else {
                    mTitleView.setText(mTitle);
                }
            }
        }
    }

使用generateDecor()创建一个DecorView对象,并赋值给mDecor变量。该变量并不完全等同于窗口

修饰,窗口修饰是mDecor内部的唯一一个子视图。


根据用户指定的参数选择不同的窗口修饰,并把窗口修饰作为mDecor的子窗口,这是在

generateLayout()中调用mDecor.addView()完成的。




不同窗口的修饰的区别不大,比如是否有标题栏,是否显示左右进度条等,这些修饰窗口共同的

特点是其内部必须包含一个id=content的FrameLayout,因为内容窗口正是被包含在该FrameLayout

之中。常见的窗口修饰对应的XML文件如下,其路径为frameworks/base/core/res/res/layout。


   R.layout.dialog_title_icons

R.layout.screen_title_icons

R.layout.screen_progress

R.layout.dialog_custom_title

R.layout.screen_custom_title

R.layout.dialog_title

R.layout.screen_title

R.layout.screen_simple


安装完窗口修饰后,就可以把用户界面layout.xml文件添加到窗口修饰中,这是通过在

setContentView()中调用inflate()方法完成的,该方法的第二个参数正是mContentParent

即id=content的FrameLayout。



我们再看一看PhoneWindow.setContentView()源代码


    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }
    }

最后,回调cb.onContentChanged()方法,通知应用程序窗口内容发生了改变,因为从无到

有了。



再来特别说明一点,所谓的“根据用户指定的参数”中“用户指定”两个地方可以指定:


第一个地方是在Activity的onCreate()方法中调用得到当前Window,然后调用requestFeature()指

定。generateLayout()方法中使用getLocalFeature()获取feature值,并根据这些值选择不同的窗口

修饰。


另一个地方是在AndroidMainfest.xml中Activity元素内部使用android:theme="XXX"指定。

generateLayout()方法中使用getWindowStyle()方法获取这些值,调用流程如下:



getWindowStyle():PhoneWindow中generateLayout()

->obtainStyleAttributes():Window中

->getTheme.obtainStyledAttributes():Context中

流程中的最后一个是调用getTheme(),而这正是使用android:theme赋值的结果


"用户指定"generateLayout()方法内部的流程可以总结如下:


7.Android内核 创建窗口的过程(二)_第3张图片





































你可能感兴趣的:(7.Android内核 创建窗口的过程(二))