در طی دهه‌ی گذشته، AsyncTask یک راهکار بسیار متداول برای پیاده‌سازی فرایند‌های همزمان بود. در پستی با عنوان Asynctask در اندروید در مورد این API و نحوه‌ی کار با آن نوشتم.  با این حال؛ این API شهرتی جنجالی به دست آورد. از یک طرف؛ Asynctask در خیلی از نرم‌افزارهای اندرویدی استفاده می‌شد و همچنان هم می‌شود. از طرفِ دیگر، بیشتر برنامه‌نویس‌های حرفه‌ای اندروید آشکارا از این API بدشان می‌آید.

اما با همه‌ی این تضاد‌ها، خبرِ مهم این است که در Commit جدید نسخه‌ی متن‌باز اندروید، این API منسوخ اعلام شده است.

در این پست، من دلایل رسمیِ عنوان شده و همینطور دلایل واقعیِ منسوخ شدنِ AsyncTask را عنوان می‌کنم. همانطور که خواهید دید؛ دلایل مختلفی برای این اقدام وجود داشته. در انتهایِ متن نیز، پیش‌بینی خودم از آینده‌ی API‌های هم‌زمانی در اندروید را بیان می‌‎کنم.

 

دلایل رسمی برای منسوخ شدن AsyncTask

دلیلِ رسمیِ منسوخ شدنِ Asynctask، و به علاوه انگیزه‌ی گرفتنِ این تصمیم، در این کامیت (commit) عنوان شده. پاراگراف اول اضافه شده در javadoc اعلام می‌کند:

هدف از ایجاد AsyncTask فراهم شدنِ امکان استفاده‌ی راحت از نخِ UI بود. با این حال، اکثرا از آن برای ارتباط با UI استفاده می‌شد، و این عمل با تغییر تنظیمات گوشی‌ها، باعثِ نشت حافظه؛ بازفراخوانی‌های از دست رفته؛ یا کرش می‌شد. بعلاوه این API در نسخه‌های مختلف اندروید، عملکرد بی ثباتی داشت. خطاها در تابع doInBackground را نادیده می‌‎گرفت؛ و کارایی زیادی بیشتر از استفاده‌ی مستقیم از Executorها فراهم نمی‌کرد.

با اینکه این بیانیه‌ی رسمی ارائه شده توسط گوگل است، چند بی‌دقتی در آن وجود دارد که ارزش بیان کردن را دارد.

اول از همه، AsyncTask هرگز به منظور “فراهم شدنِ امکان استفاده‌ی راحت از نخِ UI ” ساخته نشده بود. دلیلِ ایجادِ آن این بود که عملیات بلند مدت را از دوش نخِ UI بردارد و آن را به نخِ background محول کند و نتیجه‌ی این عملیات را به نخِ UI منتقل کند. میدانم که اینجا ریزبینی و عیب‌جویی می‌کنم. اما به نظرِ من بهتر بود گوگل برای جلوگیری از گیج شدن؛ وقت بیشتری برای نوشتنِ پیامی برای منسوخ شدن یک API که خودش آن را تولید کرده و سال‌ها از آن پشتیبانی می‌کرده؛ و سال‌ها توسعه دهندگان از آن استفاده کرده و سال‌های دیگر هم استفاده می‌کنند می‌گذاشت.

جدایِ این، بخشِ جالب‌تر پیامی که برای منسوخ شدن بیان شده اینجاست: ” با تغییر تنظیمات گوشی‌ها، باعثِ نشت حافظه؛ بازفراخوانی‌های از دست رفته؛ یا کرش می‌شد”. بنابراین؛ گوگل این را بیان می‌کند که اکثر موارد استفاده‌ی متداول از AsyncTask منجر به مشکلات جدی می‎‌شد. ولی؛ نرم‌افزار‌های با کیفیت بسیار زیادی هستند که از AsyncTask استفاده می‌کنند و بسیار هم عالی کار می‌کنند. حتی بعضی از کلاس‌ها در AOSP (جایی که گوگل نمونه‌ کد‌های رسمی برای آموزش برنامه‌نویسی اندروید را در آنجا قرار می‌دهد) هم از AsyncTask استفاده می‌کنند. چطور است که اینها این مشکلات را ندارند؟

