浅谈activity的创建与DecorView的创建加载

来源:互联网 发布:二级域名解析系统源码 编辑:程序博客网 时间:2024/05/22 13:59

前言

在Android开发中,相信接触最多的就是activity以及view,并且很多人都能熟练的运用,但很多人还是会对activity正式创建到view的加载这一流程感到迷惑,本文从源码的角度简单谈谈这一过程

view加载decorview

首先看看view是如何将decorview加载进窗体中的,至于decorview是什么等下在说。在启动一个activity的时候,会调用ActivityThread的handleLaunchActivity方法,看下源码

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {       ......        // Initialize before creating the activity        WindowManagerGlobal.initialize();        Activity a = performLaunchActivity(r, customIntent);  **第一处**        if (a != null) {            r.createdConfig = new Configuration(mConfiguration);            reportSizeConfigurations(r);            Bundle oldState = r.state;            handleResumeActivity(r.token, false, r.isForward,                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);**第二处**              .....    }

在上述源码第一处可以看到通过performLaunchActivity方法创建了一个activity,再来看看方法内部是如何创建activity的

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {       ...         Activity activity = null;        try {            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();            activity = mInstrumentation.newActivity(                    cl, component.getClassName(), r.intent);//**第一点**     ...                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);//**第二点**                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);                }           ....        return activity;    }

我将这个方法里的主要要分析的代码拿出来看看。首先看performLaunchActivity方法里标记的第一点,这里首先会去创建一个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, String referrer, IVoiceInteractor voiceInteractor,            Window window) {        attachBaseContext(context);        mFragments.attachHost(null /*parent*/);        mWindow = new PhoneWindow(this, window);        ......

这里我只截取了方法里的一段代码看看,首先会调用activity的ContextWrapper的attachBaseContext,然后去创建一个window的实例,由于window是个抽象类,这里可以知道phonewindow是window的唯一实现类
然后再看下标记的第三点,点击去看看具体做了什么

 public void callActivityOnCreate(Activity activity, Bundle icicle,            PersistableBundle persistentState) {        prePerformCreate(activity);        activity.performCreate(icicle, persistentState);        postPerformCreate(activity);    }

看到调用了activity的performCreate这个方法,点击去看下

 final void performCreate(Bundle icicle, PersistableBundle persistentState) {        restoreHasCurrentPermissionRequest(icicle);        onCreate(icicle, persistentState);        mActivityTransitionState.readState(icicle);        performCreateCommon();    }

可以看到这里就会调用activity的oncreat方法了。

现在我们再看看handleLaunchActivity方法里我做的第二处标记handleResumeActivity这个方法,看下这里做了什么操作

 final void handleResumeActivity(IBinder token,            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {...... r = performResumeActivity(token, clearHide, reason);//这里调用activity的onresume方法 ..... if (r.window == null && !a.mFinished && willBeVisible) {                r.window = r.activity.getWindow();                View decor = r.window.getDecorView();                decor.setVisibility(View.INVISIBLE);                ViewManager wm = a.getWindowManager();                WindowManager.LayoutParams l = r.window.getAttributes();                a.mDecor = decor;                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;                l.softInputMode |= forwardBit;                if (r.mPreserveWindow) {                    a.mWindowAdded = true;                    r.mPreserveWindow = false;                    // Normally the ViewRoot sets up callbacks with the Activity                    // in addView->ViewRootImpl#setView. If we are instead reusing                    // the decor view we have to notify the view root that the                    // callbacks may have changed.                    ViewRootImpl impl = decor.getViewRootImpl();                    if (impl != null) {                        impl.notifyChildRebuilt();                    }                }                if (a.mVisibleFromClient && !a.mWindowAdded) {                    a.mWindowAdded = true;                    wm.addView(decor, l);//将创建的decorview加载进窗体                }

可以看到在这个方法里获取了window,DecorView及windowmanager对象,然后在最后调用windowmanager的addview方法去addview,实际上是调用WindowManagerImpl的addview方法

 @Override    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {        applyDefaultToken(params);        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);    }

在这里有调用WindowManagerGlobal的addView方法,通过查看该方法的源码找到下面这一段代码

   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) {            // BadTokenException or InvalidDisplayException, clean up.            synchronized (mLock) {                final int index = findViewLocked(view, false);                if (index >= 0) {                    removeViewLocked(index, true);                }            }            throw e;        }

这里可以看到通过调用ViewRootImpl的setview方法将decorview当作参数传递进去,该方法里会通过跨进程的方式向WindowManagerService发起一个调用,从而将DecorView最终添加到Window上具体就不再往下分析了。

总结一下
启动一个activity的时候会调用ActivityThread的handleLaunchActivity方法,在方法里会创建一个activity的实例,并且调用acticity的 attach方法去创建一个phonewindow实例,然后回去调用activity的oncreat方法,最后再去将创建的decorview设置给窗体进行显示。

Decorview的创建

这里要冲在activity的oncreat方法里调用setContentView(R.layout.main)说起,跟踪可以发现activity的setContentView如下

public void setContentView(@LayoutRes int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();    }

根据上面的分析可以知道实际上就是调用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;    }

首先会判断mContentParent 是否为空,为空会调用installDecor方法,至于mContentParent是什么,看下他的解释

  // This is the view in which the window contents are placed. It is either    // mDecor itself, or a child of mDecor where the contents go.    ViewGroup mContentParent;

通过注释知道mContentParent表示的是decorview或者decorview的一个子view,这尼玛是啥意思,还是继续看下去吧

上面提到mContentParent为空会调用installDecor方法,看下这个方法

 private void installDecor() {        mForceDecorInstall = false;        if (mDecor == null) {            mDecor = generateDecor(-1);//**第一点**            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);            mDecor.setIsRootNamespace(true);            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);            }        } else {            mDecor.setWindow(this);        }        if (mContentParent == null) {            mContentParent = generateLayout(mDecor);//**第二点**

首先如果decorview为空的话回去调用第一点标记的generateDecor去创建一个decorview,至于什么是decorview,看下他的解释

// This is the top-level view of the window, containing the window decor.    private DecorView mDecor;

它实际上是一个FrameLayout,表示的是窗体显示的视图的最顶层view
接着看看第二点,点击方法看看内部实现

 protected ViewGroup generateLayout(DecorView decor) { ......   if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);        }  ......   int layoutResource;   int features = getLocalFeatures();   ......   mDecor.startChanging();        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);        if (contentParent == null) {            throw new RuntimeException("Window couldn't find content container view");        }

可以看到首先进行了一系列的主题设置等操作,这也就可以解释为什么requestWindowFeature()要在setContentView之前调用的原因,然后看下mDecor.onResourcesLoaded方法,在这个方法里会调用

final View root = inflater.inflate(layoutResource, null);if (mDecorCaptionView != null) {            if (mDecorCaptionView.getParent() == null) {                addView(mDecorCaptionView,                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));            }            mDecorCaptionView.addView(root,                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));        } else {            // Put it below the color views.            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));        }

可以看到就是将要设置的xml文件add给decorview,然后再generateLayout里将contentParent 返回,这时的contentParent 可能就是decorview,如果设置了不显示title,则contentParent 就是decorview的一个子view,这也就解释了mContentParent表示的是decorview或者decorview的一个子view是什么意思了

总结一下:
decorview是顶级view,包含title和content,而我们调用setcontentview实际上就是将布局设置给content

好了本文大致内容就这些了,这些只是个人看了源码之后的一些认知,不对勿怪啊

原创粉丝点击