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风格的视图组件。
参考
- http://stackoverflow.com/questions/29797172/whats-the-enhancement-of-appcompatactivity-over-actionbaractivity
- http://stackoverflow.com/questions/16939814/android-util-androidruntimeexception-requestfeature-must-be-called-before-add
- DeepLearning.ai学习笔记(二)改善深层神经网络:超参数调试、正则化以及优化--week3 超参数调试、Batch正则化和程序框架
- DeepLearning.ai学习笔记(一)神经网络和深度学习--Week3浅层神经网络
- Andrew Ng机器学习课程笔记--week3(逻辑回归&正则化参数)
- Andrew Ng机器学习课程笔记--week1(机器学习介绍及线性回归)
- C++学习笔记之模板篇
- 计算机基础之计算机硬件系统
- 迭代器和生成器
- 函数相关知识汇总
- python编码问题一点通
- 数据类型总结(二)(列表,元组,字典)
- 数据类型总结(一)(数字,字符串)
- while补充,字符串和数字的内置方法
- Python的if判断与while循环
- python基础之数据类型与变量
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Conda | 轻松安装生信工具
- R语言绘图 | 气泡矩阵图
- 在测试自动化中使用Java枚举
- 前端|初学vue
- 使用clusterProfiler对非模式生物进行富集分析
- 微信小程序|逻辑判断
- R语言绘图 | 给气泡矩阵图上个色
- 前端|创建简单动态时钟
- R语言做几何布朗运动的模拟:复杂金融产品的几何布朗运动的模拟
- 手把手教你使用Flask搭建ES搜索引擎(实战篇)
- 【基础篇】Python+Go——带大家一起另寻途径提高计算性能
- 转录组分析 | 使用Trimmomatic过滤Fastq文件
- 转录组分析 | 使用FastQC进行数据质控
- R语言绘图 | 给箱线图加个点
- R语言绘图 | 使用pheatmap快速绘制热图