Android源码剖析之------常见Window的创建过程

来源:互联网 发布:期货数据分析 编辑:程序博客网 时间:2024/04/25 17:12

上一篇分析完了Android Window的内部机制,现在,结合源码中具体的Window应用场景,分析一下常见Window的创建过程。

我们平时所使用的View是Android中视图的呈现方式,但是View是不能单独存在的,它必须附着在Window这样一个抽象概念之上。所以,有View的地方就有Window。比如Activity、Dialog、Toast、PopupWindow以及菜单等等。

下面具体讲讲Android中常见Window的使用场景。

Activity的Window创建过程

Activity的启动过程很复杂,不是我们这里介绍的重点,这里我们只需要知道,最终Activity的启动由ActivityThread.performLaunchActivity()完成Activity的启动。在实例化Activity之后,会调用

activity.attach(appContext, this, getInstrumentation(), r.token,                    r.ident, app, r.intent, r.activityInfo, title, r.parent,                    r.embeddedID, r.lastNonConfigurationInstances, config,                    r.voiceInteractor);

在这个方法里,会为Activity创建Window(这里的Window是对Android用户界面的抽象,和WindowManagerService实际管理的Window不是一个概念),并为其设置回调

下面是attch()方法的代码片段

 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);    //创建activity的window,其实创建的是一个PhoneWindow    mWindow = PolicyManager.makeNewWindow(this);    //设置回调,Activity实现了Window.CallBack    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的创建,可以看到,它的创建是通过 PolicyManager.makeNewWindow(this)实现的。andriod在这一点上采用了策略模式。

public static Window makeNewWindow(Context context) {    return sPolicy.makeNewWindow(context);}

可以看到最终是通过sPolicy.makeNewWindow(context)完成创建的,而sPolicy的定义是这样的

private static final IPolicy sPolicy;

它是一个IPolicy类型。

public interface IPolicy {public Window makeNewWindow(Context context);public LayoutInflater makeNewLayoutInflater(Context context);public WindowManagerPolicy makeNewWindowManager();public FallbackEventHandler makeNewFallbackEventHandler(Context context);

}

它是采用静态初始化进行实例化的

static {    // Pull in the actual implementation of the policy at run-time    try {        Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);        sPolicy = (IPolicy)policyClass.newInstance();    } catch (ClassNotFoundException ex) {        throw new RuntimeException(                POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);    } catch (InstantiationException ex) {        throw new RuntimeException(                POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);    } catch (IllegalAccessException ex) {        throw new RuntimeException(                POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);    }}

继续看下去:

private static final String POLICY_IMPL_CLASS_NAME =    "com.android.internal.policy.impl.Policy";

通过这个常量,我们看到,最终的实现类是com.android.internal.policy.impl.Policy

找到Policy类

public Window makeNewWindow(Context context) {    return new PhoneWindow(context);}

可以看到mWindow最终的实例是一个PhoneWindow

public class PhoneWindow extends Window implements MenuBuilder.Callback

而PhoneWindow正好是Window的唯一实现类。所以Activity的Window是一个PhoneWindow

到此,我们已经知道了Activity的Window对象时如何创建的,其实创建的是Activity的View的一个包装类,具体的View是怎么创建Window的还没有开始。

在我们编写Activity的时候,通常需要调用setContView()方法,设置布局,不妨看看这个方法:

 public void setContentView(View view) {    getWindow().setContentView(view);    initWindowDecorActionBar();}

关键这这里: getWindow().setContentView(view);

由于Window的setContentView()是一个抽象方法

 public abstract void setContentView(View view);

所以,这里调用的是PhoneWindow的实现,如下:

 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) {        //创建DecorView        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);    }    final Callback cb = getCallback();    if (cb != null && !isDestroyed()) {        //回调onContentChanged        cb.onContentChanged();    }}

这个方法主要完成3方面的内容:

  1. 如果没有DecorView,那么就创建它

可以看到,首先会调用installDecor(),创建DecorView,它正是我们的手机界面,包括标题栏和内容栏,是PhoneWindow的私有类,本身是一个FrameLayout,installDecor()的代码片段如下:

 if (mDecor == null) {        //实例化DecorView        mDecor = generateDecor();        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);        mDecor.setIsRootNamespace(true);        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);        }    }    if (mContentParent == null) {        //生成DecorView的ContentView        mContentParent = generateLayout(mDecor);

它首先会实例化DecorView,然后获取mContentParent的布局

再看看generateLayout(mDecor):

//获得DecorView里的contentViewViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);......return contentParent;

而ID_ANDROID_CONTENT的值正是com.android.internal.R.id.content,这就是我们setContView的对象,也就是我们的内容区域

2.将View添加到DecorView的内容区域mContentParent中

mLayoutInflater.inflate(layoutResID, mContentParent);

这样,布局就加载到内容区域了。

3.回调Activity的onContentChanged()通知Activity视图已经发生改变。

这个在Activity里是一个空实现,子类可以选择性的实现。

到此为止,Activity的View彻底创建完毕,剩下的工作就是将View和Window建立联系,这是通过 Activity.handleResumeActivity()来完成的。

最终会导致Activity.makeVisible()被调用:

 if (r.activity.mVisibleFromClient) {                r.activity.makeVisible();  }

实现如下:

void makeVisible() {    if (!mWindowAdded) {        ViewManager wm = getWindowManager();        wm.addView(mDecor, getWindow().getAttributes());        mWindowAdded = true;    }    mDecor.setVisibility(View.VISIBLE);}

最终将DecorView和Window建立了联系,从而能够接收用户的输入。

类似的,Dialog、Toast、PopupWindow的Window的创建过程也是类似的。了解这些原理,我们就可以自定义出一些特殊的效果,从根本原理上了解View和Window建立联系的过程。比如,不同风格的PopupWindow等。总之,view和Window的关联是通过WindowManager完成的,关于其内部实现机制,在《Wind内部机制》也已经介绍过。

0 0
原创粉丝点击