টুকরা মাইফ্র্যাগমেন্ট ক্রিয়াকলাপের সাথে সংযুক্ত নয়


393

আমি একটি ছোট পরীক্ষা অ্যাপ্লিকেশন তৈরি করেছি যা আমার সমস্যার প্রতিনিধিত্ব করে। আমি (শার্লক) টুকরোগুলি সহ ট্যাবগুলি প্রয়োগ করতে অ্যাকশনবারারলক ব্যবহার করছি।

আমার কোড: TestActivity.java

public class TestActivity extends SherlockFragmentActivity {
    private ActionBar actionBar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setupTabs(savedInstanceState);
    }

    private void setupTabs(Bundle savedInstanceState) {
        actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        addTab1();
        addTab2();
    }

    private void addTab1() {
        Tab tab1 = actionBar.newTab();
        tab1.setTag("1");
        String tabText = "1";
        tab1.setText(tabText);
        tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "1", MyFragment.class));

        actionBar.addTab(tab1);
    }

    private void addTab2() {
        Tab tab1 = actionBar.newTab();
        tab1.setTag("2");
        String tabText = "2";
        tab1.setText(tabText);
        tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "2", MyFragment.class));

        actionBar.addTab(tab1);
    }
}

TabListener.java

public class TabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener {
    private final SherlockFragmentActivity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public TabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    /* The following are each of the ActionBar.TabListener callbacks */

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);

        // Check if the fragment is already initialized
        if (preInitializedFragment == null) {
            // If not, instantiate and add it to the activity
            SherlockFragment mFragment = (SherlockFragment) SherlockFragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            ft.attach(preInitializedFragment);
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);

        if (preInitializedFragment != null) {
            // Detach the fragment, because another one is being attached
            ft.detach(preInitializedFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
        // User selected the already selected tab. Usually do nothing.
    }
}

MyFragment.java

public class MyFragment extends SherlockFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ex) {
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void result){
                getResources().getString(R.string.app_name);
            }

        }.execute();
    }
}

আমি Thread.sleepডেটা ডাউনলোডের অনুকরণে অংশটি যুক্ত করেছি । কোডটি onPostExecuteহল এর ব্যবহার অনুকরণ করা Fragment

আমি যখন ল্যান্ডস্কেপ এবং প্রতিকৃতির মাঝে পর্দাটি খুব দ্রুত ঘোরান, তখন আমি কোডটিতে একটি ব্যতিক্রম পাই onPostExecute:

java.lang.IllegalStateException: ফ্র্যাগমেন্ট মাইফ্রেগমেন্ট {410f6060 tivity ক্রিয়াকলাপের সাথে সংযুক্ত নেই

আমি মনে করি কারণ এটির মধ্যে একটি নতুন MyFragmentতৈরি হয়েছে এবং এটি AsyncTaskশেষ হওয়ার আগেই ক্রিয়াকলাপের সাথে সংযুক্ত ছিল । onPostExecuteকোডগুলিতে সংযুক্ত না হওয়ার জন্য কল MyFragment

তবে আমি কীভাবে এটি ঠিক করতে পারি?


1
খণ্ড বিস্ফোরক থেকে আপনার ব্যবহার করা উচিত। mView = inflater.inflate(R.layout.my_layout, container, false) আর এখন এই দৃশ্য যখন আপনি সেই সমস্ত সম্পদের পেতে চান ব্যবহার করুন: mView.getResources().***। এটি আমাকে এই বাগটি ঠিক করতে সহায়তা করে।
ফক্সিস

@ ফক্সিস এটি Contextআপনার এমভিউয়ের সাথে সংযুক্ত যা ফাঁস করে `
nhaarman

আমি এখনও এটি চেক না করা হতে পারে। ফাঁস এড়ানোর জন্য কীভাবে mViewঅনডাস্ট্রয়ে নালাগ্রস্থ করবেন?
ফক্সিস

উত্তর:


774

আমি খুব সহজ উত্তর পেয়েছি isAdded():

trueখণ্ডটি বর্তমানে তার ক্রিয়াকলাপে যুক্ত হলে ফিরে আসুন ।

@Override
protected void onPostExecute(Void result){
    if(isAdded()){
        getResources().getString(R.string.app_name);
    }
}

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


আমার ক্ষেত্রে যখন আমি অন্য একটি অ্যাপ্লিকেশন উদ্দিষ্ট চালু করছি ... তখন আমি একই ত্রুটি পাচ্ছি ... কোনও উত্সাহ?
CoDe


5
এপিআই <11 এ থাকাকালীন আপনি ডেভেলপার.আ্যান্ড্রয়েড. com/references/android/support/v4/app/… ব্যবহার করবেন যেখানে এটি কাজ করবে।
nhaarman

আমি যখন ডায়ালগফ্রেগমেন্টটি ব্যবহার করি তখন আমি এই সমস্যার মুখোমুখি হয়েছি। ডায়লগফ্র্যাগমেন্ট খারিজ করার পরে, আমি অন্য ক্রিয়াকলাপ শুরু করার চেষ্টা করেছি। তারপরে এই ত্রুটি ঘটেছে। আমি স্টার্টঅ্যাক্টিভিটির পরে বরখাস্ত () কল করে এই ত্রুটিটি এড়িয়েছি। সমস্যাটি ছিল যে খণ্ডটি ইতিমধ্যে ক্রিয়াকলাপ থেকে আলাদা করা হয়েছিল।
আতারু

28

সমস্যাটি হ'ল আপনি গেটআরসোর্সেস () .গেটস্ট্রিং () ব্যবহার করে সংস্থানগুলি অ্যাক্সেস করার চেষ্টা করছেন (যা এই ক্ষেত্রে স্ট্রিংগুলি) যা ক্রিয়াকলাপ থেকে সংস্থানগুলি পাওয়ার চেষ্টা করবে। খণ্ড শ্রেণীর এই উত্স কোডটি দেখুন:

 /**
  * Return <code>getActivity().getResources()</code>.
  */
 final public Resources getResources() {
     if (mHost == null) {
         throw new IllegalStateException("Fragment " + this + " not attached to Activity");
     }
     return mHost.getContext().getResources();
 }

mHost আপনার কার্যকলাপকে ধরে রাখে এমন একটি বস্তু।

ক্রিয়াকলাপটি সংযুক্ত না থাকায় আপনার গেটআরসোর্সস () কলটি ব্যাতিক্রম ছুঁড়ে দেবে।

আপনি কেবল সমস্যাটি গোপন করছেন বলে গ্রহণযোগ্য সমাধান আইএমএইচও করার উপায় নয়। সঠিক উপায়টি হ'ল অ্যাপ্লিকেশন প্রসঙ্গে যেমন সর্বদা উপস্থিত থাকার গ্যারান্টিযুক্ত অন্য কোথাও থেকে প্রাপ্ত সংস্থানগুলি পাওয়া:

youApplicationObject.getResources().getString(...)

আমি এই সমাধানটি ব্যবহার করেছি কারণ getString()আমার খণ্ডটি যখন থামানো হয়েছিল তখন আমাকে সম্পাদন করা দরকার। ধন্যবাদ
গীকারিস্ট

24

আমি এখানে দুটি ভিন্ন পরিস্থিতির মুখোমুখি হয়েছি:

1) যখন আমি অ্যাসিঙ্ক্রোনাস টাস্কটি যাইহোক শেষ করতে চাই: আমার অনপস্টে এক্সেকিউট কল্পনা করে যে ডেটা পেয়েছে এবং তারপরে ভিউগুলি আপডেট করার জন্য একটি শ্রোতাকে ফোন করবে যাতে আরও দক্ষ হতে পারে, আমি চাই যে টাস্কটি যে কোনও উপায়ে শেষ করা উচিত তাই ব্যবহারকারীর আগমনের সময় আমার কাছে ডেটা প্রস্তুত রয়েছে পেছনে. এই ক্ষেত্রে আমি সাধারণত এটি করি:

@Override
protected void onPostExecute(void result) {
    // do whatever you do to save data
    if (this.getView() != null) {
        // update views
    }
}

2) আমি যখন অ্যাসিক্রোনাস টাস্কটি কেবল তখনই শেষ করতে চাই যখন ভিউগুলি আপডেট করা যায়: আপনি এখানে যে প্রস্তাব দিচ্ছেন, টাস্কটি কেবলমাত্র ভিউগুলিকে আপডেট করে, কোনও ডেটা স্টোরেজ প্রয়োজন হয় না, সুতরাং ভিউগুলি যদি শেষ হয় তবে টাস্কটি শেষ করার কোনও চিহ্ন নেই if আর প্রদর্শিত হচ্ছে না। আমি এটা করি:

@Override
protected void onStop() {
    // notice here that I keep a reference to the task being executed as a class member:
    if (this.myTask != null && this.myTask.getStatus() == Status.RUNNING) this.myTask.cancel(true);
    super.onStop();
}

আমি এ নিয়ে কোনও সমস্যা খুঁজে পাইনি, যদিও আমি আরও জটিল পদ্ধতিতেও ব্যবহার করি যার মধ্যে খণ্ডগুলির পরিবর্তে ক্রিয়াকলাপ থেকে কাজ শুরু করা অন্তর্ভুক্ত।

আশা করি এটি কারও সাহায্য করে! :)


18

আপনার কোডটির সমস্যাটি হ'ল আপনি যেভাবে অ্যাসিঙ্কটাস্ক ব্যবহার করছেন তা হ'ল কারণ আপনি যখন ঘুমের থ্রেডের সময় পর্দাটি ঘোরান:

Thread.sleep(2000) 

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

getResources().getString(R.string.app_name)

যা এর সমান:

MyFragment.this.getResources().getString(R.string.app_name)

সুতরাং চূড়ান্ত সমাধানটি AsyncTask ইনস্ট্যান্স পরিচালনা করুন (আপনি যদি স্ক্রিনটি ঘোরানোর সময় খণ্ড পুনর্নির্মাণের আগে এটি বাতিল করার জন্য) এবং ট্রানজিশনের সময় বাতিল করা হয়, বুলিয়ান পতাকার সাহায্যে পুনর্গঠনের পরে AsyncTask পুনরায় চালু করুন:

public class MyFragment extends SherlockFragment {

    private MyAsyncTask myAsyncTask = null;
    private boolean myAsyncTaskIsRunning = true;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(savedInstanceState!=null) {
            myAsyncTaskIsRunning = savedInstanceState.getBoolean("myAsyncTaskIsRunning");
        }
        if(myAsyncTaskIsRunning) {
            myAsyncTask = new MyAsyncTask();
            myAsyncTask.execute();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("myAsyncTaskIsRunning",myAsyncTaskIsRunning);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if(myAsyncTask!=null) myAsyncTask.cancel(true);
        myAsyncTask = null;

    }

    public class MyAsyncTask extends AsyncTask<Void, Void, Void>() {

        public MyAsyncTask(){}

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            myAsyncTaskIsRunning = true;
        }
        @Override
        protected Void doInBackground(Void... params) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {}
            return null;
        }

        @Override
        protected void onPostExecute(Void result){
            getResources().getString(R.string.app_name);
            myAsyncTaskIsRunning = false;
            myAsyncTask = null;
        }

    }
}

পরিবর্তে যদি সহায়তা getResources().***ব্যবহার করা হয়Fragments.this.getResource().***
প্রবস

17

এগুলি এর জন্য বেশ কৌশল এবং সমাধান থেকে ক্রিয়াকলাপ থেকে খণ্ড ফাঁস Their

সুতরাং গেটআরসোর্স বা যে কোনও একটি যা খণ্ড থেকে প্রাপ্ত ক্রিয়াকলাপের প্রসঙ্গে নির্ভর করে এটি সর্বদা ক্রিয়াকলাপের স্থিতি এবং খণ্ড খণ্ডের স্থিতি নীচে অনুসরণ করে

 Activity activity = getActivity(); 
    if(activity != null && isAdded())

         getResources().getString(R.string.no_internet_error_msg);
//Or any other depends on activity context to be live like dailog


        }
    }

7
isAdded () যথেষ্ট কারণ: চূড়ান্ত পাবলিক বুলিয়ান isAdded () m retHHost! = নাল && এমএডেড; ।
NguyenDat

আমার ক্ষেত্রে এই চেকগুলি উত্সাহী নয়, আমি এগুলি যুক্ত করেও ক্র্যাশ পেতে চলেছি।
ডেভিড

@ ডেভিড, isAddedযথেষ্ট। আমি কখনই এমন পরিস্থিতি দেখিনি যখন getString()ক্র্যাশ হয়েছিল isAdded == true। আপনি কি নিশ্চিত যে কোনও কার্যকলাপ প্রদর্শিত হয়েছিল এবং একটি খণ্ড সংযুক্ত ছিল?
কুলমাইন্ড

14
if (getActivity() == null) return;

কিছু ক্ষেত্রেও কাজ করে। এটি থেকে কোড সম্পাদনকে কেবল বিরতি দেয় এবং অ্যাপটি ক্রাশ না হওয়ার বিষয়টি নিশ্চিত করুন


10

আমি একই সমস্যার মুখোমুখি হয়েছি আমি এরিকের দ্বারা বর্ণিত সংস্থান হিসাবে রিসোর্স পেতে কেবল একক দৃষ্টান্ত যুক্ত করেছি

MainFragmentActivity.defaultInstance().getResources().getString(R.string.app_name);

আপনি ব্যবহার করতে পারেন

getActivity().getResources().getString(R.string.app_name);

আমি আশা করি এটি সাহায্য করবে


2

লোড হওয়া পছন্দসমূহের সাথে অ্যাপ্লিকেশন সেটিংসের ক্রিয়াকলাপটি দৃশ্যমান হওয়ার সময় আমি একই ধরণের সমস্যার মুখোমুখি হয়েছিলাম। যদি আমি একটি পছন্দগুলিতে পরিবর্তন করতে পারি এবং তারপরে প্রদর্শন সামগ্রীটি ঘোরানো এবং আবার পছন্দটি পরিবর্তন করি তবে এটি একটি বার্তাটি দিয়ে ক্র্যাশ করবে যে খণ্ডটি (আমার পছন্দগুলি শ্রেণি) কোনও ক্রিয়াকলাপের সাথে সংযুক্ত ছিল না।

ডিবাগ করার সময় দেখে মনে হচ্ছিল ডিসপ্লে কনটেন্টটি ঘোরানোর সময় পছন্দসই বিভাগের অনক্রিট () পদ্ধতিটি দু'বার কল করা হচ্ছে। এটি ইতিমধ্যে যথেষ্ট অদ্ভুত ছিল। তারপরে আমি ব্লকের বাইরে ইসডেড () চেক যুক্ত করেছি যেখানে এটি ক্রাশটি নির্দেশ করবে এবং এটি সমস্যার সমাধান করেছে।

এখানে শ্রোতার কোডটি নতুন এন্ট্রিটি দেখানোর জন্য পছন্দগুলি সারাংশ আপডেট করে। এটি আমার পছন্দ শ্রেণীর অনক্রিট () পদ্ধতিতে অবস্থিত যা পছন্দস্বরূপ শ্রেণীর শ্রেণি প্রসারিত করে:

public static class Preferences extends PreferenceFragment {
    SharedPreferences.OnSharedPreferenceChangeListener listener;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // ...
        listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
            @Override
            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
                // check if the fragment has been added to the activity yet (necessary to avoid crashes)
                if (isAdded()) {
                    // for the preferences of type "list" set the summary to be the entry of the selected item
                    if (key.equals(getString(R.string.pref_fileviewer_textsize))) {
                        ListPreference listPref = (ListPreference) findPreference(key);
                        listPref.setSummary("Display file content with a text size of " + listPref.getEntry());
                    } else if (key.equals(getString(R.string.pref_fileviewer_segmentsize))) {
                        ListPreference listPref = (ListPreference) findPreference(key);
                        listPref.setSummary("Show " + listPref.getEntry() + " bytes of a file at once");
                    }
                }
            }
        };
        // ...
    }

আমি আশা করি এটি অন্যকে সহায়তা করবে!


0

আপনি যদি Applicationক্লাসটি প্রসারিত করেন এবং নীচে স্থিতিশীল 'গ্লোবাল' প্রসঙ্গ অবজেক্টটি বজায় রাখেন তবে আপনি স্ট্রিং রিসোর্স লোড করতে ক্রিয়াকলাপের পরিবর্তে এটি ব্যবহার করতে পারেন।

public class MyApplication extends Application {
    public static Context GLOBAL_APP_CONTEXT;

    @Override
    public void onCreate() {
        super.onCreate();
        GLOBAL_APP_CONTEXT = this;
    }
}

আপনি যদি এটি ব্যবহার করেন তবে Toastলাইফেসাইকেলের বিষয়ে চিন্তা না করেই আপনি রিসোর্স লোড দিয়ে এড়াতে পারবেন।


5
আমি হতাশ হয়ে পড়ছি কিন্তু কেউ কেন তা ব্যাখ্যা করেনি। স্ট্যাটিক প্রসঙ্গগুলি সাধারণত খারাপ হয় তবে আমার ধারণা ছিল যে আপনার যদি স্থির অ্যাপ্লিকেশন রেফারেন্স থাকে তবে এটি কোনও মেমরি ফাঁস নয়।
অ্যান্টনি চুইনার্ড

আপনার উত্তর হ্রাস পেয়েছে কারণ এটি ঠিক একটি হ্যাক যথাযথ সমাধান নয়। @Nhaarman ভাগ করেছেন চেক সলিউশন
বিবেক কুমার শ্রীবাস্তব

0

আমার ক্ষেত্রে খণ্ডের পদ্ধতিগুলি পরে বলা হয়েছে

getActivity().onBackPressed();

0

একটি পুরানো পোস্ট, তবে আমি সবচেয়ে বেশি ভোট দেওয়া উত্তর সম্পর্কে অবাক হয়েছি।

এর যথাযথ সমাধানটি হ'ল অনটপ (বা আপনার খণ্ডে উপযুক্ত যেখানেই) অ্যাসিনটাস্ক বাতিল করা উচিত। এইভাবে আপনি কোনও মেমরি ফাঁস (আপনার ধ্বংস হওয়া খণ্ডটির একটি রেফারেন্স রাখার মতো একটি অ্যাসিনটাস্ক) প্রবর্তন করেন না এবং আপনার খণ্ডে কী চলছে তার আপনার নিয়ন্ত্রণ আরও ভাল।

@Override
public void onStop() {
    super.onStop();
    mYourAsyncTask.cancel(true);
}

1
সর্বাধিক উত্সাহিত উত্তর এটি অন্তর্ভুক্ত। এছাড়াও, অনুরোধ করা থেকে cancelবিরত থাকতে পারে onPostExecute
nhaarman

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