Android开发中遇到的requestFeature() must be called before adding content异常

缘起

    上一篇博文中讲到了几种实现全屏显示Activity内容的方法。然而实际在实现中发现了一些问题,在本篇博文中进行总结下。首先交代一下开发环境,本人使用的是Android Studio 1.5.1,因此使用Eclipse ADT开发或者低版本的SDK的时候可能不会碰到这个问题。首先看onCreate()方法中的实现代码:

1 @Override
2 protected void onCreate(Bundle savedInstanceState) {    
3     super.onCreate(savedInstanceState);
4     requestWindowFeature(Window.FEATURE_NO_TITLE);
5     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
6     setContentView(R.layout.activity_main);
7 }    

    非常简单的两行代码,然而运行代码的时候应用却直接奔溃。在Android Studio中观察应用的奔溃信息,只看到一条简单的消息:threadid=1: thread exiting with uncaught exception (group=0x419f6c50)。根本无从得知哪里出的错误,因为代码本来就少,才这么两行。于是就在网上搜了一下AS的调试方法,总结了一下Android Studio中捕获异常的方法。

Android Studio捕获异常方案一

    我们知道Java语言提供了try-catch机制来捕获运行时异常。因此想到,我们在排查Android运行时异常时是否也可以利用起try-catch这个工具呢?加起来就试试好了:

     再次在模拟器中运行应用,可以在logcat中输出如下信息:

     这时候已经可以看到具体的异常信息了:requestFeature() must be called before adding content。已经达到了我们想要的结果,但是这个方法有个缺点:就是得估计异常大致出现在什么地方,这才好用try-catch包裹它。至于这个异常代表着什么,现在先不说,再来看看第二种异常捕获方案好了。

Android Studio 捕获异常方案二

    这种方案是从网上看来的,利用了Therad的一个静态方法,首先定义一个Thread.UncaughtExceptionHandler的实例,然后在程序中设置为未捕获异常的默认处理器:

 1 private final Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() {
 2     public void uncaughtException(Thread thread, Throwable ex) {
 3         Log.e("TestApplication", "Uncaught exception is: ", ex);
 4         // log it
 5     }
 6 };
 7 
 8 @Override
 9 protected void onCreate(Bundle savedInstanceState) {
10     Thread.setDefaultUncaughtExceptionHandler(handler);
11 
12     super.onCreate(savedInstanceState);
13     requestWindowFeature(Window.FEATURE_NO_TITLE);
14     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
15     setContentView(R.layout.activity_main);
16 }

    这种方案只需要一个Thread.UncaughtExceptionHandler的实例即可,不需要估计异常发生的大致位置。得到的输出信息如下:

    异常信息也是非常的明了,能够看出异常抛出的堆栈信息,从而更快的跟踪定位Bug的所在。那么这个异常到底说明了什么呢?看字面意思是,requestWindowFeature()方法必须在添加视图之前先调用。可是以前也是这么用的啊,也没见出现过这种异常。于是又搜索了一番才在StackOverflow上发现了解决方案。简单的来说就是将requestWindowFeature()放到第一行调用。为什么呢?关键原因在于,我在Android Studio 1.5里面新建的工程Activity默认是继承自AppCompatActivity类。在那篇帖子里面提到了一些解决方法:(1)要么把基类从AppCompatActivity(或者ActionBarActivity)改成Activity。这样就可以不用将requestWindowFeature放到第一行了。(2)或者是使用supportRequestWindowFeature()方法代替requestWindowFeature()方法,这样也不用放到第一行去调用。这三种方法随意一种都可以解决问题。至于原因,帖子里面也是众说纷纭,不好解释。不过大致的原因,是由于Google为Android提供的兼容包导致的问题。

ActionBarActivity和AppCompatActivity的关系

     在StackOverflow的那篇帖子中,有提到一个已经被Google废弃的基类ActionBarActivity。这个类在现在的SDK中已经被废弃使用了,从源代码来看,ActionBarActivity现在就是继承自AppCompatActivity的一个空类,紧紧是为了向下兼容考虑。Google已经建议开发者逐步使用ToolBar来代替以前版本中的ActionBar了,因此废弃ActionBarActivity,在新版本中使用AppCompatActivity做基类也是情理之中的事情了。

    那么,AppCompatActivity新在哪里呢?根据文档说明,AppCompatActivity是一个设计实现的更通用的类,内部使用了AppCompatDelegate作为逻辑实现核心。这个delegate的存在,是为了更好的贯彻Google推行的Material Design的设计理念。有时你可能想在一个旧版本的Activity(既不是继承自ActionBarActivity又不是继承AppCompatActivity的类)中使用Material Design的组件。此时,这个delegate能够很好的满足这个要求。只要在这个旧式Activity中实现AppCompatDelegate的相关方法,然后重写旧式Activity中的addContentView()、setContentView()等方法,并在这些方法中回调AppCompatDelegate中的对应方法,即可为旧式Activity添加具备Material Design风格的视图组件。

参考

posted @ 2015-12-29 22:16  24K纯开源  阅读(2386)  评论(0编辑  收藏  举报