isValidFragment Android API 19

时间:2013-11-14 08:41:04

标签: android android-fragments android-4.4-kitkat

当我使用Android KitKat尝试我的应用时,我在PreferenceActivity中出错。

  

PreferenceActivity的子类必须覆盖isValidFragment(String)以验证Fragment类是否有效! com.crbin1.labeltodo.ActivityPreference尚未检查片段com.crbin1.labeltodo.StockPreferenceFragment是否有效

在文档中,我找到以下解释

  

protected boolean isValidFragment(String fragmentName)

     

在API级别19中添加

     

子类应覆盖此方法并验证给定片段是否要附加到此活动的有效类型。对于为Android构建的应用程序,默认实现返回true:比KITKAT更早的targetSdkVersion。对于更高版本,它将引发异常。

我找不到任何解决问题的例子。

10 个答案:

答案 0 :(得分:65)

试试这个...这就是我们检查片段有效性的方法。

protected boolean isValidFragment(String fragmentName) {
  return StockPreferenceFragment.class.getName().equals(fragmentName);
}

答案 1 :(得分:24)

出于纯粹的好奇心,你也可以这样做:

@Override
protected boolean isValidFragment(String fragmentName) {
    return MyPreferenceFragmentA.class.getName().equals(fragmentName)
            || MyPreferenceFragmentB.class.getName().equals(fragmentName)
            || // ... Finish with your last fragment.

;}

答案 2 :(得分:19)

我发现我可以在加载时从头文件资源中获取我的片段名称的副本:

public class MyActivity extends PreferenceActivity
{
    private static List<String> fragments = new ArrayList<String>();

    @Override
    public void onBuildHeaders(List<Header> target)
    {
        loadHeadersFromResource(R.xml.headers,target);
        fragments.clear();
        for (Header header : target) {
            fragments.add(header.fragment);
        }
    }
...
    @Override
    protected boolean isValidFragment(String fragmentName)
    {
        return fragments.contains(fragmentName);
    }
}

这样我就不需要记得更新代码中隐藏的片段列表,如果我想更新它们。

我原本希望直接使用getHeaders()和现有的标题列表,但似乎活动在onBuildHeaders()之后销毁,并在调用isValidFragment()之前重新创建。

这可能是因为我正在测试的Nexus 7实际上并没有进行双窗格偏好活动。因此也需要静态列表成员。

答案 3 :(得分:18)

由于新发现的漏洞,此API已添加。请参阅http://ibm.co/1bAA8kFhttp://ibm.co/IDm2Es

  

2013年12月10日   “我们最近披露了一个针对Android安全团队的新漏洞。[...]为了更准确,任何使用导出活动扩展PreferenceActivity类的App都会自动受到攻击.Android KitKat中提供了一个补丁。如果你想知道为什么你的代码现在坏了,这是由于Android KitKat补丁要求应用程序覆盖新方法PreferenceActivity.isValidFragment,它已被添加到Android Framework中。 - 从上面的第一个链接

答案 4 :(得分:3)

使用实际的4.4设备验证:

(1)如果你的proguard.cfg文件有这一行(which many define anyway):

-keep public class com.fullpackage.MyPreferenceFragment

(2)最有效的实施方式是:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class EditPreferencesHC extends PreferenceActivity {
...
   protected boolean isValidFragment (String fragmentName) {

     return "com.fullpackage.MyPreferenceFragment".equals(fragmentName);

   }
}

答案 5 :(得分:3)

我不确定lane的实现是否没有讨论here的漏洞,但如果是,那么我认为更好的解决方案是避免使用该静态列表并简单地执行以下操作:

 @Override
    protected boolean isValidFragment(String fragmentName)
    {
        ArrayList<Header> target = new ArrayList<>();
        loadHeadersFromResource(R.xml.pref_headers, target);
        for (Header h : target) {
            if (fragmentName.equals(h.fragment)) return true;
        }
        return false;
    }

