Android Activity之Window的创建过程

本文详细解析了Android中Activity启动过程、Window创建机制及其与Activity的关系。深入探讨了PhoneWindow的创建、DecorView的初始化及内容填充过程,以及Window如何最终显示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 Activity的创建概述

  • 通常,使用startActivity(intent)来启动一个Activity,或者当需要返回一个结果的时候我们可能会调用startActivityForResult(intent, requestCode)方法。但不管使用那个方法,都最终会startActivityForResult(intent, requestCode, options) 来启动Activity。 注意,当requestCode<0时,会忽略请求结果。

  • 启动Activity是一个较为复杂的过程,涉及到跨进程与ActivityManagerService(简称AMS)的交互,AMS在收到启动一个新Activity的时候,会进行一系列的处理(诸如ActivityRecord的创建、权限校验,堆栈变换暂停前一个Activity等操作),之后会跨进程回调ActivityThread的ApplicationThread(一个Binder对象)的scheduleLaunchActivity,而该方法会向ActivityThread的消息队列发送一个启动Activity的消息,接着启动Activity的任务就交给ActivityThread的handleLaunchActivity方法了。从该方法起,就是Activity真正开始创建的时候了。

  • Activity的创建也伴随着Window的创建,并建立起Activity与Window的联系,之后当Window有变化时,会通知Activity进行相应的变化。系统默认为Activity创建的Window是PhoneWindow,在其之上,会创建DecorView(也就是窗体视图的承载者,并通过addView的方式嵌入各种需要展示的视图),之后会通过WindowManager的addView方法来添加窗体视图,绘制DecorView并向WindowManageService注册相关window信息及回调,这样当Activity在onResume被调用之后视图显示出来了。通常我们通过setContentView来设置的视图,会被填充到DecorView的一块被称作ContentParent的子View中。


二、Window创建

在大概了解了Activity的创建过程后,我们就可以来接入创建Window的部分内容。实际上,Window是一个比较抽象的概念,它的具体表征是具体的View,并通过WindowManager来进行管理。这部分原理内容可以参考 安卓学习笔记之理解Window与WindowManager

在上面说到的handleLaunchActivity方法如下,这个方法主要做了如下工作

  • 1.WindowManagerGlobal.initialize()做Window创建的初始化准备,主要是用来拿到- WindowManagerService的引用
  • 2.用了performLaunchActivity来真正完成Activity的创建,完成Window的创建及onCreate\onStart等生命周期方法的调用。
  • 3.调用handleResumeActivity来调用Activity的onResume方法,并显示其视图
  • 4.处理Activity启动错误后的处理。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }

        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);

        if (localLOGV) Slog.v(
            TAG, "Handling launch of " + r);

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

            //...
            }
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

接下来看performLaunchActivity方法的实现,首先拿到Activity一些组件信息,然后通过Instrumentation反射创建Activity,接着会创建Application、Context上下文对象等,之后会调用Activity的attach来完成Window的创建等工作,最后会回调onCreate、onStart等相关生命周期方法。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent){

        ActivityInfo aInfo = r.activityInfo;
       // 省略,拿到相关package、组件等信息

        // 反射来创建Activity
        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) {
           ...
        }

        try {  // LoadedApk创建Application对象
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (activity != null) {   // 创建上下文对象,实际创建的是ContextImpl对象
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }

                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                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);

                ...
                activity.mCalled = false;
                    if (r.isPersistable()) { //  调用OnCreate
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
               ...
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart(); //  调用onStart方法
                    r.stopped = false;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {  // 调用OnRestoreInstanceState方法
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
              ....
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } 
        ...

        return activity;
    }

接下来重点看Activity的attach方法,这个方法主要完成了如下工作:

  • 1.Window的创建,实际上创建的是PhoneWindow,然后给其设置各种回调,来建立起与Activity的关联
  • 2.给Activity初始化各种参数,如mUiThread等
  • 3.给PhoneWindow设置WindowManager,实际上设置的是WindowManagerImpl:
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*/);
        // 创建的Window是PhoneWindow
        mWindow = new PhoneWindow(this, window); 
        mWindow.setWindowControllerCallback(this); // 设置各种Callback
        mWindow.setCallback(this);  // this即activity,存在Window的mCallback中
        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(); // 主线程为Ui线程

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        ...
        mWindow.setWindowManager( // 设置WindowManager,实际上设置的是WindowManagerImpl
                (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;
    }

粗略看看设置的回调有哪些(省略了部分),有一些是我们比较常见的,诸如onAttachedToWindow:

public interface Callback {
        ...

        public boolean onMenuOpened(int featureId, Menu menu);

        public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);

        public void onContentChanged();

        public void onWindowFocusChanged(boolean hasFocus);

        public void onAttachedToWindow();

        public void onDetachedFromWindow();

        public boolean dispatchTouchEvent(MotionEvent event);

         ...
    }

