সেরা অনুশীলন: ওরিয়েন্টেশন পরিবর্তনের সময় অ্যাসিঙ্কটাস্ক


151

AsyncTask অন্য থ্রেডে জটিল কাজগুলি চালানো একটি দুর্দান্ত জিনিস।

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

সুতরাং, আমি এই ত্রুটিগুলি এড়াতে এবং অ্যাসিঙ্কটাসকে ব্যর্থতা থেকে বাঁচাতে এক ধরণের "সেরা অনুশীলন" খুঁজছি।

আমি এখন পর্যন্ত যা দেখেছি তা হ'ল:

  • ওরিয়েন্টেশন পরিবর্তনগুলি অক্ষম করুন ((নিশ্চিতভাবে যেভাবে আপনি এটি পরিচালনা করবেন তা নয়))
  • কার্যটি টিকে থাকতে দেওয়া এবং এর মাধ্যমে এটিকে নতুন ক্রিয়াকলাপের সাথে আপডেট করা onRetainNonConfigurationInstance
  • Activityধ্বংস হয়ে যাওয়ার পরে কেবল টাস্ক বাতিল করা এবং Activityআবার তৈরি করার পরে এটি পুনরায় চালু করা।
  • ক্রিয়াকলাপের উদাহরণের পরিবর্তে অ্যাপ্লিকেশন শ্রেণিতে টাস্ককে আবদ্ধ করা।
  • "তাক" প্রকল্পে ব্যবহৃত কিছু পদ্ধতি (onRestoreInstanceState এর মাধ্যমে)

কিছু কোড উদাহরণ:

স্ক্রিন ঘোরার সময় অ্যান্ড্রয়েড অ্যাসিঙ্কটাস্কস, পর্ব প্রথম এবং দ্বিতীয় খণ্ড

ShelvesActivity.java

আপনি কি আমাকে সেরা পদ্ধতির সন্ধান করতে সহায়তা করতে পারেন যা সমস্যাটি সবচেয়ে ভাল সমাধান করে এবং তা কার্যকর করাও সহজ? কোডটি নিজেও গুরুত্বপূর্ণ কারণ আমি কীভাবে এটি সঠিকভাবে সমাধান করতে জানি না।



এটি মার্ক মারফির ব্লগ থেকে এসেছে ... অ্যাসিঙ্কটাস্ক এবং স্ক্রিনরোটেশন সাহায্য করতে পারে ... লিঙ্ক
গোপাল

যদিও এটি একটি পুরাতন পোস্ট তবে এই আইএমওটি অনেক সহজ (এবং আরও ভাল?) পদ্ধতির।
ড্রয়েডদেভ

আমি কেবল ভাবছি যে ডকুমেন্টেশন কেন এইরকম তুচ্ছ পরিস্থিতি সম্পর্কে কথা বলছে না।
শ্রীকান্ত করুণাঘাট

উত্তর:


140

এই সমস্যাটির সমাধান করতে ব্যবহার করবেন নাandroid:configChanges । এটি খুব খারাপ অনুশীলন।

কোনটিই ব্যবহার করবেন নাActivity#onRetainNonConfigurationInstance() । এটি কম মডুলার এবং Fragmentবেস ভিত্তিক অ্যাপ্লিকেশনগুলির জন্য উপযুক্ত নয় ।

আপনি রক্ষণাবেক্ষণ গুলি ব্যবহার করে কীভাবে কনফিগারেশন পরিবর্তনগুলি পরিচালনা করবেন তা বর্ণনা করে আমার নিবন্ধটি পড়তে পারেন Fragment। এটি AsyncTaskএকটি ঘূর্ণন পরিবর্তনের পুরোপুরি ধরে রাখার সমস্যাটি সুন্দরভাবে সমাধান করে। আপনি মূলত আপনার হোস্ট করার জন্য প্রয়োজন AsyncTaskভিতরে Fragment, কল setRetainInstance(true)উপর Fragment, এবং রিপোর্ট AsyncTaskএর উন্নতি / ফলাফল ফিরে এটা করতে Activityঅপরিবর্তিত রাখা মাধ্যমে Fragment


26
দুর্দান্ত ধারণা, তবে সবাই খণ্ডগুলি ব্যবহার করে না। খণ্ডগুলি একটি বিকল্প ছিল তার অনেক আগে লিগ্যাসি কোড লেখা হয়েছিল।
স্কট বিগস

14
@ স্কটবিগস টুকরোগুলি অ্যান্ড্রয়েড ১. back-এ ফিরে সমস্ত দিক সমর্থন লাইব্রেরির মাধ্যমে উপলব্ধ। এবং আপনি এমন কিছু লিগ্যাসি কোডের উদাহরণ দিতে পারেন যা এখনও সক্রিয়ভাবে ব্যবহৃত হচ্ছে যা সমর্থন লাইব্রেরি টুকরোগুলি ব্যবহার করতে সমস্যা হয়? কারণ আমি সত্যই মনে করি না যে এটি একটি সমস্যা।
অ্যালেক্স লকউড

