Android 5.0使用android:onClick属性出现崩溃的原因及解决方案
来源:互联网 发布:阐释者淘宝 编辑:程序博客网 时间:2024/05/29 03:46
问题及表现
在项目中,对Button设置点击事件监听时,大多数情况下还是习惯使用setOnClickListener设置监听,但是最近发现当在布局文件中同时使用了android:theme和android:onClick属性时,在响应点击事件时程序会发生crash,发生Crash的设备为Android 5.0及以上(7.0未测试),不限机型。在Android 5.0和Android 6.0上发生crash时Log信息不一致。
Android 6.0 Crash信息如下:
Android 5.0 Crash信息如下:
原因
如果去掉android:theme属性,则点击事情可以正常响应,并未出现任何崩溃的情况。
查找资料发现,从Android 5.0开始,支持对单独的View设置主题。当在布局文件中设置了主题之后,ContextThemeWrapper 会被指定为View的Context,因此View的Context不再是Activity了,这时候点击事件的回调响应也就不存在了。
实验证明
在使用getContext()获取view的Context时,如果在布局文件中未设置主题,返回值是当前的Activity实例MainActivity@4124。
在使用getContext()获取view的Context时,如果在布局文件中设置了主题,返回值是ContextThemeWrapper,它的成员变量mBase才是当前的Activity,因此在ContextThemeWrapper无法找到”android:onClick”中设置的方法。
源码分析
Android M中代码如下:
private void parseInclude(XmlPullParser parser, Context context, View parent, AttributeSet attrs) throws XmlPullParserException, IOException { int type; if (parent instanceof ViewGroup) { // Apply a theme wrapper, if requested. This is sort of a weird // edge case, since developers think the <include> overwrites // values in the AttributeSet of the included View. So, if the // included View has a theme attribute, we'll need to ignore it. final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); final boolean hasThemeOverride = themeResId != 0; if (hasThemeOverride) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); // If the layout is pointing to a theme attribute, we have to // massage the value to get a resource identifier out of it. int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0); if (layout == 0) { final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null || value.length() <= 0) { throw new InflateException("You must specify a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); } // Attempt to resolve the "?attr/name" string to an identifier. layout = context.getResources().getIdentifier(value.substring(1), null, null); } ... } ... View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); } // Apply a theme wrapper, if allowed and one is specified. if (!ignoreThemeAttr) { final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); if (themeResId != 0) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); } if (name.equals(TAG_1995)) { // Let's party like it's 1995! return new BlinkLayout(context, attrs); }}
Android K中代码如下:
private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs) throws XmlPullParserException, IOException { int type; if (parent instanceof ViewGroup) { final int layout = attrs.getAttributeResourceValue(null, "layout", 0); if (layout == 0) { final String value = attrs.getAttributeValue(null, "layout"); if (value == null) { throw new InflateException("You must specifiy a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); } else { throw new InflateException("You must specifiy a valid layout " + "reference. The layout ID " + value + " is not valid."); } ...}...View createViewFromTag(View parent, String name, AttributeSet attrs) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); } ...}
对比源码可以发现,在M的源码中,多出了如下几号代码
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); final boolean hasThemeOverride = themeResId != 0; if (hasThemeOverride) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle();
如果对布局或者View指定了主题,那么当前的context(activity或fragment或application context)被转换为 ContextThemeWrapper的实例,因此会出现ContextThemeWrapper找不到对应方法的问题。
问题解决方案
如果是对整个Activity设置主题,尽量不要在布局文件中设置,在Manifest配置文件中设置;
如果要求必须在布局文件中设置主题,那么不要使用属性android:onClick,使用setOnClickListener替代。
参考资料:
http://stackoverflow.com/questions/31653126/crash-when-clicking-button-with-custom-theme/31672941#31672941
- Android 5.0使用android:onClick属性出现崩溃的原因及解决方案
- Android 中个别机型运行完崩溃出现UnsatisfiedLinkError 错误的原因及解决方案
- android:onClick 属性的应用
- android:onClick 属性的应用
- [Android]Fragment无法使用android:onClick属性
- Android使用TextView的onClick不触发解决方案
- Android平台程序崩溃的类型及原因列举
- Android平台程序崩溃的类型及原因
- android平台程序崩溃的类型及原因列举
- Android平台程序崩溃的类型及原因列举
- Android平台程序崩溃的类型及原因列举
- android编写UI出现error: Error parsing XML: unbound prefix 的错误原因及解决方案
- 常见Android Native崩溃及错误原因
- 常见Android Native崩溃及错误原因
- 常见Android Native崩溃及错误原因
- Android Native Crash崩溃及错误原因
- 常见Android Native崩溃及错误原因
- 常见Android Native 崩溃及错误原因
- java中的内部类总结
- Mac 安装redis
- SpringMVC整合Shiro
- Java各个集合(Collection)的特性和用途
- Java进程奔溃故障排查
- Android 5.0使用android:onClick属性出现崩溃的原因及解决方案
- nginx反向代理与负载均衡
- GridView基于pulltorefresh实现下拉刷新 上拉加载更多功能
- 如何将服务器上oracle11g数据dump出来导入到本地的oracle10版本
- 统计APK中方法数量
- apple mach-o linker error 报错解决
- 【PAT甲级】1067. Sort with Swap(0,*) (25)
- Android okhttp3 创建Socket的底层实现追踪
- Monocular slam 理论基础(2)