টুকরা কি সত্যিই একটি খালি নির্মাণকারীর প্রয়োজন?


258

আমার কাছে একটি 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

আমি জানতে আগ্রহী যে কেন এই ক্রাশটি কেবল মাঝে মধ্যে ঘটে only আমি কি ViewPagerভুলভাবে ব্যবহার করছি ? আমি Fragmentনিজেই সমস্ত গুলি ইনস্ট্যান্ট করি এবং এগুলির ভিতরে থাকা একটি তালিকায় সেভ করি Activity। আমি FragmentManagerলেনদেন ব্যবহার করি না , যেহেতু ViewPagerআমি যে উদাহরণগুলি দেখেছি তার প্রয়োজন হয় নি এবং সমস্ত কিছু বিকাশের সময় কাজ করছে বলে মনে হয়।


22
অ্যান্ড্রয়েডের কয়েকটি সংস্করণে (কমপক্ষে আইসিএস), আপনি সেটিংস -> বিকাশকারী বিকল্পগুলিতে যেতে পারেন এবং "ক্রিয়াকলাপগুলি রাখবেন না" সক্ষম করতে পারেন। এটি করার ফলে আপনাকে কেস-নন-আরগ নির্মাতারা প্রয়োজনীয় সেখানে পরীক্ষা করার একটি নিয়ামক পদ্ধতি দেবে।
কিথ

আমার এই একই সমস্যা ছিল। আমি সদস্য ভেরিয়েবলের পরিবর্তে বান্ডেল ডেটা বরাদ্দ করছিলাম (একটি অ-ডিফল্ট কর্টর ব্যবহার করে)। আমি যখন অ্যাপটি মেরেছিলাম তখন আমার প্রোগ্রামটি ক্রাশ হয় নি it এটি তখনই ঘটছিল যখন সময়সূচী আমার অ্যাপটিকে ব্যাক বার্নারে "স্থান বাঁচাতে" রাখবে। আমি এটি যেভাবে আবিষ্কার করেছি তা হ'ল টাস্ক এমগ্রিতে গিয়ে এবং আরও অনেক টন অন্যান্য অ্যাপ্লিকেশন খোলার মাধ্যমে, তারপরে আমার অ্যাপ্লিকেশনটি পুনরায় খোলার মাধ্যমে। এটি প্রতিবার ক্রাশ হয়েছে। আমি যখন ক্রিস জেনকিন্স উত্তরটি বান্ডিল আরোগুলি ব্যবহার করার জন্য ব্যবহার করেছি তখন বিষয়টি সমাধান হয়ে গেছে।
উইজেড

: আপনি এই থ্রেড আগ্রহী হতে পারে stackoverflow.com/questions/15519214/...
স্টিফান Haustein

5
ভবিষ্যতের পাঠকদের জন্য একটি পার্শ্ব নোট: যদি আপনার Fragmentসাবক্লাসটি কোনও নির্মাতাকে মোটেই ঘোষণা না করে, তবে ডিফল্টরূপে খালি পাবলিক কনস্ট্রাক্টর স্পষ্টতই আপনার জন্য তৈরি করা হবে (এটি স্ট্যান্ডার্ড জাভা আচরণ )। আপনি যদি অন্য নির্মাণকারী (যেমন তর্কযুক্ত উদাহরণস্বরূপ) ঘোষণা না করেন তবে আপনাকে স্পষ্টভাবে একটি খালি কনস্ট্রাক্টর ঘোষণা করতে হবে না
টনি চান

আমি কেবল উল্লেখ করব যে অন্তত 14.1 সংস্করণের জন্য ইন্টেলিজ আইডিইএ আপনাকে একটি সতর্কতা সরবরাহ করে যা আপনাকে এই সত্যটি সতর্ক করে যে আপনার কোনও খণ্ডে কোনও অ-ডিফল্ট নির্মাতা না থাকা উচিত।
রেনিপেট 21

উত্তর:


348

হ্যাঁ তারা করে.

আপনার কোনওভাবেই কনস্ট্রাক্টরকে ওভাররাইড করা উচিত নয়। আপনার একটি 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/Fraament.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এবং সেই শ্রেণীর লোডার এতে প্রবেশ করতে দেয় access

এটি সর্বোপরি একটি সুন্দর বাজে পদ্ধতি, তবে এটি রাষ্ট্রগুলিকে FragmentMangerহত্যা এবং পুনরায় তৈরি করার অনুমতি দেয় Fragments। (অ্যান্ড্রয়েড সাবসিস্টেম এর সাথে একই রকম কাজ করে 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...
}