4
@ টেকথথ আমার উত্তরে এই সমস্যাগুলি সমাধান করার প্রয়োজন বোধ করিনি, কারণ 99.9% মানুষ আর ব্যবহার করেন না TabActivity। সত্যি কথা বলতে কি, আমরা নিশ্চিত নই কেন আমরা এমনকি এই বিষয়ে কেন কথা বলছি ... সকলেই সম্মত হন যে Fragmentএই পথে যাওয়ার উপায়। :)
অ্যালেক্স লকউড

2
যদি অ্যাসিঙ্কটাস্ককে অবশ্যই কোনও নেস্টেড টুকরা থেকে কল করা উচিত?
এডুয়ার্ডো নাভেদা

3
@ অ্যালেক্সলকউড - "সকলে সম্মতি জানায় যে খণ্ডগুলি যাওয়ার উপায়" " স্কোয়াডে থাকা দেবগণের সাথে দ্বিমত হবে!
জেবেকটন

36

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

এইভাবে অ্যাসিঙ্কটাস্কগুলিকে তাদের ফলাফলটি পরিচালনা করে এমন নির্দিষ্ট ক্রিয়াকলাপের নজরে নজর দেওয়া উচিত নয়। তারা শেষ হয়ে গেলে কেবল "চিৎকার" করে এবং যদি কোনও ক্রিয়াকলাপ সেই সময়ের কাছাকাছি থাকে (সক্রিয় এবং মনোনিবেশিত হয় / এটি পুনরায় শুরু করা অবস্থায় রয়েছে) যা কার্যের ফলাফলগুলিতে আগ্রহী, তবে তা পরিচালনা করা হবে।

এটিতে আরও কিছুটা ওভারহেড জড়িত, যেহেতু রানটাইম সম্প্রচারটি পরিচালনা করতে হয় তবে আমি সাধারণত আপত্তি করি না। আমি মনে করি ডিফল্ট সিস্টেমের পরিবর্তে লোকালড্রোডকাস্ট ম্যানেজারটি ব্যবহার করে জিনিসগুলি কিছুটা গতি বাড়ায়।


6
যদি আপনি উত্তরে একটি উদাহরণ যোগ করতে পারেন তবে এটি আরও সহায়ক হবে
সংকর ভি

1
আমি মনে করি এটিই সমাধান যা ক্রিয়াকলাপ এবং টুকরাগুলির মধ্যে কম সংযোগের প্রস্তাব দেয়
রজার গারজন নিতো

7
এটি কোনও সমস্যার অংশ হতে পারে তবে অরিয়েন্টেশন পরিবর্তনের পরে অ্যাসিঙ্কটাস্ককে পুনরায় তৈরি করার সমস্যাটি সমাধান করবে বলে মনে হয় না।
মাইগুয়েল

4
আপনি যদি দুর্ভাগ্য হন এবং সম্প্রচারের সময় কোনও তত্পরতা না থাকে তবে কী হবে? (অর্থাত আপনি মাঝ-আবর্তিত)
স্যাম

24

এখানে অ্যাসিঙ্কটাস্কের আরেকটি উদাহরণ যা Fragmentরানটাইম কনফিগারেশন পরিবর্তনগুলি (যখন ব্যবহারকারী স্ক্রিনটি ঘোরার সময় হিসাবে) পরিচালনা করতে ব্যবহার করে setRetainInstance(true)। একটি নির্ধারিত (নিয়মিত আপডেট করা) অগ্রগতি বারটিও প্রদর্শিত হয়।

উদাহরণটি আংশিকভাবে অফিসিয়াল ডক্সের উপর ভিত্তি করে তৈরি করা হয়েছে, একটি কনফিগারেশন চেঞ্জ চলাকালীন কোনও অবজেক্ট ধরে রাখা

এই উদাহরণে ব্যাকগ্রাউন্ড থ্রেডের প্রয়োজনীয় কাজের কাজটি হ'ল ইন্টারনেট থেকে ইউআইতে কোনও চিত্র লোড করা।

অ্যালেক্স লকউড ঠিক বলে মনে হয় যে অসিঙ্কটাস্কের সাথে "রিটেইনড ফ্রেগমেন্ট" ব্যবহার করে রানটাইম কনফিগারেশন পরিবর্তনগুলি পরিচালনা করার ক্ষেত্রে এটি সর্বোত্তম অনুশীলন। onRetainNonConfigurationInstance()অ্যান্ড্রয়েড স্টুডিওতে লিন্টে অবহেলিত হয়। অফিসিয়াল ডকস নিজেকে কনফিগারেশন চেঞ্জ হ্যান্ডলিংandroid:configChanges থেকে , ...

