AsyncTask چیست؟
یک task غیر همزمان (Asynchronous) به محاسباتی گفته می شود که در بک گراند انجام شود و نتایجش در Thread UI منتشر شود.
این کلاس این اجازه را به شما می دهد که عملیات بک گراندی را انجام دهید و نتایج آن را در نخِ UI منتشر کنید؛ بدونِ اینکه مجبور باشید با نخ ها یا Handler ها سر و کله بزنید.
AsyncTask به شکل یک کلاس کمکی حولِ Thread و Handler طراحی شده است و به صورت مجزا یک چهارچوب Threading ارائه نمی کند.
طبق مستندات گوگل، به صورت ایده آل، AsyncTask باید برای عملیات کوتاه استفاده شود (حداکثر چند ثانیه). اگر بخواهید برای مدتی طولانی نخ ها را در جریان نگه دارید، توصیه می شود که از API هایی که داخل پکیج java.Util.concurent ارائه شده استفاده کنید، چیزهایی مثل Executor؛ FutureTask؛ و ThreadPoolExecutor.
Asynctask برای عملیات زمان بری که فقط یک بار به اجرا نیاز دارند و نمیتوانند در نخ UI انجام شوند طراحی شده است. مثلا دریافت و یا پردازش داده ها وقتی که یک دکمه انتخاب شد مثالِ خوبی از این عملکرد است.
AsyncTask با Service ها چه تفاوتی دارد؟
همین ابتدا بگویم، مبحث دیگری در اندروید وجود دارد به نام Service ها، آموزش ویدئویی این مبحث را می توانید در آپارات مشاهده کنید.
برای افرادی که اندک آشنایی با بحث سرویس ها دارند، ممکن است سوال پیش بیاید که تفاوت سرویس و AsyncTask در چه چیزی است.
در بعضی از حالت ها، پیاده سازی یک عمل، با هر دو روش Service و AsyncTask امکان پذیر است. با این حال، معمولا یکی؛ از دیگری بسته به نوع task؛ مناسب تر است.
AsyncTask ها برای به انجام رساندنِ کارهای زمان برِ یک بار رخ دهنده (یعنی کارهایی که فقط یک بار نیاز به اجرا دارند) که نمی توانند در نخِ UI اجرا شوند طراحی شده است. یک مثال متداول برای استفاده از این عملکرد، دریافت و یا پردازش اطلاعات پس از کلیک کردن روی یک دکمه است.
service ها اما برای این طراحی شده اند که به صورت مداوم در بکگراند اجرا شوند. در مثال بالا برای دریافت اطلاعات وقتی روی یک دکمه لمس شد، می توانستیم از سرویس ها هم استفاده کنیم، به این صورت که یک سرویس را start کنیم، اجازه دهیم داده ها را دریافت کند و سپس آن را متوقف کنیم، اما این کار یک کار ناکارآمد محسوب می شود. اگر در این مورد از AsyncTask استفاده کنیم که یک بار اجرا می شود، اطلاعات را برمی گرداند و تمام می شود، بسیار سریعتر است.
اگر نیاز داشته باشیم که به صورت مداوم کاری را در بکگراند انجام دهیم، انتخاب Service یک انتخاب درست است. مثال هایی از این می توانند پخش موزیک، و چک کردن برای داده های جدید به صورت مداوم می باشد.
سرویس ها لزوما خارج از نخِ UI اجرا نمی شوند (اگر برایشان یک نخ جداگانه تعریف نکنیم).
در اکثر مواقع؛ service ها برای وقتی هستند که می خواهید یک کد را، حتی اگر اکتیویتی نرم افزار شما باز نیست اجرا کنید. AsyncTask اما برای اجرای بسیار ساده ی کدها خارج از نخِ UI طراحی شده است.
(توضیح بالا از سایت StackOverflow ترجمه شده است)
ساخت و استفاده از AsyncTask
باید از AsyncTask فرزند بسازیم تا بتوانیم از آن استفاده کنیم. این فرزند، حداقل یکی از متدهای موجود در AsyncTask را باید پیاده سازی (override) کند (تابع doInBackground )، البته اغلب اوقات نیاز است تا تابع دومی هم باز نویسی شود (تابع onPostExecute ).
مثالی از این فرزند سازی را در زیر می بینیم:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }
وقتی ساخته شد، استفاده از این task در اکتیویتی بسیار ساده خواهد بود:
new DownloadFilesTask().execute(url1, url2, url3);
اگر دقت کرده باشید، به عنوان ورودی در هنگام ساخت فرزند، در این بخش:
AsyncTask<URL, Integer, Long>
ما سه نوع داده مشخص کرده ایم، به این نوع داده ها، پارامتر های داده های عمومی یا generic types گفته می شود.
این سه نوع داده، به ترتیب Params ؛ Progress ، و Result نامیده می شود.
برای استفاده از AsyncTask، اول ما باید کاری را به انجام رسانیم، دوما مراحل پیشرفت آن کار را به UI اطلاع دهیم و در نهایت نتیجه را به UI برگردانیم، حالا این سه ورودی به ترتیب مشخص می کنند که:
1- نوع پارامتری که قرار است Asynctask به عنوان ورودی تابع doInBackground خود بگیرد تا با استفاده از آن کار را به انجام رساند چه باید باشد.
2- نوع واحدی که حین انجام رساندنِ کار، به عنوان سیگنالِ پیشرفت کار باید به UI ارسال شود چیست؟ (مثلا برای اینکه نشان دهیم چند درصد از دانلود پیش رفته است، شاید رشته یا عدد صحیح انتخاب مناسبی باشد.)
3- داده ای که میخواهیم این AsyncTask به عنوان نتیجه برای ما ارسال کند از چه نوعی باید باشد؛ مثلا حجم فایل دانلود شده می تواند با نوع داده ای Long به عنوان نتیجه برایمان ارسال شود.
اگر یک یا چند تا از این سه نوع هم در مورد کاری ما استفاده نمی شوند، می توانیم در تعریف به جایشان از Void استفاده کنیم:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
مراحل اجرای AsyncTask
وقتی یک Asynchronous task اجرا می شود، اجرای task این 4 مرحله را طی می کند:
1- onPreExecute : این اولین تابعی است که در حین اجرای تسک اجرایی می شود، این تابع در نخِ UI اجرا شده و برای آماده سازی task استفاده می شود، مثلا فعال کردن نمایش یک ProgressBar در رابط کاربری می تواند مثال خوبی برای کاربرد آن باشد.
2- doInBackground : این تابع در نخِ background (نخی جدا از نخِ UI) و به محص تمام شدنِ اجرایِ onPreExecute اجرا می شود.اینجا جایی است که محاسبات پس زمینه ای که میتواند مدت زمان زیادی نیز طول بکشد انجام می گیرد. نتیجه محاسبات در این مرحله باید برگشت داده شود که این داده return شده به مرحله آخر ارسال می شود.
این مرحله می تواند از publishProgress هم برای ارسال یک یا چند واحد progress استفاده کند. این مقادیر ارسال شده در نخِ UI منتشر شده و در تابع onProgressUpdate دریافت می شود.
3- onProgressUpdate این تابع در نخِ UI و بعد از اینکه تابع publishProgress فراخوانی شد اجرا می شود. مدت زمان اجرایی شدنِ کامل Task معلوم نیست. به همین دلیل این تابع برای نشان دادن میزان پیشرفت کار در حال انجام در بک گراند به کاربر و در رابط کاربری تعبیه شده است. برای مثال این تابع می تواند برای نشان دادن میزان پیشرفت انجام یک عملیات توسط progressbar یا نمایش Log به کاربر استفاده شود.
4- onPostExecute : این تابع در نخِ UI و پس از اینکه محاسباتِ بک گراند (تابع doInBackground ) انجام شد اجرایی می شود. نتیجه برگشت داده شده توسط تابع doInBackground به این تابع به عنوان ورودی ارسال می شود.
متوقف کردن اجرای AsyncTask
اینکار می تواند با فراخوانی تابع ;(cansel(boolean انجام شود. فراخوانی این تابع باعث می شود تا تابع isCanceled در فراخوانی اهی بعدی اش true برگرداند.
بعد از فراخوانی تابع cancel، به جای اجرا شدنِ onPostExecute ، تابع onCanceled بعد از return شدن تابع از طرفِ doInBackgroud فراخوانی می شود.
قوانین نخ ها
چند قانون در نخ بندی نرم افزار وجود دارد که برای عملکردِ مناسب در استفاده از این کلاس باید رعایت کنید:
- کلاس AsyncTask حتما باید در نخِ UI بارگذاری یا load شود.
- داده ی task (شیء task) باید حتما در نخِ UI تولید شود.
- اجرایی کردنِ نخ با استفاده از تابع execute باید حتما در نخِ UI انجام شود.
- توابع onPreExecute ، onPostExecute ، doInBackground و onProgressUpdate را نباید به صورت دستی فراخوانی کنید.
- یک task فقط می تواند یک بار اجرا شود، تلاش دوباره برای اجرایی کردنِ یک task برای بار دوم موجب پرتاب یک exception می شود)
قابل رویت بودنِ حافظه ها در AsyncTask
Asynctask تضمین می کند همه فراخوانی ها، به صورتی همگام سازی شده که بدون نیاز به پیاده سازیِ یک روند همگام شدهی مجزا یا جدید؛ همهی این عملیات به صورت امن و قطعی اجرا میشوند:
1- مقدار دهی متغیر ها در سازنده یا در تابع onPreExecute و ارجاع به آنها در doInBackground
2- مقدار دهی متغیر ها در doInBackground و استفاده از آنها در onProgressUpdate و onPostExecute .
پروژه عملی نحوه کار با AsyncTask
به عنوان پروژه ای ساده برای درک مفهوم عملکردِ AsyncTask، برنامه ای برای دانلود یک فایل از طریق اینترنت ونمایش میزان پیشرفت دانلود را پیاده سازی می کنیم.
ویدئوی مربوط به این آموزش را می توانید در آپارات مشاهده کنید:
اکثر مطالب عنوان شده در این بخش از مستندات گوگل برداشته و ترجمه شده است.
تشکر و آرزوي توفيق جاويدان براي شما
ممنون قربان، انشاألله مفید بوده باشه
سلام. ممنون از آموزش خیلی خوبتون. هم آموزش وبلاگ و هم ویدئوی آموزشی عالی بودند و جزئیات لازم رو بیان کردید. فقط کاش از متدهای deprecate شده استفاده نمیکردید ( عجیبه که حتی نسخه نصبی امولاتورتون هم جدید بود، ولی بعضی از متدهای تو کد رو deprecate میزدید)… ولی این از ارزش آموزش خوبتون که بصورت کاربردی اونهم رایگان منتشر کردید کم نمیکنه. واقعا از زحماتتون و وقتی که برای تهیه این آموزش ها گذاشتید و میگذارید متشکرم و براتون آرزوی موفقیت میکنم.
سلام،
خواهش میکنم. بله یک سری از توابع deprecate شده بودن که چون هدف آموزش مفهوم کلیِ Asynctaskها بود، خیلی روشون حساسیت به خرج ندادم.
سعی میکنم از این پس این نکته رو لحاظ کنم در آموزشها.
خیلی ممنون از شما و امیدوارم مفید بوده باشه.
خواهش میکنم، ممنون. موفق باشید.
دمت گرم حاجی خیلی خوب بود
خواهش میکنم قربان 🙂
سلام یک سوال زمانی که توسط asyn task در یک فرگمنت ارتباط ایجاد میشود مثلا ارتباط یک سوکت آیا با خارج شدن از آن فرگمنت آن ارتباط هم قطع میشود ؟و باید مجدد ارتباط بر قرار شود؟
الان من در یک asyn task به سوکت وصل میشوم در فرگمنت و داده را با ارتباط ایجاد شده ارسال می کنم اما از فرگمنت خارج میشود و دوباره وارد میوشد نمیتونم داده ارسال کنم و نه میتونم مجدد به سوکت وصل شوم و فقط باید از برنامه خارج شوم تا بتونم مجدد ارتباط رو بر قرار کنم
با سلام و وقت بخیر، در مجموع Asynctask لایف سایکلش وابسته به contextای که اون رو ایجاد کرده نیست، یعنی اگر اکتیویتی از بین بره، Asynctask به کارش ادامه میده. (منظور از اکتیویتی هر کانتکستی هست که داخلش asynctask اجرا میشه). دقت کنید که اگر از فرگمنت خارج میشید، حتما asynctask رو متوقف کنید تا مشکل پیش نیاد.
این لینک میتونه دید خوبی بهتون بده:
https://stackoverflow.com/questions/2531336/asynctask-wont-stop-even-when-the-activity-has-destroyed
cheqadr khoob bood alireza jan aziz, kheyli lezat bakhsh bood baram, tashakor az in amoozesh mofid k ijad kardi
ممنونم از توجه شما 🙂
سلام یه سوال الان که از این کلاس استفاده میکنم یه خط روش کشیده شده یعنی گوگل دیگه ساپورت نمیکنه
سلام وقت شما بخیر.
بله این ای پی آی منسوخ شده، مطلبی هم در وبسایت در این مورد نوشتم، میتونید مطالعه کنید:
https://alirezapir.info/2019/11/25/asynctask-deprecated-in-android-by-google/