2
আপনি যদি কার্যকলাপটি বিরতি দেন বা এটি ধ্বংস করেন destroy সুতরাং আপনি হোম স্ক্রিনে যান এবং ক্রিয়াকলাপটি তখন অ্যান্ড্রয়েডের মাধ্যমে রুম সংরক্ষণের জন্য হত্যা করা হয়। টুকরো টুকরো রাষ্ট্র সংরক্ষণ করা হবে (আরগগুলি ব্যবহার করে) তারপরে বস্তুটি (সাধারণত)। তাই ক্রিয়াকলাপে ফিরে যাওয়ার সময় টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো করে সংরক্ষণ করা অবস্থা, নতুন ডিফল্ট () এরপরে অনক্রিট ইত্যাদি ব্যবহার করে পুনরায় তৈরি করার চেষ্টা করা উচিত ... এছাড়াও যদি কার্যকলাপটি সংস্থানগুলি সংরক্ষণ করার চেষ্টা করছে (লো মেম ফোন) এটি কেবলমাত্র বিরতিপ্রাপ্ত অবজেক্টগুলিকে সরিয়ে ফেলতে পারে .. কমন্সগুই আরও ভাল ব্যাখ্যা করতে সক্ষম হওয়া উচিত। সংক্ষেপে আপনি জানেন না! :)
ক্রিস। জেনকিন্স

1
@ ম্যামকি সত্যিই যদি আপনার প্রচুর অবজেক্টস / মডেলগুলির প্রয়োজন হয় তবে আপনাকে সেগুলি একটি ডেটাবেস বা কনটেন্টপ্রোভাইডার থেকে অবিচ্ছিন্নভাবে গ্রহন করা উচিত।
ক্রিস। জেনকিনস

1
@ ক্রিস। জেনকিনস দুঃখিত, আমি যদি পরিষ্কার ছিল না ... আমার বক্তব্যটি ছিল ক্রিয়াকলাপগুলির বিপরীতে, খণ্ডগুলি এটিকে এতটা পরিষ্কার করে না যে ডেটা পাসিং / ভাগ করে নেওয়ার জন্য নির্মাণকারীদের অবশ্যই ব্যবহার করা উচিত নয়। এবং ডাম্পিং / পুনরুদ্ধার করা ঠিকঠাক হলেও, আমি বিশ্বাস করি যে ধ্বংসগুলি পুনরায় অর্জন করতে পারে তার চেয়ে বেশ কয়েকটি ডেটা অনুলিপি রাখা আরও বেশি স্মৃতি গ্রহণ করতে পারে। কিছু ক্ষেত্রে, ক্রিয়াকলাপ / সংকলনগুলির সংগ্রহকে ইউনিট হিসাবে বিবেচনা করা, সম্পূর্ণরূপে ধ্বংস করা বা একেবারে না করার বিকল্পটি ব্যবহার করা কার্যকর হতে পারে - তবে আমরা কনস্ট্রাক্টরের মাধ্যমে ডেটা পাস করতে পারি । আপাতত, এই সমস্যাটি সম্পর্কে, খালি নির্মাণকারীর একমাত্র এটি।
কায়ে

3
আপনি ডেটা একাধিক অনুলিপি রাখা হবে কেন? বান্ডিলস | পার্সেবলযোগ্য যখন রাজ্য / টুকরা / ক্রিয়াকলাপের মধ্যে থাকতে পারে তখন মেমরির রেফারেন্সটি পাস করে (এটি আসলে কিছু অদ্ভুত রাষ্ট্রীয় সমস্যার কারণ হয়ে দাঁড়ায়), পার্সেলেবল কেবলমাত্র কার্যকরভাবে "ডুপ্লিকেট" ডেটা প্রক্রিয়া এবং পূর্ণ আজীবনের মধ্যে থাকে। উদাহরণস্বরূপ, যদি আপনি আপনার ক্রিয়াকলাপ থেকে কোনও টুকরো টুকরো টুকরো করেন তবে আপনার পাসিং রেফারেন্সটি ক্লোন নয়। আপনার একমাত্র আসল অতিরিক্ত ওভারহেড হ'ল অতিরিক্ত টুকরা অবজেক্ট।
ক্রিস। জেনকিনস