কনফিগারেশন পরিবর্তনটি নিজেকে পরিচালনা করা বিকল্প সংস্থানগুলি ব্যবহার করা আরও বেশি জটিল করে তুলতে পারে, কারণ সিস্টেমগুলি সেগুলি স্বয়ংক্রিয়ভাবে আপনার জন্য প্রয়োগ করে না। কোনও কনফিগারেশন পরিবর্তনের কারণে আপনাকে পুনরায় আরম্ভ করা এড়াতে হবে এবং বেশিরভাগ অ্যাপ্লিকেশনগুলির জন্য প্রস্তাবিত না হলে এই কৌশলটি সর্বশেষ অবলম্বন হিসাবে বিবেচনা করা উচিত।

তারপরে পটভূমির থ্রেডের জন্য কোনও একটি এ্যাসিনটাস্ক ব্যবহার করা উচিত কিনা তা নিয়ে সমস্যা রয়েছে is

AsyncTask জন্য অফিসিয়াল রেফারেন্স সতর্ক ...

অ্যাসিঙ্কটাস্কগুলি আদর্শভাবে সংক্ষিপ্ত ক্রিয়াকলাপের জন্য ব্যবহার করা উচিত (সর্বাধিক কয়েক সেকেন্ড) threads এক্সিকিউটার, থ্রেডপুলএক্সেকিউটার এবং ফিউচারটাস্ক।

বিকল্পভাবে কেউ অ্যাসিক্রোনাস ক্রিয়াকলাপ সম্পাদনের জন্য কোনও পরিষেবা, লোডার (একটি কার্সারলডার বা অ্যাসিঙ্কটাস্কলডার ব্যবহার করে) বা সামগ্রী সরবরাহকারী ব্যবহার করতে পারে।

আমি বাকি পোস্টগুলিকে এতে ভাঙ্গি:

  • কার্যপ্রণালী; এবং
  • উপরোক্ত পদ্ধতির জন্য সমস্ত কোড।

কার্যপ্রণালী

  1. কোনও ক্রিয়াকলাপের অভ্যন্তর শ্রেণি হিসাবে বেসিক অ্যাসিঙ্কটাস্ক দিয়ে শুরু করুন (এটি কোনও অভ্যন্তরীণ শ্রেণির হওয়ার দরকার নেই তবে এটি সম্ভবত সুবিধাজনক হবে)। এই পর্যায়ে AsyncTask রানটাইম কনফিগারেশন পরিবর্তনগুলি পরিচালনা করে না।

    public class ThreadsActivity extends ActionBarActivity {
    
        private ImageView mPictureImageView;
    
        private class LoadImageFromNetworkAsyncTask
                              extends AsyncTask<String, Void, Bitmap> {
    
            @Override
            protected Bitmap doInBackground(String... urls) {
                return loadImageFromNetwork(urls[0]);
            }
    
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                mPictureImageView.setImageBitmap(bitmap);
            }
        }
    
        /**
         * Requires in AndroidManifext.xml
         *  <uses-permission android:name="android.permission.INTERNET" />
         */
        private Bitmap loadImageFromNetwork(String url) {
            Bitmap bitmap = null;
            try {
                bitmap = BitmapFactory.decodeStream((InputStream)
                                              new URL(url).getContent());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bitmap;
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_threads);
    
            mPictureImageView =
                (ImageView) findViewById(R.id.imageView_picture);
        }
    
        public void getPicture(View view) {
            new LoadImageFromNetworkAsyncTask()
                .execute("http://i.imgur.com/SikTbWe.jpg");
        }
    
    }
  2. ফ্রেসমেন্ট শ্রেণি প্রসারিত এবং এর নিজস্ব ইউআই নেই এমন একটি নেস্টেড ক্লাসের রেটিন্টফ্র্যাগমেন্ট যুক্ত করুন। এই খণ্ডটির অনক্রিট ইভেন্টে সেটট্রেইটইন ইনস্ট্যান্স (সত্য) যুক্ত করুন। আপনার ডেটা সেট এবং পেতে পদ্ধতি সরবরাহ করুন।

    public class ThreadsActivity extends Activity {
    
        private ImageView mPictureImageView;
        private RetainedFragment mRetainedFragment = null;
        ...
    
        public static class RetainedFragment extends Fragment {
    
            private Bitmap mBitmap;
    
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
    
                // The key to making data survive
                // runtime configuration changes.
                setRetainInstance(true);
            }
    
            public Bitmap getData() {
                return this.mBitmap;
            }
    
            public void setData(Bitmap bitmapToRetain) {
                this.mBitmap = bitmapToRetain;
            }
        }
    
        private class LoadImageFromNetworkAsyncTask
                        extends AsyncTask<String, Integer,Bitmap> {
        ....
  3. বহিরাগত কার্যকলাপের ক্লাসের অনক্রিট () রেটিনফ্রেগমেন্টটি হ্যান্ডেল করুন: যদি ইতিমধ্যে এটি উপস্থিত থাকে (যদি কার্যকলাপটি পুনরায় চালু হয় তবে) এটি উল্লেখ করুন; এটি উপস্থিত না থাকলে এটি তৈরি এবং যুক্ত করুন; তারপরে, এটি ইতিমধ্যে বিদ্যমান থাকলে, র‌েটিনফ্র্যাগমেন্ট থেকে ডেটা পান এবং সেই ডেটা দিয়ে আপনার ইউআই সেট করুন।

    public class ThreadsActivity extends Activity {
    
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_threads);
    
            final String retainedFragmentTag = "RetainedFragmentTag";
    
            mPictureImageView =
                      (ImageView) findViewById(R.id.imageView_picture);
            mLoadingProgressBar =
                    (ProgressBar) findViewById(R.id.progressBar_loading);
    
            // Find the RetainedFragment on Activity restarts
            FragmentManager fm = getFragmentManager();
            // The RetainedFragment has no UI so we must
            // reference it with a tag.
            mRetainedFragment =
              (RetainedFragment) fm.findFragmentByTag(retainedFragmentTag);
    
            // if Retained Fragment doesn't exist create and add it.
            if (mRetainedFragment == null) {
    
                // Add the fragment
                mRetainedFragment = new RetainedFragment();
                fm.beginTransaction()
                    .add(mRetainedFragment, retainedFragmentTag).commit();
    
            // The Retained Fragment exists
            } else {
    
                mPictureImageView
                    .setImageBitmap(mRetainedFragment.getData());
            }
        }
  4. ইউআই থেকে অ্যাসিঙ্কটাস্ক শুরু করুন

    public void getPicture(View view) {
        new LoadImageFromNetworkAsyncTask().execute(
                "http://i.imgur.com/SikTbWe.jpg");
    }
  5. একটি নির্ধারিত অগ্রগতি বার যুক্ত করুন এবং কোড করুন:

    • ইউআই লেআউটে একটি অগ্রগতি বার যুক্ত করুন;
    • ক্রিয়াকলাপের ক্রিয়াকলাপে এটির একটি রেফারেন্স পান ();
    • প্রক্রিয়াটির শুরু এবং শেষে এটিকে দৃশ্যমান এবং অদৃশ্য করে তুলুন;
    • অনপ্রযুক্তি আপডেটে UI তে রিপোর্ট করার অগ্রগতিটি সংজ্ঞায়িত করুন।
    • অ্যাসিঙ্কটাস্ক ২ য় জেনেরিক প্যারামিটারটি শূন্য থেকে একটি প্রকারে পরিবর্তন করুন যা অগ্রগতি আপডেটগুলি হ্যান্ডেল করতে পারে (যেমন পূর্ণসংখ্যার)।
    • ডাইনব্যাকগ্রাউন্ডে () এর নিয়মিত পয়েন্টগুলিতে প্রকাশের প্রচার।

