جایگزین های AsyncTask منقضی شده
من در اندروید استدیو میخواهم کدی بنویسم که یک فرایند را که قبلا در AsyncTask انجام میدادم و دیگر منقضی شده است را با روشی جدید و جایگزین انجام دهد. یعنی هم اینکار را در پس زمینه انجام دهد. هم یک دیالوگ باکس داشته باشد که میزان پیشرفت کار را نشان دهد و هم پیغام ها را به کاربر نشان دهد و هم قابلیت کنسل کردن را به کاربر بدهد.
قبلا از کلاس ProgressDialog و AsyncTask استفاده میکردیم. کد زیر فرایندی را در پس زمینه انجام میدهد، با استفاده از AsyncTask اجرا میکند و همزمان با آن یک ProgressDialog را به نمایش میگذارد:
private class MyTask extends AsyncTask<Void, Integer, String> { private ProgressDialog progressDialog; private boolean running = true; @Override protected void onPreExecute() { super.onPreExecute(); progressDialog = new ProgressDialog(MainActivity.this); progressDialog.setTitle("Processing"); progressDialog.setMessage("Please wait..."); progressDialog.setCancelable(false); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setButton
اما خوب میدانیم که در API level 30 (Android 11) کلاس AsyncTask به عنوان یک کلاس قدیمی و منسوخ شده (deprecated) اعلام شده است. به جای AsyncTask، میتوان از کلاس هایی مانند Executor، ExecutorService و ThreadPoolExecutor استفاده کرد.
به عنوان مثال، برای اجرای یک فرایند در پس زمینه با استفاده از ExecutorService، کد زیر را میتوان استفاده کرد:
private ExecutorService executorService; private void startBackgroundTask() { progressDialog = new ProgressDialog(MainActivity.this); progressDialog.setTitle("Processing"); progressDialog.setMessage("Please wait..."); progressDialog.setCancelable(false); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { progressDialog.dismiss(); executorService.shutdownNow(); } }); executorService = Executors.newSingleThreadExecutor(); executorService.execute(new Runnable() { @Override public void run() { // انجام فرایند در پس زمینه // در هر مرحله میتوانید با فراخوانی progressDialog.setProgress() میزان پیشرفت را به روز کنید // در صورتی که فرایند قابل لغو است، باید در هر مرحله با فراخوانی executorService.isShutdown() بررسی کنید که آیا فرایند لغو شده است یا خیر. } }); }
در این مثال، با استفاده از Executors.newSingleThreadExecutor() یک ExecutorService ایجاد میکنیم که تنها یک نخ (thread) در اختیار دارد و با فراخوانی executorService.execute()، فرایند ما به صورت همزمان با دیالوگ پیشرفت (ProgressDialog) در پسزمینه اجرا میشود.
در متد onClick دکمه لغو، با فراخوانی executorService.shutdownNow() فرایند لغو شده و با فراخوانی executorService.isShutdown() در هر مرحله، میتوانیم بررسی کنیم که آیا فرایند قابل لغو است یا خیر.
حالا اگر اینکار را بخواهیم با rxjava انجام دهیم، میتوانیم از روش subscribeOn و observeOn برای اجرای کدهای پسزمینه و UI به ترتیب استفاده کنیم. همچنین میتوانیم از متد map برای تغییر دادههای ورودی و متد doOnSubscribe، doOnNext، doOnError و doOnComplete برای دسترسی به دیتاهای میانی و ارسال پیغام به کاربر استفاده کنیم. برای مثال، کد زیر نشاندهنده این است که چگونه با استفاده از RxJava کارهایی که قبلا با AsyncTask انجام میشد، انجام داده شود:
Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> emitter) throws Exception { // اجرای فرایند پسزمینه int progress = 0; while (progress < 100) { Thread.sleep(1000); progress += 10; emitter.onNext(progress); } emitter.onComplete(); } }) .subscribeOn(Schedulers.io()) // اجرای فرایند پسزمینه .observeOn(AndroidSchedulers.mainThread()) // نمایش پیام در رابط کاربری .map(new Function<Integer, String>() { @Override public String apply(Integer progress) throws Exception { return "در حال دانلود... " + progress + "%"; } }) .doOnSubscribe(new Consumer<Disposable>() { @Override public void accept(Disposable disposable) throws Exception { // نمایش دیالوگ باکس } }) .doOnNext(new Consumer<String>() { @Override public void accept(String message) throws Exception { // به روز رسانی میزان پیشرفت } }) .doOnError(new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { // نمایش پیغام خطا به کاربر } }) .doOnComplete(new Action() { @Override public void run() throws Exception { // نمایش پیغام پایان به کاربر } }) .subscribe();
در این کد، Observable.create یک ایمیتر برای ارسال دادهها در فرایند پسزمینه ایجاد میکند. با subscribeOn(Schedulers.io()) فرایند اجرای Observable در پسزمینه تضمین میشود. با اضافه کردن observeOn(AndroidSchedulers.mainThread())، هرگونه کار برای بهروزرسانی رابط کاربری در موضوع اصلی RxJava انجام میشود.
RxJava یک راه حل بسیار قدرتمند برای مدیریت عملیات های پس زمینه و نمایش پیشرفت کار به کاربر است. می توان با استفاده از Observable و Observer یک سری از داده ها را در قالب یک جریان به صورت پیوسته ارسال کرد و از عملیات متناظر با onNext و onComplete برای مدیریت این جریان استفاده کرد. همچنین می توان از عملیات متناظر با onError برای مدیریت خطاها و نمایش پیغام های خطا به کاربر استفاده کرد.
برای مثال، می توان از کتابخانه RxAndroid استفاده کرد و مثلا این کد برای دانلود فایل:
public void downloadFile(String fileUrl) { ProgressDialog progressDialog = new ProgressDialog(this); progressDialog.setMessage("Downloading..."); progressDialog.setCanceledOnTouchOutside(false); progressDialog.show(); Observable.fromCallable(() -> { // کد دانلود فایل return true; }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Boolean>() { @Override public void onSubscribe(Disposable d) { // لازم نیست انجام شود } @Override public void onNext(Boolean success) { if (success) { // نمایش پیغام موفقیت آمیز بودن دانلود } else { // نمایش پیغام خطا در دانلود } } @Override public void onError(Throwable e) { // نمایش پیغام خطا به کاربر } @Override public void onComplete() { progressDialog.dismiss(); } }); }
در این مثال، از یک Observable استفاده شده است که یک فرایند در پس زمینه را اجرا می کند و نتیجه آن را در یک جریان برمی گرداند. با استفاده از subscribeOn و observeOn، فرایند در پشت زمینه اجرا شده و پیشرفت کار در جریان اصلی بروز می کند. همچنین در اینجا از یک ProgressDialog برای نمایش پیشرفت کار استفاده شده است که در onComplete مخفی شده است.
بسته به نیازها و محیط پروژه شما، هر کدام از این روشها ممکن است مناسب باشند. اما به طور کلی، استفاده از روشهایی مانند RxJava و Kotlin Coroutines برای انجام عملیات پس زمینه به دلیل پشتیبانی از concurrency، مدیریت خطاها و امکان لغو عملیات بهتر است. همچنین این روشها بهبود عملکرد برنامه شما را نیز فراهم میکنند.