1
@ ক্রিস। জেনকিনস ওয়েল, তখন, পার্সেবল সম্পর্কে আমার অজ্ঞতা ছিল। পার্সেবলের সংক্ষিপ্ত জাভাদোক এবং পার্সেলের একটি অংশ "পুনর্গঠন" শব্দের অতীত না পড়ে, আমি "অ্যাক্টিভ অবজেক্টস" অংশে পৌঁছিনি, এই সিদ্ধান্তে পৌঁছে যে এটি মাত্র একটি নিম্ন-স্তরের আরও অনুকূলিত, তবে কম বহুমুখী সিরিয়ালযোগ্য। আমি এরপরে লজ্জা ও বিড়বিড় করে বলছি "তবুও অবিচ্ছেদ্য ভাগ করে নেওয়া এবং পার্সেবলগুলি তৈরি করা বিরক্তিকর হতে পারে" :)
কায়ে

17

এই প্রশ্নটি https://stackoverflow.com/a/16064418/1319061 তে CommonsWare দ্বারা উল্লিখিত হিসাবে , বেনামী শ্রেণীর কনস্ট্রাক্টর না থাকতে পারার কারণে আপনি যদি কোনও খণ্ডের একটি বেনামী সাবক্লাস তৈরি করছেন তবে এই ত্রুটিটিও ঘটতে পারে।

টুকরা :-) এর বেনামে সাবক্লাস করবেন না


1
অথবা, কমনসওয়্যার সেই পোস্টে উল্লিখিত হিসাবে, নিশ্চিত করুন যে আপনি এই ত্রুটিটি এড়াতে কোনও অভ্যন্তরীণ ক্রিয়াকলাপ / অংশ / পুনরুদ্ধারকারীকে "স্থিতিশীল" হিসাবে ঘোষণা করেছেন।
টনি উইকহাম

6

হ্যাঁ, আপনি দেখতে পাচ্ছেন সমর্থন-প্যাকেজটি খণ্ডগুলিও ইনস্ট্যান্ট করে তোলে (যখন তারা ধ্বংস হয়ে যায় এবং পুনরায় খোলা হয়)। আপনার Fragmentসাবক্লাসগুলির একটি সার্বজনীন খালি নির্মাণকারীর প্রয়োজন কারণ এটি ফ্রেমওয়ার্ক দ্বারা ডাকা হচ্ছে।


খালি খণ্ড কনস্ট্রাক্টরকে সুপার () কনস্ট্রাক্টর কল করা উচিত নাকি? আমি এটি জিজ্ঞাসা করছি যেহেতু আমি খালি পাবলিক কনস্ট্রাক্টর বাধ্যতামূলক। যদি সুপার () কল করা খালি পাবলিক কন্সট্রাক্টরদের জন্য
অর্থপূর্ণ না হয়

@ টিএনআর হিসাবে সমস্ত ফাংশন বিমূর্তিগুলির একটি খালি নির্মাতা নিরর্থক হবে, কারণ পিতামাত্ত super()শ্রেণি খালি পাবলিক কনস্ট্রাক্টর বিধি ভঙ্গ করেছে। সুতরাং না super()আপনার নিজের নির্মাণকারীর ভিতরে যাওয়ার দরকার নেই ।
ক্রিস। জেনকিনস

4
প্রকৃতপক্ষে কোনও খণ্ডে খালি নির্মাতাকে স্পষ্টভাবে সংজ্ঞায়িত করার প্রয়োজন নেই। প্রতিটি জাভা ক্লাসেই যাই হোক না কেন একটি অন্তর্নিহিত ডিফল্ট নির্মাতা রয়েছে। থেকে নেওয়া: ডকস.ওরকল / জাভাস / টিউটোরিয়াল / জাভা / জাজাও / কনস্ট্রাক্টরস html ~ "সংকলক স্বয়ংক্রিয়ভাবে বিনা বিনা কোনও শ্রেণীর জন্য একটি নো-আর্গুমেন্ট, ডিফল্ট কনস্ট্রাক্টর সরবরাহ করে" "
ইগোরগানাপলস্কি

-6

এখানে আমার সহজ সমাধান:

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 - এটি উপভোগ করুন!

স্পষ্টতই আপনি টাইপ এবং পরামিতিগুলির সংখ্যা পরিবর্তন করতে পারেন। দ্রুত এবং সহজ।


5
এটি যদিও সিস্টেম দ্বারা খণ্ডের পুনরায় লোড পরিচালনা করতে পারে না।
বিদিয়া
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.