উপরোক্ত পদ্ধতির জন্য সমস্ত কোড

ক্রিয়াকলাপ বিন্যাস

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.mysecondapp.ThreadsActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

        <ImageView
            android:id="@+id/imageView_picture"
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:background="@android:color/black" />

        <Button
            android:id="@+id/button_get_picture"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_below="@id/imageView_picture"
            android:onClick="getPicture"
            android:text="Get Picture" />

        <Button
            android:id="@+id/button_clear_picture"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/button_get_picture"
            android:layout_toEndOf="@id/button_get_picture"
            android:layout_toRightOf="@id/button_get_picture"
            android:onClick="clearPicture"
            android:text="Clear Picture" />

        <ProgressBar
            android:id="@+id/progressBar_loading"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/button_get_picture"
            android:progress="0"
            android:indeterminateOnly="false"
            android:visibility="invisible" />

    </RelativeLayout>
</ScrollView>

এর সাথে ক্রিয়াকলাপ: উপ-শ্রেণিবদ্ধ AsyncTask অভ্যন্তর শ্রেণি; সাবক্ল্যাসড রিটইনফ্রেগমেন্ট অভ্যন্তর শ্রেণি যা রানটাইম কনফিগারেশন পরিবর্তনগুলি পরিচালনা করে (উদাহরণস্বরূপ যখন ব্যবহারকারী স্ক্রিনটি ঘোরান); এবং নিয়মিত বিরতিতে অগ্রগতি বার আপডেট করা নির্ধারণ করে। ...

public class ThreadsActivity extends Activity {

    private ImageView mPictureImageView;
    private RetainedFragment mRetainedFragment = null;
    private ProgressBar mLoadingProgressBar;

    public static class RetainedFragment extends Fragment {

        private Bitmap mBitmap;

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

            // The key to making data survive runtime configuration changes.
            setRetainInstance(true);
        }

        public Bitmap getData() {
            return this.mBitmap;
        }

