280

複数の引数をFragment取るコンストラクターがあります。私のアプリは開発中は正常に動作しましたが、本番環境ではユーザーに次のクラッシュが発生することがあります。

android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment 
make sure class name exists, is public, and has an empty constructor that is public

このエラーメッセージが示すように、空のコンストラクターを作成することもできますが、それ以降は、別のメソッドを呼び出しての設定を完了する必要がありFragmentます。

なぜこのクラッシュがたまにしか起こらないのか知りたいです。多分私はViewPager間違って使用していますか?すべてを自分でインスタンス化し、Fragment内のリストに保存しますActivityFragmentManager私が見た例ではトランザクションをViewPager必要とせず、開発中にすべてが機能しているように見えたため、トランザクションは使用しません。

4

5 に答える 5

366

はい、彼らはやる。

とにかく、コンストラクターを実際にオーバーライドするべきではありません。newInstance()静的メソッドを定義し、引数を介してパラメーターを渡す必要があります(バンドル)

例えば:

public static final MyFragment newInstance(int title, String message) {
    MyFragment f = new MyFragment();
    Bundle bdl = new Bundle(2);
    bdl.putInt(EXTRA_TITLE, title);
    bdl.putString(EXTRA_MESSAGE, message);
    f.setArguments(bdl);
    return f;
}

そしてもちろん、この方法で引数を取得します。

@Override
public void onCreate(Bundle savedInstanceState) {
    title = getArguments().getInt(EXTRA_TITLE);
    message = getArguments().getString(EXTRA_MESSAGE);

    //...
    //etc
    //...
}

次に、フラグメントマネージャから次のようにインスタンス化します。

@Override
public void onCreate(Bundle savedInstanceState) {
    if (savedInstanceState == null){
        getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.content, MyFragment.newInstance(
                R.string.alert_title,
                "Oh no, an error occurred!")
            )
            .commit();
    }
}

このように、デタッチおよび再アタッチされた場合、オブジェクトの状態は引数を介して保存できます。インテントに添付されたバンドルによく似ています。

理由-追加の読書

なぜだろうと思っている人のために、その理由を説明しようと思いました。

チェックした場合:https ://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Fragment.java

instantiate(..)クラス内のメソッドがメソッドをFragment呼び出すことがわかりますnewInstance

public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
    try {
        Class<?> clazz = sClassMap.get(fname);
        if (clazz == null) {
            // Class not found in the cache, see if it's real, and try to add it
            clazz = context.getClassLoader().loadClass(fname);
            if (!Fragment.class.isAssignableFrom(clazz)) {
                throw new InstantiationException("Trying to instantiate a class " + fname
                        + " that is not a Fragment", new ClassCastException());
            }
            sClassMap.put(fname, clazz);
        }
        Fragment f = (Fragment) clazz.getConstructor().newInstance();
        if (args != null) {
            args.setClassLoader(f.getClass().getClassLoader());
            f.setArguments(args);
        }
        return f;
    } catch (ClassNotFoundException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (java.lang.InstantiationException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (IllegalAccessException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (NoSuchMethodException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": could not find Fragment constructor", e);
    } catch (InvocationTargetException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": calling Fragment constructor caused an exception", e);
    }
}

http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#newInstance()publicインスタンス化時に、アクセサが存在すること、およびそのクラスローダーがアクセサへのアクセスを許可していることを確認する理由を説明します。

これは全体としてかなり厄介な方法ですが、状態を使用しFragmentMangerて強制終了して再作成することができFragmentsます。(Androidサブシステムはと同様のことを行いActivitiesます)。

クラスの例

電話についてよく聞かれnewInstanceます。これをクラスメソッドと混同しないでください。このクラス全体の例は、使用法を示しているはずです。

/**
 * Created by chris on 21/11/2013
 */
public class StationInfoAccessibilityFragment extends BaseFragment implements JourneyProviderListener {

    public static final StationInfoAccessibilityFragment newInstance(String crsCode) {
        StationInfoAccessibilityFragment fragment = new StationInfoAccessibilityFragment();

        final Bundle args = new Bundle(1);
        args.putString(EXTRA_CRS_CODE, crsCode);
        fragment.setArguments(args);

        return fragment;
    }

    // Views
    LinearLayout mLinearLayout;

    /**
     * Layout Inflater
     */
    private LayoutInflater mInflater;
    /**
     * Station Crs Code
     */
    private String mCrsCode;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCrsCode = getArguments().getString(EXTRA_CRS_CODE);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mInflater = inflater;
        return inflater.inflate(R.layout.fragment_station_accessibility, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mLinearLayout = (LinearLayout)view.findViewBy(R.id.station_info_accessibility_linear);
        //Do stuff
    }

    @Override
    public void onResume() {
        super.onResume();
        getActivity().getSupportActionBar().setTitle(R.string.station_info_access_mobility_title);
    }

    // Other methods etc...
}
于 2012-05-04T14:10:04.263 に答える
18

CommonsWareがこの質問https://stackoverflow.com/a/16064418/1319061で指摘しているように、匿名クラスはコンストラクターを持つことができないため、フラグメントの匿名サブクラスを作成している場合にもこのエラーが発生する可能性があります。

フラグメントの匿名のサブクラスを作成しないでください:-)

于 2013-06-24T13:38:27.243 に答える
8

はい、ご覧のとおり、support-packageはフラグメントもインスタンス化します(フラグメントが破棄されて再度開かれた場合)。これはフレームワークによって呼び出されているものであるため、サブクラスFragmentにはパブリックな空のコンストラクターが必要です。

于 2012-05-04T14:04:05.323 に答える
0

公式ドキュメントをご覧ください:フラグメント:https ://developer.android.com/reference/android/app/Fragment

Fragmentのすべてのサブクラスには、引数のないパブリックコンストラクターが含まれている必要があります。フレームワークは、必要に応じて、特に状態の復元中にフラグメントクラスを再インスタンス化することが多く、インスタンス化するためにこのコンストラクターを見つけることができる必要があります。引数なしのコンストラクターが使用できない場合、状態の復元中に実行時例外が発生する場合があります。

于 2022-01-20T22:43:01.253 に答える
-8

これが私の簡単な解決策です:

1-フラグメントを定義します

public class MyFragment extends Fragment {

    private String parameter;

    public MyFragment() {
    }

    public void setParameter(String parameter) {
        this.parameter = parameter;
    } 
}

2-新しいフラグメントを作成し、パラメーターを設定します

    myfragment = new MyFragment();
    myfragment.setParameter("here the value of my parameter");

3-楽しんでください!

もちろん、パラメータのタイプと数を変更することができます。早くて簡単。

于 2016-04-05T07:47:16.350 に答える