برای جواب به این سوال، بگذارید در مورد ارتباط بین AsyncTask و نشت حافظه (یا memory leaks) به صورت متمرکز‌تری صحبت کنیم.

 

AsyncTask و نشت حافظه

این کد ‌شیء Fragment یا Activityای را که توسطِ آن AsyncTask اجرا شده تا ابد نگه می‌دارد:

@Override
public void onStart() {
super.onStart();
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
int counter = 0;
while (true) {
Log.d("AsyncTask", "count: " + counter);
counter ++;
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

به نظر می‌رسد که این مثال، صحبتِ گوگل را اثبات می‌کند: AsyncTask درواقع باعث نشت حافظه می‌شود. باید از روش دیگری برای نوشتنِ کد همزمان (concurrent) استفاده کنیم! پس، بیایید دوباره سعی کنیم.

این همان مثال است که توسط RxJava نوشته شده:

@Override
public void onStart() {
super.onStart();
Observable.fromCallable(() -> {
int counter = 0;
while (true) {
Log.d("RxJava", "count: " + counter);
counter ++;
}
}).subscribeOn(Schedulers.computation()).subscribe();
}

این هم شیء Fragment یا Activity ایجاد کننده را تا ابد نگه می‌دارد.

شاید قابلیت جدید زبان Kotlin، یعنی Coroutineها کمکمان کند. این کاری است که با کمک Coroutine در کاتلین انجام دادیم:

override fun onStart() {
super.onStart()
CoroutineScope(Dispatchers.Main).launch {
withContext(Dispatchers.Default) {
var counter = 0
while (true) {
Log.d("Coroutines", "count: $counter")
counter++
}
}
}
}

متأسفانه این هم دقیقا منجر به نشتِ حافظه می‌شود.

به نظر می‌رسد که این عملیات، صرف نظر از چهارچوب مورد استفاده برای فرایند چندنخی، منجر به نشتِ حافظه می‌شود. در واقع؛ حتی اگر از کلاسِ ساده‌ی Thread هم استفاده کنم، باز هم همین نتیجه را خواهیم دید:

@Override
public void onStart() {
super.onStart();
new Thread(() -> {
int counter = 0;
while (true) {
Log.d("Thread", "count: " + counter);
counter++;
}
}).start();
}

بنابراین، مسئله اصلا AsyncTask نیست؛ مسئله؛ منطقی است که من پیاده‌سازی کردم. برای حل مشکل، بگذارید مثالِ اول خودمان که با AsyncTask نوشته شده بود را ویرایش کنیم تا دیگر نشتِ حافظه در آن اتفاق نیوفتد:

private AsyncTask mAsyncTask;

@Override
public void onStart() {
super.onStart();
mAsyncTask = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
int counter = 0;
while (!isCancelled()) {
Log.d("AsyncTask", "count: " + counter);
counter ++;
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

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

در این حالت، من از همان AsyncTask مخوف استفاده کردم؛ اما دیگر از نشتِ حافظه خبری نیست. این معجزه است!

خب در واقع از معجزه خبری نیست. فقط این مورد نشان‌دهنده‌ی این واقعیت است که، می‌شود کدِ درست و امنی را برای فرایند‌های چند‌نخی با کمک AsyncTask نوشت، دقیقا همانطور که می‌توان به کمکِ هر چهارچوبِ چند‌نخیِ دیگری اینکار را انجام داد. رابطه‌ی خاصی بینِ AsyncTask و نشت حافظه وجود ندارد. بنابراین اعتقادِ متداول که AsyncTask خود به خود باعث نشت حافظه می‌شود، و همینطور پیامِ منسوخ شدنی که در AOSP عنوان شده؛ خیلی ساده نادرست است.

ممکن است با خود فکر کنید: اگر این تفکر که AsyncTask باعث نشتِ حافظه می‌شود غلط است، پس چرا به این اندازه در میانِ توسعه‌دهندگان اندروید متداول است؟

خب، یک قانون خطی در اندروید استودیو هست که هشدار می‌دهد برای جلوگیری از نشتِ حافظه، AsyncTask را به شکلِ static تعریف کنید. این هشدار و توصیه هم غلط است، اما توسعه‌دهندگانی که از AsyncTask در پروژه‌ی خودشان استفاده می‌کنند این هشدار را می‌بینند و از آنجا که از طرفِ گوگل گفته شده؛ آن را جدی می‌گیرند.

هشدار asycntask

به نظرِ من؛ خطِ هشدارِ بالا باعث شده تا افسانه‌ی ارتباط AsyncTask با نشتِ حافظه اینقدر گسترش پیدا کند. این اتفاق توسط خودِ گوگل رقم خورده.

چطور در کد‌های چندنخی، از نشتِ حافظه جلوگیری کنیم

تا الان فهمیدیم که هیچ ارتباط معنادار و خودکاری بینِ AsyncTask و نشت حافظه وجود ندارد. بعلاوه دیدید که نشتِ حافظه در هر چهارچوبِ چند‌نخیِ دیگری نیز ممکن است اتفاق بی‌افتد. حالا ممکن است برایتان سوال باشد که چطور جلوی نشتِ حافظه را بگیریم.

این سوال را خیلی جزئی جواب نمی‌دهم، چون نمیخواهم از بحثِ اصلی دور شوم، اما نمیخواهم هم که دستتان در این مورد خالی بماند. بنابراین اجازه دهید مفاهیمی را که نیاز است برای نوشتن کد‌های جاوا و کاتلین چند‌نخیِ بدونِ نشتِ حافظه یاد بگیرید، لیست کنم:

  • Garbage Collector
  • ریشه‌های Garbage collection
  • چرخه‌ی حیات نخ‌ها با توجه به garbage collection
  • مراجع ضمنی از کلاس داخلی به شیء پدر

اگر این مفاهیم را بلد باشید، احتمال اینکه کدی بنویسید که دچار نشتِ حافظه شود بسیار پایین می‌آید. از طرفِ دیگر، اگر این مفاهیم را ندانید، هر لحظه این امکان وجود دارد که با این مشکل روبرو شوید، فارغ از اینکه از چه چهارچوبِ چند‌نخی استفاده کنید.

 

آیا AsyncTask بدون دلیل منسوخ شد؟

از آنجا که AsyncTask به شکل خودکار باعث نشت حافظه نمی‌شود، به نظر می‌رسد که گوگل به اشتباه و بی‌دلیل آن را منسوخ کرد. خب، نه دقیقا.

در سال‌های گذشته، AsyncTask به خودیِ خود توسطِ توسعه‌دهندگان، “عملا” منسوخ شده بود. خیلی از ما مقابل استفاده از این API جبهه می‌گرفتیم. من، به شخصه برای توسعه‌دهنده‌هایی که خیلی زیاد از AsyncTask در برنامه‌هایشان استفاده می‌کردند، ابراز تأسف می‌کردم. این روند برای سال‌ها همینطور بود، و سندِ این بود که AsyncTask یک API پرمشکل است. بنابراین، منسوخ کردنِ AsyncTask کاری منطقی بود. اگر از من بپرسید، گوگل باید سال‌ها پیش اینکار را می‌کرد.

بنابراین، با اینکه گوگل در مورد چیزی که درست کرده همچنان سردرگم است، منسوخ شدنِ آن خیلی مناسب و خوب است. حداقل اینکار باعث می‌شود توسعه دهندگان جدید اندروید متوجه شوند که نیازی نیست زمانِ خود را صرفِ یادگیری این API کنند و از آن در نرم‌افزار‌های خود استفاده نمی‌کنند.

با همه‌ی اینها، احتمالا هنوز هم متوجه نشدید که چرا AsyncTask “بد” بود و چرا بسیاری از توسعه‌دهندگان تا این حد از آن بدشان می‌آمد. به نظرِ من، این سوال خیلی جالب و کاربردی است. اگر ندانید که دقیقا مشکلِ AsyncTask چه بود، ضمانتی وجود ندارد که همان اشتباهات را دوباره تکرار کنید.

بنابراین، اجازه دهید من چیز‌هایی که شخصا دلیلِ بد بودنِ AsyncTask می‌دانم را لیست کنم:

 

مشکلِ 1 در AsyncTask: پیاده‌سازی فرایند چند‌نخی را پیچیده‌تر می‌کند

یکی از نکاتِ “پرفروش و دهان‌ پر کن” که در مورد AsyncTask گفته می‌شد، این بود که قول داده می‌شود که با استفاده از آن، از درگیر شدن شما با کلاس Thread و سایر متغیر‌های مرتبط با چند‌‎نخی جلوگیری شود. AsyncTask می‌خواست پیاده‌سازی فرایند چند‌نخی را ساده‌تر کند، مخصوصا برای توسعه‌دهندگان تازه‌کار. عالی به نظر می‌رسد، درست است؟

به هر حال، در عمل، این “سادگی” باعث چندین برابر دوباره‌کاری شد.

کدِ مستندات کلاسِ AsyncTask، به تعداد 16 بار از کلمه‌ی “thread” استفاده کرده است. درواقع اگر مفهوم thread را ندانید، هرگز نمی‌توانید عملکردِ AsyncTask را متوجه شوید. بعلاوه، این مستندات بسیاری از قوانین مخصوص و شرایط خاص را که فقط مختصِ AsyncTask هستند بیان می‌کند. به عبارت دیگر، اگر بخواهید از AsyncTask استفاده کنید، باید thread را متوجه شوید و علاوه بر آن، باید بسیاری تفاوت‌های ضریف AsyncTask با thread را هم متوجه شوید. از هر طرفی نگاه کنیم، این اصلا نمی‌تواند به معنیِ “ساده‌تر” شدن باشد!

علاوه بر این، نوشتنِ فرایند‌های چند‌نخی یک بحث پیچیده است. به نظرِ من حتی، این مبحث یکی از پیچیده‌ترین عناوین در نرم‌افزار به شکلِ کلی است (و به همین دلیل، در سخت‌افزار هم به همین صورت). حالا، برخلافِ بسیاری مباحث و عناوینِ دیگر، شما نمی‌توانید در نوشتنِ برنامه‌های چند‌نخی میانبر بزنید، به این دلیل که حتی کوچکترین اشتباه منجر به باگ‌های خیلی جدی می‌شود که کشف و برطرف کردنِ آن بسیار سخت است (نمونه‌ی مشابه این در برنامه‌نویسی اپن جی ال برای بازی تا به تا برای من اتفاق افتاده بود که کار کشف مشکل را برایم خیلی خیلی سخت کرد). نرم‌افزار‌هایی بودند که ماه‌ها حتی بعد از اینکه توسعه‌دهندگان آن متوجه مشکل شده بودند، باگ‌های مرتبط با چند‌نخی را داشتند. فقط نمی‌توانستند دقیقا بفهمند این باگ‌ها از کجا می‌آید.

بنابراین، به نظرِ من، هیچ راهی برای ساده‌سازی فرایند‌های همزمان وجود ندارد و از همان ابتدا هم AsyncTask محکوم به بلااستفاده بودن بود.

 

مشکلِ 2 در AsyncTask: مستند‌سازی بد

شکی نیست که مستندات اندروید غیر بهینه است (تلاش می‌کنم تا در صحبتم مؤدب باشم). طی سال‌ها بهتر شده، اما حتی امروز هم نمی‌توانم بگویم تر و تمیز است. به نظرِ من،  مستنداتِ نامناسب، اصلی‌ترین عامل در تاریخِ پردردسر َAsyncTask بود. اگر AsyncTask، یک چهارچوبِ بد-مهندسی شده، پیچیده و پر از قلق‌های اختصاصی، همانطور که الان هست بود، اما مستنداتِ خوبی داشت، می‌توانست بخشی از اکوسیستم باقی بماند. هرچند راه حلی برای APIهای زشتی که توسعه‌دهندگان می‌نویسند وجود ندارد، اما مستندات AsyncTask وحشتناک بود و مشکلاتی که داشت را بدتر کرد.

بدتر از همه مثال‌های خودِ گوگل. آنها بدترین رویکرد برای نوشتنِ فرایند‌های چند‌نخی را معرفی می‌کردند: همه‌ی کدها داخل Activityها، بی‌اعتنایی کامل به چرخه‌ی حیات (life cycle)، بدونِ در نظر گرفتنِ سناریو‌های لغو عملکرد و… اگر شما از این مثال‌ها به همان صورتی که هست در برنامه‌ی خود استفاده می‌کردید، نشت حافظه و عملکردِ نادرست کاملا تضمین شده بود.

بعلاوه، مستندات AsyncTask شامل هیچ توضیحی در مورد مفاهیم مرکزی مرتبط با چند‌نخی نمی‌شد (آنهایی که قبل‌تر لیست کردم  و سایرشان). در واقع، فکر می‌کنم هیچ بخشی از مستندات اینکار را نکرده. حتی یک لینک هم به مستندات اوراکل در این مورد برای توسعه‌دهندگانی که می‌خواهند برای فهمِ همزمانی مستندات “رسمی” را بخوانند داده نشده (قراردادن گیومه‌ها ضروری است چون مستندات Oracle برای اندروید رسمی نیست).

به هر حال، به نظر من، خطِ هشداری که قبلا گفتم؛ همانی که شایعه درمورد نشت حافظه را عنوان می‌کرد هم بخشی از مستندات است. بنابراین، نه تنها مستندات ناکافی بودند، که شامل اطلاعات غلط هم بودند.

 

مشکلِ 3 در AsyncTask: پیچیدگی مفرط

AsyncTask سه عدد آرگومان عمومی دارد (generic argument). سه تا! اگر اشتباه نکنم، هیچ کلاس دیگری را ندیده‌ام که این تعداد آرگومان عمومی داشته باشد.

هنوز اولین برخوردم با AsyncTask را به یاد دارم. در آن زمان من کمی در مورد threadها در جاوا اطلاع داشتم و نمی‌‎توانستم متوجه شوم چرا نوشتنِ فرایند چندنخی (multithreading) در اندروید اینقدر سخت است. فهمیدن سه آرگومان عمومی خیلی سخت بود و من را سردرگم کرد. بعلاوه، از آنجا که توابع AsyncTask در نخ‌های مختلفی فراخوانی می‌شوند، مجبور بودم که همیشه به خودم آن را یادآوری کنم و بعد از نوشتن، به مستندات مراجعه کنم تا ببینم اشتباهی نکرده باشم.

امروز، وقتی که خیلی بیشتر در مورد همزمانی و نخِ UI در اندروید می‌دانم، می‌توانم احتمالا این اطلاعات را با مهندسی معکوس به یاد آورم. اما این اطلاعات خیلی بعد‌تر به من منتقل شد. وقتی که به شدت درگیر استفاده از AsyncTask شده بودم.

و با همه‌ی این پیچیدگی‌ها، همچنان فقط باید تابع execute را در نخِ UI فراخوانی کنید!

 

مشکلِ 4 در AsyncTask: سوء استفاده در وراثت

فلسفه‌ی AsyncTask روی وراثت بنا گذاشته شده: هروقت نیاز بود تا کاری را در بکگراند انجام دهید، از AsyncTask ارث‌بری کنید.

همراه با مستندات بد، فلسفه‌ی ارث‌بری باعث شد تا توسعه‌دهندگان کلاس‌های عظیم بنویسند که منطق فرایند‌های چند‌نخی، دامنه، و UI را با هم در یکجا داشته باشد و منجر به غیر قابل نگه‌داری شدنِ کدها شود. (maintainability).  و  خب چرا نه؟ این همان چیزی است که API از شما خواسته بوده.

اگر قانونی از کتاب Effective Java که می‌گفت: “ترکیب را بر وراثت ترجیح دهید” دنبال می‌شد، تفاوت فاحشی در AsyncTask ایجاد می‌کرد. (و جالب است بدانید Joshua Bloch، نویسنده‌ی این کتاب، خودش در گوگل کار می‌کرد و اویل کار اندروید هم با آنها همکاری می‌کرد).

 

مشکلِ 5 در AsyncTask: قابل اطمینان بودن

خیلی ساده، تنظیمات پیش‏‌فرض THREAD_POOL_EXECUTOR که پشتِ پرده‌ی AsyncTask است، بد تنظیم شده و غیر قابل اطمینان است. گوکل حداقل دو بار طی این سال‌ها تنظیمات آن را تغییر داده (در این و این commit)، اما همچنان باعث کرش کردنِ صفحه‌ی تنظیمات اندروید شده است.

اکثر نرم‌افزار‌های اندرویدی به این اندازه از همزمانی نیاز ندارند. به هر حال، شما هرگز نمی‌توانید پیش‌بینی کنید یک سال دیگر روی چه پروژه‌ای کار می‌کنید، بنابراین تکیه بر راه‌حل‌های غیر قابل اطمینان مشکل‌ دارد.

 

مشکلِ 6 در AsyncTask: درک غلط از هم‌زمانی

این نکته از مستندات بد نشئت می‌گیرد، اما فکر می‌کنم مستحق آن باشد که به شکل جداگانه بررسی‌اش کنیم. مستندات برای متد executeOnExecutor عنوان می‌کند:

اجازه به اجرای چند task به شکل موازی از یک استخر thread عموما کاری نیست که کسی بخواهد انجام دهد، به این دلیل که ترتیب انجام آنها مشخص نیست. […] این تغییرات در بهترین حالت باید پشتِ سر هم اتفاق بی‌افتند. برای اطمینان از اینکه مستقل از پلتفرم این اتفاق می‌افتد، می‌توانید از این تابع با SERIAL_EXECUTOR استفاده کنید.

خب، این غلط است. اجازه به اجرای چند وظیفه به شکل همزمان، دقیقا همان چیزی است که در اکثر اوقات که می‌خواهید کاری را از روی دوش UI بردارید، انجام دهید.

برای مثال، فرض کنیم که می‌خواهید یک درخواست به سرور ارسال کنید و به هردلیلی دچار timeout می‌شود. زمان پیش‌فرض برای timeout در OkHttp به اندازه 10 ثانیه است. اگر از SERIAL_EXECUTOR که فقط یک وظیفه را در هر نمونه انجام می‌دهد استفاده کنید، تمامِ عملیاتِ بکگراند در برنامه‌ی خود را برای 10 ثانیه متوقف کرده‌اید. و اگر بخواهید دو درخواست ارسال کنید و هردو تایم اوت شوند چه؟ خب، 20 ثانیه بدونِ هیچ عملیاتی در بک‌گراند. حالا، timeout شدنِ سرور تنها نمونه نیست، خیلی موارد مشابه دیگر هم هست: عملیات دیتابیس، پردازش تصویر، محاسبات و…

بله، همانطور که در مستندات گفته، ترتیب اجرای عملیات در برنامه در استخر thread مشخص نیست چون به شکل همزمان اجرا می‌شوند. اما، این یک مشکل نیست. در واقع، این دقیقا همان تعریفِ همزمانی است.

بنابراین به نظرِ من، این توضیحات نشان از درکِ نادرست نویسنده‌های AsyncTask از هم‌زمانی دارد. نتوانستم هیچ بخش دیگری از مستندات را پیدا کنم که تا این اندازه کژفهمی در آن وجود داشته باشد.

 

آینده‌ی AsyncTask

خوشبختانه متقاعدتان کردم که منسوخ کردنِ AsyncTask یک حرکت خوب از سمتِ گوگل بود. اما، برای پروژه‌هایی که از AsyncTask استفاده کرده‌اند، این یک خبر خوب نیست. اگر روی پروژه‌هایی که در آنها از AsyncTask استفاده شده کار می‌کنید، باید کد‌هایتان را تغییر دهید؟

اول از همه، فکر نمی‌کنم نیازی باشد که کد‍‌های مربوط به AsyncTask را از برنامه‌ی خودتان حذف کنید. منسوخ شدنِ این API بدین معنی نیست که دیگر کار نمی‌کند. در واقع؛ عجیب نخواهد بود اگر AsyncTask تا زمان حیات اندروید، باقی بماند. برنامه‌های زیادی، من جمله نرم‌افزارهای خود گوگل، از این API استفاده می‌کنند. و اگر هم مثلا در 5 سال آینده حذف شود؛ می‌توانید کدش را در پروژه‌ی خودتان کپی کنید و کلاسش را ایمپورت کنید و به کار ادامه دهید.

تأثیر اصلی این منسوخ شدن، بر روی توسعه دهندگان اندروید جدید خواهد بود. برایشان روشن می‌کند که نیازی نیست روی این API وقت بگذارند و در نرم‏‌افزار‌های خود از آن استفاده نخواهند کرد.

 

آینده‌ی فرایند‌های چند‌نخی در اندروید

منسوخ شدنِ AsyncTask مقدایری فضای خالی ایجاد می‌کند که باید با رویکرد‌های دیگری برای فرایند‌های چند‌نخی جایگزین شود. و آن چه خواهد بود؟ بگذارید نظرِ خودم را در موردش با شما به اشتراک بگذارم.

اگر تازه راه خود در اندروید را شروع کرده اید و از جاوا استفاده می‌کنید، توصیه می‌کنم کلاس پایه‌ی Thread و UI Handler را بفهمید. بسیاری از توسعه‌دهندگان اندرویدی مخالفت می‌کنند، اما من از این رویکرد استفاده کردم و نتیجه‌ی بسیار بسیار بهتری از آنچه AsyncTask  می‌توانست ارائه دهد را گرفتم.

برای اینکه بازخورد بیشتری در مورد این تکنیک گرفته شود، یک رأی گیری توئیتری راه انداخته شد. در زمان نگارش این متن، نتایج به این صورت بود:

علیرضا پیر

به نظر می‌آید فقط من نبودم و اکثر توسعه‌دهندگانی که از این روش استفاده کردند، آن را خوب می‌دانند.

اگر کمی تجربه دارید، می‌توانید ایجاد دستیِ کلاس‌های Thread را با روشی متمرکز همچون استفاده از ExecutorService جایگزین کنید. برای من، بزرگترین مشکل در استفاده از Thread‌ها این بود که فراموش می‌کردم آنها را Start کنم و بعد مدت‌ها مشغولِ برطرف کردنِ این اشتباه احمقانه می‌شدم. خیلی اذیت کننده است. ExecutorService این مشکل را حل کرد.

[حالا اگر این را می‌خوانید و می‌خواهید کامنتی در مورد performance بگذارید، لطفا مطمئن شوید که کامنتتان متریک‌های واقعی مرتبط با performance را دارد]

حالا، من شخصا ترجیح می‌دهم از کتابخانه‌ی ThreadPoster برای فرایند‌های چندنخی در جاوا استفاده کنم. یک تجرید خیلی سبک بر اساس ExecutorService و Handler. این کتابخانه چند‌نخی نوشتن را خیلی ضمنی می‌کند و کار UnitTest را نیز راحت تر می‌کند.

اگر از زبان کاتلین استفاده می‌کنید، باز هم توصیه‌های بالا معتبر هستند، فقط یک نکته‌ی دیگر وجود دارد که باید در نظر بگیرید.

به نظر می‌رسد که چهارچوب Coroutine قرار است متغیر رسمی مرتبط با همزمانی در کاتلین باشد. به عبارت دیگر، هرچند کاتلین در اندروید در زیر عبایِ خود از threadها استفاده می‌کند، Coroutineها قرار است که سطح پایین‌ترین تجرید در مستندات و آموزش‌های این زبان باشد.

برای من، شخصا در این نقطه Coroutineها به شدت پیچیده و نابالغ حس می‌شوند، اما من همیشه سعی می‌کنم ابزار‌های خود را با پیش‌بینی دو سال از حالا انتخاب کنم. با توجه به این معیار، من از Coroutine برای پروژه‌های جدید کاتلین استفاده می‌کنم. بنابراین، به همه‌ی توسعه‌دهندگانی که از کاتلین استفاده می‌کنند. توصیه می‌کنم که همین کار را انجام دهند.

 

جمع‌بندی

به نظر من، منسوخ شدنِ AsyncTask خیلی عقب افتاد و زمینه‌ی چند‌نخی در اکوسیستم اندروید را خیلی خالی نگه داشت. این API مشکلات بسیاری داشت و باعث ایجاد مشکلات فراوانی در طی سالها شد.

متأسفانه، متن بیانیه‌ی منسوخ شدنی که توسط گوگل ارائه شده، اطلاعات غلطی ارائه می‌کند و ممکن است باعث سردرگمی توسعه‌دهندگانی شود که الان از AsyncTask استفاده می‌کنند یا در آینده کلاس‌هایی را از AsyncTask میسازند. خوشبختانه، این پست برخی موارد را در مورد AsyncTask روشن کرد و بعلاوه برخی موارد مورد نیاز برای کار با هم زمانی به صورت کلی در اندروید را در اختیار شما قرار داد.

برای پروژه هایی که از AsyncTask استفاده کرده اند این منسوخ شدن مشکل ساز خواهد بود اما نیازی به هیچ تغییری در کدها وجود ندارد چون به این زودی ها حذف نخواهد شد.

 

این متن ترجمه ای آزاد از این مقاله می باشد.