Activity销毁重建 ActionBar IllegalStateException

一、问题来源
二、原因分析
  1.getSupportActionBar()
  2.setSupportActionBar()
  3.完整过程图
三、解决方法


问题来源

给Activity在代码中设置主题theme,然后Activity被销毁重建了。先看主体代码:


/*=====NoActionBarActivity.java=====*/
public class NoActionBarActivity extends AppCompatActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setTheme(R.style.No_Actionbar);
        Toolbar toolbar = (Toolbar) getView().findViewById(R.id.home_page_toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(" ");
    }
    //省略代码...
}
 

/*=====Style.xml=====*/
<style name="No_Actionbar" parent="Theme.AppCompat.Light">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="android:windowBackground">@drawable/bg_actionmode</item>
</style>

正常运行都没问题,但是当Activity被销毁重建时(模拟 Activity销毁重建 方法)就出现crash了,报错 java.lang.IllegalStateException: This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.

原因分析

当Activity设置ActionBar被销毁重建时,主要涉及AppCompatDelegateImplV9调用getSupportActionBar()setSupportActionBar()两个方法的过程
1.getSupportActionBar()

/*=====AppCompatDelegateImplBase.java=====*/
@Override
public ActionBar getSupportActionBar() {
    // The Action Bar should be lazily created as hasActionBar
    // could change after onCreate
    initWindowDecorActionBar();   //***(1)***
    return mActionBar;
}

 
/*=====AppCompatDelegateImplV9.java=====*/
@Override
public void initWindowDecorActionBar() {
    ensureSubDecor();    //***(2)***
    if (!mHasActionBar || mActionBar != null) {   //***(6)***
        return;
    }

    if (mOriginalWindowCallback instanceof Activity) {
        mActionBar = new WindowDecorActionBar((Activity) mOriginalWindowCallback,
                mOverlayActionBar);
    } else if (mOriginalWindowCallback instanceof Dialog) {
        mActionBar = new WindowDecorActionBar((Dialog) mOriginalWindowCallback);
    }
    if (mActionBar != null) {
        mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
    }
}
 
private void ensureSubDecor() {
    if (!mSubDecorInstalled) {
        mSubDecor = createSubDecor();   //***(3)***

        // If a title was set before we installed the decor, propagate it now
        CharSequence title = getTitle();
        if (!TextUtils.isEmpty(title)) {
            onTitleChanged(title);
        }
       //省略代码...
    }
}
 
private ViewGroup createSubDecor() {
    TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);

    if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
        a.recycle();
        throw new IllegalStateException(
                "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
    }

    if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {  //***(4)***
        requestWindowFeature(Window.FEATURE_NO_TITLE);
    } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {  //***(5)***
        // Don't allow an action bar if there is no title.
        requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
    }
    if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
        requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
    }
    if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
        requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
    }
   //省略代码..
}

首先,会从(1) initWindowDecorActionBar() —> (2) ensureSubDecor() —> (3) createSubDecor();
然后,根据主题theme中设置的值给requestWindowFeature()传不同的参数,在theme中设置了<item name="windowActionBar">false</item>时,正常情况下会走(4)requestWindowFeature(Window.FEATURE_NO_TITLE),但销毁重建时就会走(5)requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR)。为什么会走到(5)去呢?大致原因就是销毁重建getSupportActionBar()时,还没有走到代码中设置theme的地方(就是上面NoActionBarActivity.java中的setTheme方法),于是createSubDecor()获取出来的theme就为默认的了。代码中No_Actionbar主题的父类是Theme.AppCompat.Light,默认带有Actionbar,所以走到了(5)传入参数FEATURE_SUPPORT_ACTION_BAR;
关键就在调用requestWindowFeature()方法这里:

最后,回到initWindowDecorActionBar()中的(6),当mHasActionBar为true时就不会return,而是继续往下走,于是就创建了mActionBar。
2.setSupportActionBar()
调用完getSupportActionBar()后会进行setSupportActionBar(),crash异常也就是在这个里面抛出的:

/*=====AppCompatDelegateImplV9.java=====*/
@Override
public void setSupportActionBar(Toolbar toolbar) {
    if (!(mOriginalWindowCallback instanceof Activity)) {
        // Only Activities support custom Action Bars
        return;
    }

    final ActionBar ab = getSupportActionBar();  //***(1)***
    if (ab instanceof WindowDecorActionBar) {
        throw new IllegalStateException("This Activity already has an action bar supplied " +   //***(2)***
                "by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set " +
                "windowActionBar to false in your theme to use a Toolbar instead.");
    }

    // If we reach here then we're setting a new action bar
    // First clear out the MenuInflater to make sure that it is valid for the new Action Bar
    mMenuInflater = null;

    // If we have an action bar currently, destroy it
    if (ab != null) {
        ab.onDestroy();
    }

    if (toolbar != null) {
        final ToolbarActionBar tbab = new ToolbarActionBar(toolbar,
                ((Activity) mOriginalWindowCallback).getTitle(), mAppCompatWindowCallback);
        mActionBar = tbab;
        mWindow.setCallback(tbab.getWrappedWindowCallback());
    } else {
        mActionBar = null;
        // Re-set the original window callback since we may have already set a Toolbar wrapper
        mWindow.setCallback(mAppCompatWindowCallback);
    }

    invalidateOptionsMenu();
}

由上面知道,在get销毁重建时在getSupportActionBar()中会创建一个ActionBar出来,也就是在(1)出获取的ActionBar不为null了,于是就走到了(2)中,抛出了异常。
3.完整过程图

解决方法

1.在AndroidManifest.xml中为Activity设置theme,不在代码中动态设置
2.代码动态设置theme时,在super.onCreate()前面设置theme

/*=====NoActionBarActivity.java=====*/
public class NoActionBarActivity extends AppCompatActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.No_Actionbar);   //修改setTheme()在super.onCreate()之前
        super.onCreate(savedInstanceState);
        Toolbar toolbar = (Toolbar) getView().findViewById(R.id.home_page_toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(" ");
    }
    //省略代码...
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容