        public void setData(Bitmap bitmapToRetain) {
            this.mBitmap = bitmapToRetain;
        }
    }

    private class LoadImageFromNetworkAsyncTask extends AsyncTask<String,
            Integer, Bitmap> {

        @Override
        protected Bitmap doInBackground(String... urls) {
            // Simulate a burdensome load.
            int sleepSeconds = 4;
            for (int i = 1; i <= sleepSeconds; i++) {
                SystemClock.sleep(1000); // milliseconds
                publishProgress(i * 20); // Adjust for a scale to 100
            }

            return com.example.standardapplibrary.android.Network
                    .loadImageFromNetwork(
                    urls[0]);
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            mLoadingProgressBar.setProgress(progress[0]);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            publishProgress(100);
            mRetainedFragment.setData(bitmap);
            mPictureImageView.setImageBitmap(bitmap);
            mLoadingProgressBar.setVisibility(View.INVISIBLE);
            publishProgress(0);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_threads);

        final String retainedFragmentTag = "RetainedFragmentTag";

        mPictureImageView = (ImageView) findViewById(R.id.imageView_picture);
        mLoadingProgressBar = (ProgressBar) findViewById(R.id.progressBar_loading);

        // Find the RetainedFragment on Activity restarts
        FragmentManager fm = getFragmentManager();
        // The RetainedFragment has no UI so we must reference it with a tag.
        mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(
                retainedFragmentTag);

        // if Retained Fragment doesn't exist create and add it.
        if (mRetainedFragment == null) {

            // Add the fragment
            mRetainedFragment = new RetainedFragment();
            fm.beginTransaction().add(mRetainedFragment,
                                      retainedFragmentTag).commit();

            // The Retained Fragment exists
        } else {

            mPictureImageView.setImageBitmap(mRetainedFragment.getData());
        }
    }

    public void getPicture(View view) {
        mLoadingProgressBar.setVisibility(View.VISIBLE);
        new LoadImageFromNetworkAsyncTask().execute(
                "http://i.imgur.com/SikTbWe.jpg");
    }

    public void clearPicture(View view) {
        mRetainedFragment.setData(null);
        mPictureImageView.setImageBitmap(null);
    }
}

এই উদাহরণে লাইব্রেরির ফাংশন (উপরে বর্ণিত স্পষ্ট প্যাকেজ উপসর্গ com.example.standardapplibrary.android.Network সহ) যা সত্য কাজ করে ...

public static Bitmap loadImageFromNetwork(String url) {
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream((InputStream) new URL(url)
                .getContent());
    } catch (Exception e) {
        e.printStackTrace();
    }
    return bitmap;
}

আপনার ব্যাকগ্রাউন্ড টাস্কের যে কোনও অনুমতি AndroidManLive.xML এ যুক্ত করুন ...

<manifest>
...
    <uses-permission android:name="android.permission.INTERNET" />

আপনার ক্রিয়াকলাপটি AndroidManLive.xML এ যুক্ত করুন ...

<manifest>
...
    <application>
        <activity
            android:name=".ThreadsActivity"
            android:label="@string/title_activity_threads"
            android:parentActivityName=".MainActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.example.mysecondapp.MainActivity" />
        </activity>

গ্রেট। আপনার এটিতে একটি ব্লগ লেখা উচিত।
আখ

2
@AKh। আপনি কি বলতে চান আমার উত্তর স্ট্যাকওভারফ্লোতে খুব বেশি জায়গা নেয়?
জন বেন্টলে

1
আমার মনে হয় তার কেবলমাত্র আপনার কাছে দুর্দান্ত উত্তর রয়েছে এবং আপনার একটি ব্লগ লেখা উচিত! =) @ জনবেন্টলি
স্যান্ডি ডি

@ SandyD.yes গতকাল ইতিবাচক ব্যাখ্যার জন্য ধন্যবাদ। আমি আশা করি সে বা সে উদ্দেশ্য করেছিল।
জন বেন্টলে

আমি এটিও একটি দুর্দান্ত উত্তর বলে ভেবেছিলাম এবং আমি এটির মতো ব্যাখ্যাও করেছি। এই মত খুব সম্পূর্ণ উত্তর দুর্দান্ত !!
লিওনার্দোসিবেলা

3

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


এই আকর্ষণীয় উত্তরের জন্য আপনাকে অনেক ধন্যবাদ। সম্পর্কিত প্রশ্নের সাথে বর্ণিত সমস্যাগুলি ছাড়াও এটি একটি ভাল সমাধান।
কাও

5
দুর্ভাগ্যক্রমে, এই সমাধানটি অবহেলা পদ্ধতি ব্যবহার করে।
ড্যামিয়েন

3

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

এই ঘূর্ণনটি পরিচালনা করা হয়, এবং যদি অ্যাসিঙ্ক টাস্ক কার্যকরের সময় ক্রিয়াকলাপ ব্যাকগ্রাউন্ডে চলে যায়, কার্যকলাপটি কলব্যাকগুলি গ্রহণ করবে (onPreExecute, onProgressUpdate, onPostExecute & onCancelled) একবার পুনরায় শুরু হয়েছে, সুতরাং কোনও অবৈধ স্ট্যাটেক্সটি ছোঁড়া হবে না (দেখুন হ্যান্ডলার কীভাবে পরিচালনা করবেন ক্রিয়াকলাপ / খণ্ড বিরাম দেওয়া থাকলে বার্তা )।

