当来自PreferenceActivity的android的isValidFragment()被调用时?

Shi*_*ade 7 sdk android exception android-fragments android-activity

对于我工作的某些应用程序,对于API级别为19的设备,我会遇到异常

Caused by: java.lang.RuntimeException: Subclasses of PreferenceActivity must override isValidFragment(String) to verify that the Fragment class is valid! com... has not checked if fragment com...$. is valid.
Run Code Online (Sandbox Code Playgroud)

然后,我发现对于那些应用程序,android框架protected boolean isValidFragment(String fragmentName)被调用,它有代码

if (getApplicationInfo().targetSdkVersion  >= android.os.Build.VERSION_CODES.KITKAT) {
        throw new RuntimeException(
                "Subclasses of PreferenceActivity must override isValidFragment(String)"
                + " to verify that the Fragment class is valid! " + this.getClass().getName()
                + " has not checked if fragment " + fragmentName + " is valid.");
    } else {
        return true;
}
Run Code Online (Sandbox Code Playgroud)

然后我试图复制错误

我从Preferences Activity Example中获取了示例应用程序的代码, 并<uses-sdk android:targetSdkVersion="19" />在manifest中添加了一行.

但奇怪的是,我没有得到错误(在这种情况下isValidFragment()没有被调用).

所以请告诉我如何在我的示例应用程序中复制该错误.

Cam*_*gny 21

您的问题的答案在这篇文章中.这是一个重复的问题:

isValidFragment Android API 19

- 更新 -

以下是解决方案:

基本上,无论哪个Activity在上面的错误中使用你的片段"com ... $",你必须使用下面的修复更新它.对于使用片段的任何Acitvity,您应该使用此修复程序更新项目中的所有活动.

文件说明:

protected boolean isValidFragment (String fragmentName)

Added in API level 19

Subclasses should override this method and verify that the given fragment is a 
valid type to be attached to this activity. The default implementation returns 
true for apps built for android:targetSdkVersion older than KITKAT. For later 
versions, it will throw an exception.
Run Code Online (Sandbox Code Playgroud)

您可以通过将此方法重写为Activity/FragmentActivity来修复此错误:

@Override
protected boolean isValidFragment (String fragmentName) {
  return [YOUR_FRAGMENT_NAME_HERE].class.getName().equals(fragmentName);
}
Run Code Online (Sandbox Code Playgroud)

如果你是懒惰的,只是想在将所有片段编码到这个方法之前测试这个修复是否有效,你可以简单地返回true而不进行任何检查:

@Override
protected boolean isValidFragment (String fragmentName) {
  return true;
}
Run Code Online (Sandbox Code Playgroud)

在模拟器上进行测试时遇到了同样的问题,这就是解决方案.


leR*_*bot 6

似乎是一个bug或4.4安全限制.Workaraound是使用仍然兼容的19以下的任何东西PreferenceActivity,并咬紧牙关与旧目标进行编译.

我使用标题"模式"作为PreferenceActivity(覆盖public void onBuildHeaders(List<Header> target)),我认为OP也是,很可能是发生事件和崩溃的地方.

在我的情况下,我已经缩小了这个例外<uses-sdk android:targetSdkVersion="19" />,并且[14-18]构建目标中的任何内容都将编译并运行而没有问题.

建议(对于Eclipse):我从来没有直接搞砸这些东西,但是我假设你PreferenceActivity在一个不同的项目中编译你的(也许是片段),目标是18或者不足(双关语:O),然后使用它作为针对KitKat(19)的主项目的库项目,也许你可以在运行时避免崩溃场​​景,同时仍然使用你需要的最新版本的功能(只要这些功能不在build-18中) - 绑定PreferenceActivity).如果这不成功,请尝试以jar形式(预编译)的项目,而不是使用项目作为库.

更新:还要注意CamilleSévigny的回答.如果问题与其他问题有关(恕我直言的可能性为50%),那么针对API 18的所有应用都容易受到片段注入攻击(请参阅他的链接问题).


Dra*_*awn 5

干得好!

在那里拍打它,你很好!

收集此PreferenceActivity中找到的所有内部类.我选择将列表放在静态字段变量中:

public class whatever extends PreferenceActivity {

    static final Class<?>[] INNER_CLASSES = 
                                     whatever.class.getDeclaredClasses();
Run Code Online (Sandbox Code Playgroud)

然后,覆盖方法ValidFragment,并确保要显示的片段是"父"活动知道的片段:

 /**
 *  Google found a 'security vulnerability' and imposed this hack.
 *  Have to check this fragment was actually conceived by this activity.
 */
@Override
protected boolean isValidFragment(String fragmentName) {

    Boolean knownFrag = false;

    for (Class<?> cls : INNER_CLASSES) {

       if ( cls.getName().equals(fragmentName) ){

           knownFrag = true;

           break;
       }
    }           

    return knownFrag;
}
Run Code Online (Sandbox Code Playgroud)