问题
For some application on which I was working, for devices with API level 19 I'm getting exception
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.
Then, I found out that for those applications android frameworks protected boolean isValidFragment(String fragmentName) is getting called, which has code
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;
}
Then I tried to replicate the error
I took my sample app's code from Preferences Activity Example
and added line <uses-sdk android:targetSdkVersion="19" /> in manifest.
But strangely, I'm not getting the error(isValidFragment() not getting called in that case).
So please tell me how to replicate that error in my sample app.
回答1:
Seems to be a bug or a 4.4 security restriction. Workaraound is to use anything below 19 that is still compatible with PreferenceActivity, and bite the bullet for compiling with an older target.
I am using the headers "pattern" for the PreferenceActivity (overriding public void onBuildHeaders(List<Header> target)), and I assume the OP is too, most likely being the place where stuff happens and crashes.
In my case, I have narrowed this exception to <uses-sdk android:targetSdkVersion="19" />, and anything in [14-18] build targets will compile and run without issues.
Suggestion (for Eclipse): I never messed directly messed with such stuff, but I'm assuming if you compile your PreferenceActivity (and maybe fragments) on a different project, targeting 18 or under (pun not intended :O ), and then using that project as a library for your main project targeting KitKat (19), perhaps you can avoid the crash scenario at run-time while still using the features you need from the latest build (as long as those features aren't in the build-18-bound PreferenceActivity). If this does not succeed, try with that project in jar form (pre-compiled) instead of using project as library.
UPDATE: also take note of Camille Sévigny's answer. If the issue has anything to do with that other question (50% chance IMHO), all apps targeting API 18 are vulnerable to fragment injection attacks (see his linked question).
回答2:
The answer to your question is in this post. This is a duplicate question:
isValidFragment Android API 19
--Updated--
Here is what the solution is:
Basically, whichever Activity is using your fragment "com...$" in the error above, you must update it with the fix below. You should update all the Activities in your project with this fix for any Acitvity that uses a Fragment.
The documentation states:
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.
You can fix this error by overriding this method to the Activity/FragmentActivity:
@Override
protected boolean isValidFragment (String fragmentName) {
return [YOUR_FRAGMENT_NAME_HERE].class.getName().equals(fragmentName);
}
If you are being lazy and just want to test out if this fix works before coding all your fragments into this method, you can simply return true without any checking:
@Override
protected boolean isValidFragment (String fragmentName) {
return true;
}
I had the same issues when testing on the emulator and this was the solution.
回答3:
Here you go!
Slap this in there, and you're good!
Collect all the inner classes found in this PreferenceActivity. I chose to put the list in a static field variable:
public class whatever extends PreferenceActivity {
static final Class<?>[] INNER_CLASSES =
whatever.class.getDeclaredClasses();
Then, override the method, ValidFragment, and ensure the fragment about to be displayed is one the 'parent' activity is aware of:
/**
* 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;
}
来源:https://stackoverflow.com/questions/20954072/when-androids-isvalidfragment-from-preferenceactivity-gets-called