অ্যাসিঙ্কটাস্কের মতো (যেমন: অ্যাসিঙ্কটাস্কফ্রেগমেন্ট <প্যারামস, প্রগতি, ফলাফল>) এর মতো জেনেরিক আর্গুমেন্টগুলির সাথে একই জিনিসটি পাওয়া ভাল হবে তবে আমি তা দ্রুত পরিচালনা করতে পারি নি এবং এই মুহুর্তে আমার কোনও সময় নেই। কেউ যদি উন্নতি করতে চান, দয়া করে নির্দ্বিধায়!

কোড:

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class AsyncTaskFragment extends Fragment {

    /* ------------------------------------------------------------------------------------------ */
    // region Classes & Interfaces

    public static abstract class Task extends AsyncTask<Object, Object, Object> {

        private AsyncTaskFragment _fragment;

        private void setFragment(AsyncTaskFragment fragment) {

            _fragment = fragment;
        }

        @Override
        protected final void onPreExecute() {

            // Save the state :
            _fragment.setRunning(true);

            // Send a message :
            sendMessage(ON_PRE_EXECUTE_MESSAGE, null);
        }

        @Override
        protected final void onPostExecute(Object result) {

            // Save the state :
            _fragment.setRunning(false);

            // Send a message :
            sendMessage(ON_POST_EXECUTE_MESSAGE, result);
        }

        @Override
        protected final void onProgressUpdate(Object... values) {

            // Send a message :
            sendMessage(ON_PROGRESS_UPDATE_MESSAGE, values);
        }

        @Override
        protected final void onCancelled() {

            // Save the state :
            _fragment.setRunning(false);

            // Send a message :
            sendMessage(ON_CANCELLED_MESSAGE, null);
        }

        private void sendMessage(int what, Object obj) {

            Message message = new Message();
            message.what = what;
            message.obj = obj;

            Bundle data = new Bundle(1);
            data.putString(EXTRA_FRAGMENT_TAG, _fragment.getTag());
            message.setData(data);

            _fragment.handler.sendMessage(message);
        }
    }

    public interface AsyncTaskFragmentListener {

        void onPreExecute(String fragmentTag);
        void onProgressUpdate(String fragmentTag, Object... progress);
        void onCancelled(String fragmentTag);
        void onPostExecute(String fragmentTag, Object result);
    }

    private static class AsyncTaskFragmentPauseHandler extends PauseHandler {

        @Override
        final protected void processMessage(Activity activity, Message message) {

            switch (message.what) {

                case ON_PRE_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPreExecute(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
                case ON_POST_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPostExecute(message.getData().getString(EXTRA_FRAGMENT_TAG), message.obj); break; }
                case ON_PROGRESS_UPDATE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onProgressUpdate(message.getData().getString(EXTRA_FRAGMENT_TAG), ((Object[])message.obj)); break; }
                case ON_CANCELLED_MESSAGE : { ((AsyncTaskFragmentListener)activity).onCancelled(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
            }
        }
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Attributes

    private Task _task;
    private AsyncTaskFragmentListener _listener;
    private boolean _running = false;

    private static final String EXTRA_FRAGMENT_TAG = "EXTRA_FRAGMENT_TAG";
    private static final int ON_PRE_EXECUTE_MESSAGE = 0;
    private static final int ON_POST_EXECUTE_MESSAGE = 1;
    private static final int ON_PROGRESS_UPDATE_MESSAGE = 2;
    private static final int ON_CANCELLED_MESSAGE = 3;

    private AsyncTaskFragmentPauseHandler handler = new AsyncTaskFragmentPauseHandler();

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Getters

    public AsyncTaskFragmentListener getListener() { return _listener; }
    public boolean isRunning() { return _running; }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Setters

    public void setTask(Task task) {

        _task = task;
        _task.setFragment(this);
    }

    public void setListener(AsyncTaskFragmentListener listener) { _listener = listener; }
    private void setRunning(boolean running) { _running = running; }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Fragment lifecycle

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public void onResume() {

        super.onResume();
        handler.resume(getActivity());
    }

    @Override
    public void onPause() {

        super.onPause();
        handler.pause();
    }

    @Override
    public void onAttach(Activity activity) {

        super.onAttach(activity);
        _listener = (AsyncTaskFragmentListener) activity;
    }

    @Override
    public void onDetach() {

        super.onDetach();
        _listener = null;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Utils

    public void execute(Object... params) {

        _task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }

    public void cancel(boolean mayInterruptIfRunning) {

        _task.cancel(mayInterruptIfRunning);
    }

    public static AsyncTaskFragment getRetainedOrNewFragment(AppCompatActivity activity, String fragmentTag) {

        FragmentManager fm = activity.getSupportFragmentManager();
        AsyncTaskFragment fragment = (AsyncTaskFragment) fm.findFragmentByTag(fragmentTag);

        if (fragment == null) {

            fragment = new AsyncTaskFragment();
            fragment.setListener( (AsyncTaskFragmentListener) activity);
            fm.beginTransaction().add(fragment, fragmentTag).commit();
        }

        return fragment;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */
}

আপনার পজহ্যান্ডলারের প্রয়োজন হবে:

import android.app.Activity;
import android.os.Handler;
import android.os.Message;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Message Handler class that supports buffering up of messages when the activity is paused i.e. in the background.
 *
 * /programming/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused
 */
public abstract class PauseHandler extends Handler {

    /**
     * Message Queue Buffer
     */
    private final List<Message> messageQueueBuffer = Collections.synchronizedList(new ArrayList<Message>());

    /**
     * Flag indicating the pause state
     */
    private Activity activity;

    /**
     * Resume the handler.
     */
    public final synchronized void resume(Activity activity) {
        this.activity = activity;

        while (messageQueueBuffer.size() > 0) {
            final Message msg = messageQueueBuffer.get(0);
            messageQueueBuffer.remove(0);
            sendMessage(msg);
        }
    }

    /**
     * Pause the handler.
     */
    public final synchronized void pause() {
        activity = null;
    }

    /**
     * Store the message if we have been paused, otherwise handle it now.
     *
     * @param msg   Message to handle.
     */
    @Override
    public final synchronized void handleMessage(Message msg) {
        if (activity == null) {
            final Message msgCopy = new Message();
            msgCopy.copyFrom(msg);
            messageQueueBuffer.add(msgCopy);
        } else {
            processMessage(activity, msg);
        }
    }

    /**
     * Notification message to be processed. This will either be directly from
     * handleMessage or played back from a saved message when the activity was
     * paused.
     *
     * @param activity  Activity owning this Handler that isn't currently paused.
     * @param message   Message to be handled
     */
    protected abstract void processMessage(Activity activity, Message message);
}

নমুনা ব্যবহার:

public class TestActivity extends AppCompatActivity implements AsyncTaskFragmentListener {

    private final static String ASYNC_TASK_FRAGMENT_A = "ASYNC_TASK_FRAGMENT_A";
    private final static String ASYNC_TASK_FRAGMENT_B = "ASYNC_TASK_FRAGMENT_B";

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        Button testButton = (Button) findViewById(R.id.test_button);
        final AsyncTaskFragment fragment = AsyncTaskFragment.getRetainedOrNewFragment(TestActivity.this, ASYNC_TASK_FRAGMENT_A);

        testButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if(!fragment.isRunning()) {

                    fragment.setTask(new Task() {

                        @Override
                        protected Object doInBackground(Object... objects) {

                            // Do your async stuff

                            return null;
                        }
                    });

                    fragment.execute();
                }
            }
        });
    }

    @Override
    public void onPreExecute(String fragmentTag) {}

    @Override
    public void onProgressUpdate(String fragmentTag, Float percent) {}

    @Override
    public void onCancelled(String fragmentTag) {}

    @Override
    public void onPostExecute(String fragmentTag, Object result) {

        switch (fragmentTag) {

            case ASYNC_TASK_FRAGMENT_A: {

                // Handle ASYNC_TASK_FRAGMENT_A
                break;
            }
            case ASYNC_TASK_FRAGMENT_B: {

                // Handle ASYNC_TASK_FRAGMENT_B
                break;
            }
        }
    }
}