到此,表面上Window已经创建完了,但实际上它还只是一个空架子,等着我们去给它填充内容。实际上,我们在Activity的onCreate方法中通过setContentView会触发DecorView的创建,并将需要显示的视图添加到了DecorView,之后会通过WindowManager来将DecorView添加到Window,这样Window才有了真正的意义。

如下是Activity的setContentView方法,getWindow()返回的实际上是上面创建的PhoneWindow,也就是它会调用PhoneWindow的setContentView,在该方法中会创建DecorView并完成布局视图的填充。

public void setContentView(View view, ViewGroup.LayoutParams params) {
        getWindow().setContentView(view, params);
        initWindowDecorActionBar();
    }

PhoneWindow的setContentView方法如下,做了如下几件事:

  • 1 如果所需的DecorView不存在则创建之,否则移除其中的mContentParent中所有的View
  • 2 将我们的View填充到mContentParent中
  • 3 回调Activity来通知改变
@Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        // 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();  // 创建DecorView
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            mContentParent.addView(view, params); //我们的布局被填充到了mContentParent中
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback(); // 得到的是mCallback,实际上是设置回调传入的activity引用
        if (cb != null && !isDestroyed()) {  // 最后还会回调Activity来通知content改变
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;

三、Window内容的填充 — DecorView的创建

在Window创建后,还需要给它填充视图。从performLaunchActivity方法返回,在其之后又调用了handleResumeActivity方法,该方法来完成DecorView的添加以及显示,DecorView实际上是一个继承自FrameLayout的ViewGroup。
DecorView的创建这里需要考虑两种情况:

  • 1.我们在Activity的onCreate方法中通过setContentView设置了视图
  • 2.我们没有为Activity设置视图。

这两种情况的区别在于对DecorView创建的影响,如果不设置setContentView,那么可能会延迟到onResume创建DecorView,并且Content内容会无视图填充。不管是那种情况都会调用导致DecorView的创建,不过只会创建一次,也就是前面已经创建了就直接使用。

可以看到,在handleResumeActivity方法有如下代码,getDecorView会试图获取DecorView,如果之前我们没有setContentView,则会导致DecorView的创建,当创建完成后会通过WindowManager添加到窗体。

 if (r.window == null && !a.mFinished && willBeVisible) {
       r.window = r.activity.getWindow(); // 获取的Window为PhoneWindow
        View decor = r.window.getDecorView(); // 获取DecorView
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
      ...

}

PhoneWindow的getDecorView方法如下,当没有创建DecorView时,会调用installDecor方法来进行创建(如同setContentView一样,都会调用installDecor):

@Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

installDecor方法如下,主要做了以下几件事:

  • generateDecor方法,用来创建DecorView
  • generateLayout方法, 得到内容父布局,即setContentView填充的目的地
  • 设置title等样式:
private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);  // 创建DecorView
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this); // 建立DecorView与Window的关联
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor); // 得到内容父布局

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);

            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
                mDecorContentParent.setWindowCallback(getCallback());
                if (mDecorContentParent.getTitle() == null) {
                    mDecorContentParent.setWindowTitle(mTitle);
                }

                final int localFeatures = getLocalFeatures();
                for (int i = 0; i < FEATURE_MAX; i++) {
                    if ((localFeatures & (1 << i)) != 0) {
                        mDecorContentParent.initFeature(i);
                    }
                }

                mDecorContentParent.setUiOptions(mUiOptions);

              ...
            } else {
                mTitleView = (TextView) findViewById(R.id.title);  // title部分
                if (mTitleView != null) {
                    // 是否隐藏title
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        final View titleContainer = findViewById(R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        mContentParent.setForeground(null);
                    } else {
                        mTitleView.setText(mTitle);
                    }
                }
            }

            if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
                mDecor.setBackgroundFallback(mBackgroundFallbackResource);
            }

            // Only inflate or create a new TransitionManager if the caller hasn't
            // already set a custom one.
             ...
            }
        }
    }

