isValidFragment Android API 19

isValidFragment Android API 19,android,android-fragments,android-4.4-kitkat,Android,Android Fragments,Android 4.4 Kitkat,当我使用Android KitKat尝试我的应用程序时,我在PreferenceActivity中出错 PreferenceActivity的子类必须重写isValidFragment(String),以验证Fragment类是否有效!com.crbin1.labeltodo.ActivityReference未检查片段com.crbin1.labeltodo.StockPreferenceFragment是否有效 在文档中,我找到了以下解释 受保护的布尔值isValidFragment(字符串

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

PreferenceActivity的子类必须重写isValidFragment(String),以验证Fragment类是否有效!com.crbin1.labeltodo.ActivityReference未检查片段com.crbin1.labeltodo.StockPreferenceFragment是否有效

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

受保护的布尔值isValidFragment(字符串碎片名称)

在API级别19中添加

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


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

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

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

使用实际的4.4设备进行验证:

(1) 如果proguard.cfg文件包含以下行():

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

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

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

   }
}

出于好奇,您也可以这样做:

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

;}

此API是由于新发现的漏洞而添加的。请看或

2013年12月10日 “我们最近向Android安全团队披露了一个新的漏洞。[…]更准确地说,任何使用导出的活动扩展PreferenceActivity类的应用程序都会自动受到攻击。Android KitKat中提供了一个修补程序。如果您想知道为什么代码现在会被破坏,那是因为Android KitKat修补程序要求应用程序覆盖新方法PreferenceActivity.isValidFragment、 已经添加到Android框架中。”——来自上面的第一个链接


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

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);
    }
}
公共类MyActivity扩展了PreferenceActivity
{
私有静态列表片段=新的ArrayList();
@凌驾
public void onBuildHeaders(列表目标)
{
loadHeadersFromResource(R.xml.headers,目标);
片段。清除();
用于(标题:目标){
添加(header.fragment);
}
}
...
@凌驾
受保护的布尔值isValidFragment(字符串碎片名称)
{
返回fragments.contains(fragmentName);
}
}
这样,如果我想更新代码中的片段列表,我就不需要记住更新它们

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


这可能是因为我正在测试的Nexus7实际上没有进行两个窗格的首选项活动。因此也需要静态列表成员。

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

 @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;
    }
@覆盖
受保护的布尔值isValidFragment(字符串碎片名称)
{
ArrayList target=新的ArrayList();
loadHeadersFromResource(R.xml.pref_头,目标);
用于(标题h:目标){
if(fragmentName.equals(h.fragment))返回true;
}
返回false;
}
这是我的解决方案:

  • 如果你需要动态重建头
  • 如果您使用extras启动首选项活动-onBuildHeaders()方法将失败!(使用以下启动意图附加项-为什么?-很简单,因为从未调用onBuildHeaders()):

    Intent.putExtra(PreferenceActivity.EXTRA\u SHOW\u FRAGMEN,Fragment.class.getName()); Intent.putExtra(PreferenceActivity.EXTRA\u无\u头,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;
        }
    }
}
/**
*首选项标题,用于将设置和添加视图显示为平板电脑的两个面板
*对于ActionBar,我们需要覆盖onCreate和setContentView
*/
公共类设置PreferenceActivity扩展了PreferenceActivity{
/**有效的片段列表声明*/
私有列表validFragmentList;
/**用于举行用户会话的一些示例无关类*/
SessionManager_SessionManager;
@凌驾
public void onBuildHeaders(列表目标){
/**从res加载标题*/
loadHeadersFromResource(getValidResId(),目标);
}
/**
*由于新发现的漏洞,添加了此API方法。
*/
@凌驾
受保护的布尔值isValidFragment(字符串碎片名称){
列表标题=新建ArrayList();
/**填充碎片列表*/
tryObtainValidFragmentList(getValidResId(),标头);
/**检查id是否有效*/
返回validFragmentList.contains(fragmentName);
}
/**尝试填充有效片段的列表*/
私有void tryObtainValidFragmentList(int-resourceId,List-target){
/**检查空值*/
if(validFragmentList==null){
/**初始化*/
validFragmentList=新的ArrayList();
}否则{
/**清楚的*/
validFragmentList.clear();
}
/**将标题加载到列表*/
loadHeadersFromResource(资源ID,目标);
/**将标题类名称设置为列表*/
用于(标题:目标){
/**填满*/
validFragmentList.add(header.fragment);
}
}
/**获取有效的res id以生成标头*/
私有int getValidResId(){
/**获取会话管理器*/
_sessionManager=sessionManager.getInstance();
/**检查用户是否被授权*/
if(_sessionManager.getCurrentUser().getWebPart().isAuthorized()){
/**如果是返回完整首选项标题*/
返回R.xml.settings\u preferences\u header\u logged\u;
}否则{
/**否则返回短标题*/
返回R.xml.settings\u preferences\u header\u logged\u;
}
}
}
以下是
/**
 * 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;
        }
    }
}
<?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>  
@Override
protected boolean isValidFragment(String fragmentName)
{
  //  return AppPreferencesFragment.class.getName().contains(fragmentName);
    return fragmentName.contains (AppPreferencesFragment.class.getName());
}
@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;
}
@Override
protected boolean isValidFragment (String fragmentName) {
    for (Class<?> cls : ImePreferences.class.getDeclaredClasses()) {
        if (cls.getName().equals(fragmentName)){return true;}
    }
    return false;
}