3

আপনি এর জন্য লোডার ব্যবহার করতে পারেন। এখানে ডক পরীক্ষা করুন


2
লোডারগুলি এখন অ্যান্ড্রয়েড এপিআই 28 হিসাবে অবহিত করা হয়েছে (লিঙ্কটি আপনাকে বলবে)।
সুমিত

লোডারদের হ্রাস করা হয় না, কেবলমাত্র আপনি কীভাবে তাদের পরিবর্তিত বলে
ডাকছেন

2

যারা খণ্ডগুলি ডজ করতে চান তাদের জন্য, আপনি onRetainCustomNonConfigrationInstance () এবং কিছু তারের ব্যবহার করে ওরিয়েন্টেশন পরিবর্তনে চলমান AsyncTask ধরে রাখতে পারেন ।

(লক্ষ্য করুন যে এই পদ্ধতিটি হ'ল রিটেনন কনফিগারেশনআইনস্ট্যান্স () ) অবমূল্যায়নের বিকল্প ।

মনে হচ্ছে এই সমাধানটির প্রায়শই উল্লেখ করা হয়নি। আমি উদাহরণের জন্য একটি সহজ চলমান উদাহরণ লিখেছি।

চিয়ার্স!

public class MainActivity extends AppCompatActivity {

private TextView result;
private Button run;
private AsyncTaskHolder asyncTaskHolder;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    result = (TextView) findViewById(R.id.textView_result);
    run = (Button) findViewById(R.id.button_run);
    asyncTaskHolder = getAsyncTaskHolder();
    run.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            asyncTaskHolder.execute();
        }
    });
}

private AsyncTaskHolder getAsyncTaskHolder() {
    if (this.asyncTaskHolder != null) {
        return asyncTaskHolder;
    }
    //Not deprecated. Get the same instance back.
    Object instance = getLastCustomNonConfigurationInstance();

    if (instance == null) {
        instance = new AsyncTaskHolder();
    }
    if (!(instance instanceof ActivityDependant)) {
        Log.e("", instance.getClass().getName() + " must implement ActivityDependant");
    }
    return (AsyncTaskHolder) instance;
}