generateDecor方法如下,比较简单主要用来创建DecorView:

protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());

接下来看看generateLayout方法做了哪些事:

  • 样式主题设置的请求值,存储到了mLocalFeatures中
  • DecorView的布局初始化
  • 获取contentParent
  • 设置设置背景,图标,title等
 protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();

        ...

        // 省略样式主题设置等无关代码
        // 举一例常见
         if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);  // 请求无title,设置到了mLocalFeatures中
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }

        // Inflate the window decor. 

        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        // 选择合适的布局来创建DecorView的视图
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
         ...
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();
        // 真正的去解析布局
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        // 获取contentParent 
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }

        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            registerSwipeCallbacks();
        }

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        //设置背景,图标,title等,,,

        mDecor.finishChanging();

        return contentParent;
    }

DecorView的onResourcesLoaded方法是真正去解析其布局的,在这之前会经过主题样式进行大量的判断来选择合适的布局,以 R.layout.screen_simple这个资源(在frameworks\base\core\res\res\layout下)为例,可以简单的分为bar和content两个区域,也就是我们常见的标题和内容区域:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

接下来看看onResourcesLoaded方法的实现,这个方法会解析上面选择出来的layoutResource ,并生成一个mContentRoot 出来,它就是DecorView中被填充的根布局了:

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        mStackId = getStackId();

        if (mBackdropFrameRenderer != null) {
            loadBackgroundDrawablesIfNeeded();
            mBackdropFrameRenderer.onResourcesLoaded(
                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                    getCurrentColor(mNavigationColorViewState));
        }

        mDecorCaptionView = createDecorCaptionView(inflater);
        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));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }

用一张图简单的示例一下创建的布局,当然布局不仅仅是这一种情况

DecorView布局层级

到这里,DecorView就已经初始化完毕了,如果我们设置了setContentView,则会将该视图填充到DecorView中的mContentParent 所代表的布局了。


四、Window的显示

到现在,DecorView中已经填充了相应的视图,不过还没显示出来,需要调用WindowManager的addView方法将其添加到Window,然后它才能显示出来,在handleResumeActivity方法有如下代码:

if (r.window == null && !a.mFinished && willBeVisible) {
             r.window = r.activity.getWindow();
             View decor = r.window.getDecorView();
             decor.setVisibility(View.INVISIBLE);  // 设置了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了,由于设置了INVISIBLE暂时还未显示出来,系统需要判断是否应该要显示,当要显示的时候,会走如下代码:

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

通过调用Activity的makeVisible方法来完成最终的显示,方法如下:

void makeVisible() {
        if (!mWindowAdded) { // 没有添加则会完成添加
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

到此,Window才真正的被显示了出来,整个流程就走完了。其中涉及到WindowManager的addView的相关内容在另一篇博客 安卓学习笔记之理解Window与WindowManager已有介绍,不再赘述了。

五、总结

  • 1 Activity在其attach方法中创建了Window,实际上创建的是PhoneWindow,并通过各种回调等建立起与Activity的联系,我们在Activity中使用getWindow()所得到的即是这个创建的Window。
  • 2 在PhoneWindow中装载了一个顶级的View,即DecorView,它实际是一个FrameLayout,并通过各种主题样式选择加载不同的视图来填充DecorView,这部分被称作mContentRoot。
  • 3 setContentView设置我们自己需要展示的视图,这部分视图被填充到了DecorView的一块子ViewGroup中,这块子ViewGroup被称作contentParent。
  • 4 DecorView最终是通过WindowManager的addView方法添加到Window的,并且最终在Activity的onResume方法执行后显示出来。
  • 5 在使用requestWindowFeature来设置样式时,实际上是调用了PhoneWindow的requestFeature方法,会将样式存储在Window的mLocalFeatures变量中,当installDecor时,会应用这些样式。也就是说,当需要通过requestWindowFeature来请求样式时,应该在setContentView方法之前调用,因为setContentView方法的调用会导致DecorView的创建并应用样式,如果在之后调用则会导致不会生效,因为此时DecorView已经创建完成了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值