Android KitKatでアプリを試すと、PreferenceActivityでエラーが発生します。
PreferenceActivityのサブクラスは、フラグメントクラスが有効であることを確認するためにisValidFragment(String)をオーバーライドする必要があります! com.crbin1.labeltodo.ActivityPreferenceは、フラグメントcom.crbin1.labeltodo.StockPreferenceFragmentが有効かどうかをチェックしていません
ドキュメントには、次の説明があります
protected boolean isValidFragment(String fragmentName)
APIレベル19で追加
サブクラスはこのメソッドをオーバーライドし、指定されたフラグメントがこのアクティビティにアタッチされる有効なタイプであることを確認する必要があります。 KitKatより古いAndroid:targetSdkVersion用にビルドされたアプリの場合、デフォルトの実装はtrueを返します。それ以降のバージョンでは、例外がスローされます。
問題を解決する例が見つかりません。
これを試してください...これがフラグメントの有効性を確認する方法です。
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()
が呼び出される前に再作成されたようです。
これは、テストしているNexus 7が実際に2ペインの設定アクティビティを実行していないためです。したがって、静的リストメンバーも必要です。
このAPIは、新しく発見された脆弱性のために追加されました。 http://ibm.co/1bAA8kF または http://ibm.co/IDm2Es をご覧ください
2013年12月10日「最近、Androidセキュリティチームへの新しい脆弱性を開示しました。[...]より正確に言うと、エクスポートされたアクティビティを使用してPreferenceActivityクラスを拡張したアプリは、自動的に脆弱になります。Android KitKat。コードが壊れている理由がわからない場合は、 Android KitKatパッチにより、アプリケーションは新しいメソッドPreferenceActivity.isValidFragmentをオーバーライドする必要があります。PreferenceActivity.isValidFragmentは、Android Framework。 "-From上記の最初のリンク
実際の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);
}
}
レーンの実装に議論されている脆弱性がないかどうかはわかりません here ですが、そうであれば、その静的リストの使用を避けて、次のようにするだけの方が良いと思います:
@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;
}
これが私の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文字列を使用している限り、すべて正常に検証されます。
ロードされたフラグメントはPreferenceFragment.classのサブクラスであると想定されるため、私のソリューションは(クラスのArrayListを作成する代わりに)@OverRideメソッドでこのチェックを実行します。
@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;
}
これは私の解決策です:
設定アクティビティを開始するためにエクストラを使用する場合-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;
}
}
}