isValidFragment Android API 19

假如想象 提交于 2019-12-17 08:45:48

问题


When I try my app with Android KitKat I have an error in PreferenceActivity.

Subclasses of PreferenceActivity must override isValidFragment(String) to verify that the Fragment class is valid! com.crbin1.labeltodo.ActivityPreference has not checked if fragment com.crbin1.labeltodo.StockPreferenceFragment is valid

In documentation I find the following explanation

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.

I don't find any example to resolve the problem.


回答1:


Try this... this is how we check validity of fragment.

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



回答2:


Out of pure curiosity, you can also do this as well:

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

;}



回答3:


I found I could grab a copy of my fragment names from my header resource as it was loaded:

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

This way I don't need to remember to update a list of fragments buried in the code if I want to update them.

I had hoped to use getHeaders() and the existing list of headers directly, but it seems the activity is destroyed after onBuildHeaders() and recreated before isValidFragment() is called.

This may be because the Nexus 7 I'm testing on doesn't actually do two-pane preference activities. Hence the need for the static list member as well.




回答4:


This API was added due to a newly discovered vulnerability. Please see http://ibm.co/1bAA8kF or http://ibm.co/IDm2Es

December 10, 2013 "We have recently disclosed a new vulnerability to the Android Security Team. [...] To be more accurate, any App which extended the PreferenceActivity class using an exported activity was automatically vulnerable. A patch has been provided in Android KitKat. If you wondered why your code is now broken, it is due to the Android KitKat patch which requires applications to override the new method, PreferenceActivity.isValidFragment, which has been added to the Android Framework." -- From the first link above




回答5:


Verified with an actual 4.4 device:

(1) if your proguard.cfg file has this line (which many define anyway):

-keep public class com.fullpackage.MyPreferenceFragment

(2) than the most efficient implementation would be:

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

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

   }
}



回答6:


I am not sure if lane's implementation is free of the vulnerabilities discussed here but if it is, then i think a better solution would be to avoid using that static list and simply do the following :

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



回答7:


this is my solution:

  • if u need dynamic rebuild headers
  • if u use extras to start preference activity - onBuildHeaders() approach will fail! (with below start intent extras - why ??? - simple because onBuildHeaders() is never called):

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

This is example class:

/**
 * 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;
        }
    }
}



回答8:


Here's my headers_preferences.xml file:

<?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>  

In my PreferencesActivity where the isValidFragment code occurs, I just turned it on its head:

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

As long as I use the AppPreferencesFragment string at the start of all my fragment names, they all validate just fine.




回答9:


my solution (instead of creating ArrayList of class) since the fragments that are loaded suppose to be subclass of PreferenceFragment.class run this check in the @OverRide method

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


来源:https://stackoverflow.com/questions/19973034/isvalidfragment-android-api-19

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!