@Override
//Not deprecated. Save the object containing the running task.
public Object onRetainCustomNonConfigurationInstance() {
    return asyncTaskHolder;
}

@Override
protected void onStart() {
    super.onStart();
    if (asyncTaskHolder != null) {
        asyncTaskHolder.attach(this);
    }
}

@Override
protected void onStop() {
    super.onStop();
    if (asyncTaskHolder != null) {
        asyncTaskHolder.detach();
    }
}

void updateUI(String value) {
    this.result.setText(value);
}

interface ActivityDependant {

    void attach(Activity activity);

    void detach();
}

class AsyncTaskHolder implements ActivityDependant {

    private Activity parentActivity;
    private boolean isRunning;
    private boolean isUpdateOnAttach;

    @Override
    public synchronized void attach(Activity activity) {
        this.parentActivity = activity;
        if (isUpdateOnAttach) {
            ((MainActivity) parentActivity).updateUI("done");
            isUpdateOnAttach = false;
        }
    }

    @Override
    public synchronized void detach() {
        this.parentActivity = null;
    }

    public synchronized void execute() {
        if (isRunning) {
            Toast.makeText(parentActivity, "Already running", Toast.LENGTH_SHORT).show();
            return;
        }
        isRunning = true;
        new AsyncTask<Void, Integer, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                for (int i = 0; i < 100; i += 10) {
                    try {
                        Thread.sleep(500);
                        publishProgress(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                if (parentActivity != null) {
                    ((MainActivity) parentActivity).updateUI(String.valueOf(values[0]));
                }
            }

            @Override
            protected synchronized void onPostExecute(Void aVoid) {
                if (parentActivity != null) {
                    ((MainActivity) parentActivity).updateUI("done");
                } else {
                    isUpdateOnAttach = true;
                }
                isRunning = false;
            }
        }.execute();
    }
}

0

আমি লাইব্রেরি প্রয়োগ করেছি যা আপনার কার্য সম্পাদন করার সময় ক্রিয়াকলাপ বিরতি এবং বিনোদন দিয়ে সমস্যার সমাধান করতে পারে।

আপনার বাস্তবায়ন করা উচিত AsmykPleaseWaitTaskএবং AsmykBasicPleaseWaitActivity। আপনার ক্রিয়াকলাপ এবং পটভূমি টাস্ক সূক্ষ্মভাবে কাজ করবে এমনকি আপনি পর্দার ঘোরাবেন এবং অ্যাপ্লিকেশনগুলির মধ্যে স্যুইচ করবেন


-9

দ্রুত কাজ (পুনরায় সংশোধন করা হয়নি)

কোনও ক্রিয়াকলাপ ধ্বংস করতে এবং নিজে তৈরি করা এড়াতে আপনার ক্রিয়াকলাপটি ম্যানিফেস্ট ফাইলটিতে ঘোষণা করা হয়: অ্যান্ড্রয়েড: কনফিগারেশন = "ওরিয়েন্টেশন | কীবোর্ডহাইডার | স্ক্রিন সাইজ

  <activity
        android:name=".ui.activity.MyActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:label="@string/app_name">

যেমন এটি ডক্সে উল্লেখ করা হয়েছে

স্ক্রিন ওরিয়েন্টেশন পরিবর্তিত হয়েছে - ব্যবহারকারী ডিভাইসটি ঘোরান।

দ্রষ্টব্য: যদি আপনার অ্যাপ্লিকেশনটি এপিআই লেভেল 13 বা ততোধিক (minSdkVersion এবং টার্গেটএসডিকি ভার্সন বৈশিষ্ট্যগুলির দ্বারা ঘোষিত) হিসাবে লক্ষ্য করে তবে আপনার "স্ক্রিনসাইজ" কনফিগারেশনটিও ঘোষণা করা উচিত, কারণ কোনও ডিভাইস প্রতিকৃতি এবং ল্যান্ডস্কেপ ওরিয়েন্টেশনের মধ্যে স্যুইচ করলে এটিও পরিবর্তিত হয়।


1
এটি সেরা এড়ানো হয়। বিকাশকারী.অ্যান্ড্রয়েড / গাইড / টপিক্স / রিসোর্স / "" দ্রষ্টব্য: কনফিগারেশনটি নিজেকে পরিবর্তন করা বিকল্প রিসোর্সগুলি ব্যবহার করা আরও জটিল করে তুলতে পারে, কারণ সিস্টেমগুলি সেগুলি স্বয়ংক্রিয়ভাবে আপনার জন্য প্রয়োগ করে না This এই কৌশলটি শেষ হিসাবে বিবেচনা করা উচিত কনফিগারেশনের পরিবর্তনের কারণে পুনঃসূচনাগুলি এড়াতে হবে এবং বেশিরভাগ অ্যাপ্লিকেশনগুলির জন্য প্রস্তাবিত না হলে রিসর্ট করুন।
ডেভিড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.