答案 6 :(得分:0)

这是我的解决方案:

  • 如果你需要动态重建标题
  • 如果您使用extras来启动首选项活动 - onBuildHeaders()方法将失败! (以下开始意图附加 - 为什么??? - 简单因为onBuildHeaders()永远不会被调用):

    Intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMEN,Fragment.class.getName()); Intent.putExtra(PreferenceActivity.EXTRA_NO_HEADERS,true);

这是示例类:

/**
 * Preference Header for showing settings and add view as two panels for tablets
 * for ActionBar we need override onCreate and setContentView
 */
public class SettingsPreferenceActivity extends PreferenceActivity {

    /** valid fragment list declaration */
    private List<String> validFragmentList;

    /** some example irrelevant class for holding user session  */
    SessionManager _sessionManager;

    @Override
    public void onBuildHeaders(List<Header> target) {
        /** load header from res */
        loadHeadersFromResource(getValidResId(), target);
    }

    /**
     * this API method was added due to a newly discovered vulnerability.
     */
    @Override
    protected boolean isValidFragment(String fragmentName) {
        List<Header> headers = new ArrayList<>();
        /** fill fragments list */
        tryObtainValidFragmentList(getValidResId(), headers);
        /** check  id valid */
        return validFragmentList.contains(fragmentName);
    }

    /** try fill list of valid fragments */
    private void tryObtainValidFragmentList(int resourceId, List<Header> target) {  
        /** check for null */
        if(validFragmentList==null) {
            /** init */
            validFragmentList = new ArrayList();
        } else {
            /** clear */
            validFragmentList.clear();
        }
        /** load headers to list */
        loadHeadersFromResource(resourceId, target);
        /** set headers class names to list */
        for (Header header : target) {
            /** fill */
            validFragmentList.add(header.fragment);
        }
    }

    /** obtain valid res id to build headers */
    private int getValidResId() {
        /** get session manager */
        _sessionManager = SessionManager.getInstance();
        /** check if user is authorized */
        if (_sessionManager.getCurrentUser().getWebPart().isAuthorized()) {
            /** if is return full preferences header */
            return R.xml.settings_preferences_header_logged_in;
        } else {
            /** else return short header */
            return R.xml.settings_preferences_header_logged_out;
        }
    }
}

答案 7 :(得分:0)

这是我的headers_preferences.xml文件:

<?xml version="1.0" encoding="utf-8"?>  
<preference-headers  
xmlns:android="http://schemas.android.com/apk/res/android">  

    <header  

        android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs1Fragment"  
        android:title="Change Your Name" />  

    <header  
        android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs2Fragment"  
        android:title="Change Your Group''s Name" />  

    <header  
        android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs3Fragment"  
        android:title="Change Map View" />  

</preference-headers>  

在我的PreferencesActivity中出现了isValidFragment代码,我只是把它转过来了:

@Override
protected boolean isValidFragment(String fragmentName)
{
  //  return AppPreferencesFragment.class.getName().contains(fragmentName);
    return fragmentName.contains (AppPreferencesFragment.class.getName());
}

只要我在所有片段名称的开头使用AppPreferencesFragment字符串,它们都可以正常验证。

答案 8 :(得分:0)

我的解决方案(而不是创建类的ArrayList),因为加载的片段假设是PreferenceFragment.class的子类,运行此检查 @OverRide方法

@Override
protected boolean isValidFragment(String fragmentName) {
    try {
        Class cls = Class.forName(fragmentName);
        return (cls.getSuperclass().equals(PreferenceFragment.class));
                                  // true if superclass is PreferenceFragmnet
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return false;
}

答案 9 :(得分:0)

@Override
protected boolean isValidFragment (String fragmentName) {
    for (Class<?> cls : ImePreferences.class.getDeclaredClasses()) {
        if (cls.getName().equals(fragmentName)){return true;}
    }
    return false;
}