49

Android KitKat でアプリを試すと、PreferenceActivity でエラーが発生します。

Fragment クラスが有効であることを確認するには、PreferenceActivity のサブクラスで isValidFragment(String) をオーバーライドする必要があります。com.crbin1.labeltodo.ActivityPreference は、フラグメント com.crbin1.labeltodo.StockPreferenceFragment が有効かどうかをチェックしていません

ドキュメントには、次の説明があります

protected boolean isValidFragment (文字列フラグメント名)

API レベル 19 で追加

サブクラスはこのメソッドをオーバーライドし、指定されたフラグメントがこのアクティビティに関連付けられる有効な型であることを確認する必要があります。デフォルトの実装は、KITKAT より古い android:targetSdkVersion 用にビルドされたアプリに対して true を返します。それ以降のバージョンでは、例外がスローされます。

問題を解決する例が見つかりません。

4

10 に答える 10

24

純粋な好奇心から、これも同様に実行できます。

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

;}
于 2013-12-10T12:37:12.010 に答える
18

この API は、新たに発見された脆弱性のために追加されました。http://ibm.co/1bAA8kFまたはhttp://ibm.co/IDm2Esを参照してください。

2013 年 12 月 10 日 「最近、Android セキュリティ チームに新しい脆弱性を開示しました。[...] より正確には、エクスポートされたアクティビティを使用して PreferenceActivity クラスを拡張したアプリは、自動的に脆弱でした。Android でパッチが提供されました。 KitKat.なぜあなたのコードが今壊れているのか疑問に思っているなら、それはアプリケーションが Android フレームワークに追加された新しいメソッド PreferenceActivity.isValidFragment をオーバーライドすることを要求する Android KitKat パッチによるものです." -- 上記の最初のリンクから

于 2013-12-10T20:19:51.850 に答える
3

実際の 4.4 デバイスで検証:

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

   }
}
于 2013-11-22T23:07:45.540 に答える
3

レーンの実装にここで説明した脆弱性がないかどうかはわかりませんが、そうである場合は、その静的リストの使用を避け、単に次のことを行うことがより良い解決策になると思います:

 @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;
    }
于 2015-09-25T14:56:41.160 に答える
0

ここに私の 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());
}

すべてのフラグメント名の先頭に AppPreferencesFragment 文字列を使用している限り、それらはすべて正常に検証されます。

于 2016-09-21T01:09:44.883 に答える
0

これが私の解決策です:

  • 動的再構築ヘッダーが必要な場合
  • エクストラを使用して設定アクティビティを開始すると、 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;
        }
    }
}
于 2016-01-14T18:27:03.783 に答える
0
@Override
protected boolean isValidFragment (String fragmentName) {
    for (Class<?> cls : ImePreferences.class.getDeclaredClasses()) {
        if (cls.getName().equals(fragmentName)){return true;}
    }
    return false;
}
于 2021-02-22T09:07:39.567 に答える