isValidFragment Android API 19

当我尝试与Android KitKat我的应用程序时,PreferenceActivity中有一个错误。

PreferenceActivity的子类必须重写isValidFragment(String)来validationFragment类是有效的! com.crbin1.labeltodo.ActivityPreference没有检查,如果片段com.crbin1.labeltodo.StockPreferenceFragment是有效的

在文档中我find了下面的解释

protected boolean isValidFragment(String fragmentName)

在API级别19中添加

子类应该重写此方法,并validation给定片段是否附加到此活动的有效types。 对于为android:targetSdkVersion构build的应用程序,默认实现将返回true,此值比KITKAT更早。 对于以后的版本,它会抛出一个exception。

我没有find任何解决问题的例子。

试试这个…这是我们如何检查片段的有效性。

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

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

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

我发现我可以从头文件资源抓取我的片段名称的副本,因为它是加载的:

 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()之前重新创build。

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

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

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

使用实际的4.4设备进行validation:

(1)如果你的proguard.cfg文件有这一行( 反正有很多定义 ):

 -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); } } 

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

  @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; } 

这是我的解决scheme:

  • 如果你需要dynamic重build标题
  • 如果你使用额外开始偏好活动 – 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; } } } 

这是我的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> 

在发生isValidFragment代码的PreferencesActivity中,我只是把它翻了个头:

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

只要我在我的所有片段名称的开始使用AppPreferencesFragmentstring,他们都validation就好了。