美文网首页高级AndroidAndroid工具程序员
为什么手机无法运行应用? Values之谜

为什么手机无法运行应用? Values之谜

作者: SpikeKing | 来源:发表于2017-03-31 10:58 被阅读270次

    欢迎Follow我的GitHub, 关注我的简书.

    Why

    本文的合集已经编著成书,高级Android开发强化实战,欢迎各位读友的建议和指导。在京东即可购买:https://item.jd.com/12385680.html

    Android

    在GitHub上Clone的某开源Android项目, 下载配置, 完成构建, 在手机上可以安装, 但是无法运行. 项目的编译版本(compileSdkVersion)是25(7.1), 最低的兼容版本(minSdkVersion)是19(4.4), 手机的系统版本是21(5.0), 已经满足应用的最低运行条件. 然而, 在相同系统版本(25, 7.1)的模拟机上, 应用运行正常.

    在我的手机运行应用时, 报错如下:

    E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.saulmm.cui/com.saulmm.cui.HomeActivity}: 
    java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
    Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
        at android.support.v7.app.AppCompatDelegateImplV9.createSubDecor(AppCompatDelegateImplV9.java:359)
        at android.support.v7.app.AppCompatDelegateImplV9.ensureSubDecor(AppCompatDelegateImplV9.java:328)
        at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:289)
        at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:140)
        at android.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:276)
        at android.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:261)
        at com.saulmm.cui.HomeActivity.onCreate(HomeActivity.java:42)
    

    定位

    问题起源于DataBindingUtil#setContentView, DataBindingUtil绑定layout布局.

    // HomeActivity.java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_home);
        // ...
    }
    

    调用AppCompatActivity#setContentView, Activity绑定layout布局.

    // DataBindingUtil.java
    public static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId,
            DataBindingComponent bindingComponent) {
        activity.setContentView(layoutId);
        // ...
    }
    

    最终是Activity代理实现类AppCompatDelegateImplV9实现setContentView的具体逻辑. 通过ensureSubDecor方法创建DecorView, 填充Activity的自定义布局resId, ensureSubDecor再调用createSubDecor方法创建DecorView.

    // AppCompatDelegateImplV9.java
    @Override
    public void setContentView(int resId) {
        ensureSubDecor(); // 创建并初始化DecorView
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }
    
    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();
            // ...
        }
    }
    

    createSubDecor方法, 根据应用的样式主题(Theme)设置根布局DecorView的样式, 并执行初始化. 当未含有AppCompatTheme_windowActionBar属性时, 则认为主题未设置, 并抛出异常IllegalStateException.

    // AppCompatDelegateImplV9.java
    // 根据布局样式Style设置根布局DecorView的样式
    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.");
        }
        // ...
    }
    

    为什么API 25的模拟器可以启动, 我的手机(API 21)就不能启动呢? 原因很简单, 就是因为开源项目的主题资源设置有误. 默认主题在AndroidManifesttheme属性中设置.

    <application android:theme="@style/AppTheme">
    

    点击IDE的AppTheme跳转至声明, 发现只有一处, 即在values-v23中声明.

    values-v23

    原因

    对于资源属性而言, 系统默认查找与匹配低于当前API等级的属性, 保证高版本属性不会在低版本中执行. 因为高版本会添加更多的新接口, 低版本无法找到, 强制使用可能导致异常甚至崩溃, 所以禁止访问高版本的属性.

    解决

    理解了问题的所在, 解决方案就非常简单. 为了支持最低API以上的全部系统, 在默认的values/themes.xml中, 添加AppTheme属性即可.

    <style name="AppTheme" parent="Base.AppTheme"/>
    
    values

    问题虽小, 但不可忽视, 否则就只能在某些手机可用, 在某些手机崩溃, 摸不着头脑. 在开发中, 优先在默认values文件夹中添加属性, 如果需要额外支持, 在其他高版本values-vXX中再添加. Do you get it?

    That's all! Enjoy it!

    相关文章

      网友评论

        本文标题:为什么手机无法运行应用? Values之谜

        本文链接:https://www.haomeiwen.com/subject/yuupottx.html