راهنمای شروع به کار، نحوه ایجاد یک WorkRequest و قرار دادن آن در صف انتظار را پوشش داد.
در این راهنما یاد خواهید گرفت که چگونه اشیاء WorkRequest را برای مدیریت موارد استفاده رایج تعریف و سفارشی کنید، مانند نحوه انجام موارد زیر:
- کارهای یکباره و تکرارشونده را برنامهریزی کنید
- محدودیتهای کاری مانند نیاز به وایفای یا شارژ را تعیین کنید
- تضمین حداقل تأخیر در اجرای کار
- تنظیم استراتژیهای تلاش مجدد و عقبنشینی
- داده های ورودی را به کار منتقل کنید
- کارهای مرتبط را با استفاده از برچسبها گروهبندی کنید
نمای کلی
کار در WorkManager با استفاده از WorkRequest تعریف میشود. برای زمانبندی هر کاری با WorkManager، ابتدا باید یک شیء WorkRequest ایجاد کنید و سپس آن را در صف قرار دهید.
کاتلین
val myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest)
جاوا
WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);
شیء WorkRequest شامل تمام اطلاعات مورد نیاز WorkManager برای زمانبندی و اجرای کار شماست. این شیء شامل محدودیتهایی است که باید برای اجرای کار شما رعایت شوند، اطلاعات زمانبندی مانند تأخیرها یا فواصل تکرار، پیکربندی تلاش مجدد و در صورت نیاز به دادههای ورودی برای کار شما.
WorkRequest خود یک کلاس پایه انتزاعی است. دو پیادهسازی مشتقشده از این کلاس وجود دارد که میتوانید برای ایجاد درخواست از آنها استفاده کنید، OneTimeWorkRequest و PeriodicWorkRequest . همانطور که از نامشان پیداست، OneTimeWorkRequest برای زمانبندی کارهای غیرتکراری مفید است، در حالی که PeriodicWorkRequest برای زمانبندی کارهایی که در یک بازه زمانی تکرار میشوند، مناسبتر است.
برای کارهای یکباره برنامهریزی کنید
برای کارهای اساسی که نیازی به پیکربندی اضافی ندارند، from متد استاتیک زیر استفاده کنید:
کاتلین
val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)
جاوا
WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);
برای کارهای پیچیدهتر، میتوانید از یک سازنده استفاده کنید:
کاتلین
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
// Additional configuration
.build()
جاوا
WorkRequest uploadWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
// Additional configuration
.build();
برای کارهای فوری برنامهریزی کنید
WorkManager 2.7.0 مفهوم کار تسریعشده را معرفی کرد. این به WorkManager اجازه میدهد تا کارهای مهم را انجام دهد و در عین حال کنترل بهتری بر دسترسی به منابع به سیستم بدهد.
کار سریع به دلیل ویژگیهای زیر قابل توجه است:
- اهمیت : کار تسریعشده برای وظایفی مناسب است که برای کاربر مهم هستند یا توسط کاربر آغاز شدهاند.
- سرعت : کار سریع برای وظایف کوتاهی که فوراً شروع میشوند و ظرف چند دقیقه به پایان میرسند، مناسبتر است.
- سهمیهها : سهمیهای در سطح سیستم که زمان اجرای پیشزمینه را محدود میکند، تعیین میکند که آیا یک کار تسریعشده میتواند شروع شود یا خیر.
- مدیریت توان : محدودیتهای مدیریت توان ، مانند صرفهجویی در مصرف باتری و چرت زدن، کمتر احتمال دارد که بر کار سریع تأثیر بگذارند.
- تأخیر : سیستم بلافاصله کارهای تسریعشده را اجرا میکند، مشروط بر اینکه حجم کاری فعلی سیستم امکان انجام این کار را فراهم کند. این بدان معناست که آنها به تأخیر حساس هستند و نمیتوان آنها را برای اجرای بعدی برنامهریزی کرد.
یک مورد استفاده بالقوه برای کار سریع میتواند در یک برنامه چت باشد، زمانی که کاربر میخواهد یک پیام یا یک تصویر پیوست شده ارسال کند. به طور مشابه، برنامهای که جریان پرداخت یا اشتراک را مدیریت میکند نیز ممکن است بخواهد از کار سریع استفاده کند. دلیل این امر این است که این وظایف برای کاربر مهم هستند، به سرعت در پسزمینه اجرا میشوند، باید فوراً شروع شوند و حتی اگر کاربر برنامه را ببندد، باید به اجرای خود ادامه دهند.
سهمیهها
سیستم باید قبل از اینکه یک کار تسریعشده بتواند اجرا شود، زمان اجرا را به آن اختصاص دهد. زمان اجرا نامحدود نیست. بلکه، هر برنامه سهمیهای از زمان اجرا دریافت میکند. وقتی برنامه شما از زمان اجرای خود استفاده میکند و به سهمیه اختصاص داده شده خود میرسد، دیگر نمیتوانید کار تسریعشده را اجرا کنید تا سهمیه تازه شود. این به اندروید اجازه میدهد تا منابع را به طور مؤثرتری بین برنامهها متعادل کند.
میزان زمان اجرای موجود برای یک برنامه بر اساس سطل آماده به کار و اهمیت فرآیند است.
شما میتوانید تعیین کنید که چه اتفاقی میافتد وقتی سهمیه اجرا اجازه اجرای فوری یک کار تسریعشده را نمیدهد. برای جزئیات بیشتر به قطعه کدهای زیر مراجعه کنید.
انجام کار با سرعت بالا
با شروع از WorkManager 2.7، برنامه شما میتواند setExpedited() را فراخوانی کند تا اعلام کند که یک WorkRequest باید با استفاده از یک کار تسریعشده در اسرع وقت اجرا شود. قطعه کد زیر مثالی از نحوه استفاده از setExpedited() را ارائه میدهد:
کاتلین
val request = OneTimeWorkRequestBuilder<SyncWorker>()
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build()
WorkManager.getInstance(context)
.enqueue(request)
جاوا
OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
.setInputData(inputData)
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build();
در این مثال، ما یک نمونه از OneTimeWorkRequest را مقداردهی اولیه میکنیم و setExpedited() را روی آن فراخوانی میکنیم. سپس این درخواست به یک کار تسریعشده تبدیل میشود. اگر سهمیه اجازه دهد، بلافاصله در پسزمینه شروع به اجرا میکند. اگر سهمیه استفاده شده باشد، پارامتر OutOfQuotaPolicy نشان میدهد که درخواست باید به صورت عادی و بدون تسریع اجرا شود.
سازگاری معکوس و سرویسهای پیشزمینه
برای حفظ سازگاری با نسخههای قبلی برای کارهای تسریعشده، WorkManager ممکن است یک سرویس پیشزمینه را روی نسخههای پلتفرم قدیمیتر از اندروید ۱۲ اجرا کند. سرویسهای پیشزمینه میتوانند یک اعلان به کاربر نمایش دهند.
متدهای getForegroundInfoAsync() و getForegroundInfo() در Worker شما، WorkManager را قادر میسازند تا قبل از اندروید ۱۲، هنگام فراخوانی setExpedited() یک اعلان نمایش دهد.
اگر میخواهید درخواست کنید که وظیفه به عنوان یک کار تسریعشده اجرا شود، هر ListenableWorker باید متد getForegroundInfo پیادهسازی کند.
هنگام هدف قرار دادن اندروید ۱۲ یا بالاتر، سرویسهای پیشزمینه از طریق متد setForeground مربوطه در دسترس شما باقی میمانند.
کارگر
کارگران نمیدانند که آیا کاری که انجام میدهند تسریع شده است یا خیر. اما کارگران میتوانند در برخی از نسخههای اندروید، هنگامی که یک WorkRequest تسریع شده است، اعلانی را نمایش دهند.
برای فعال کردن این قابلیت، WorkManager متد getForegroundInfoAsync() را ارائه میدهد که باید آن را پیادهسازی کنید تا WorkManager بتواند در صورت لزوم، اعلانی برای شروع ForegroundService برای شما نمایش دهد.
کوروتین ورکر
اگر از CoroutineWorker استفاده میکنید، باید getForegroundInfo() پیادهسازی کنید. سپس آن را به setForeground() درون doWork() ارسال کنید. انجام این کار، اعلان را در نسخههای قبل از ۱۲ اندروید ایجاد میکند.
به مثال زیر توجه کنید:
class ExpeditedWorker(appContext: Context, workerParams: WorkerParameters):
CoroutineWorker(appContext, workerParams) {
override suspend fun getForegroundInfo(): ForegroundInfo {
return ForegroundInfo(
NOTIFICATION_ID, createNotification()
)
}
override suspend fun doWork(): Result {
TODO()
}
private fun createNotification() : Notification {
TODO()
}
}
سیاستهای سهمیهبندی
شما میتوانید کنترل کنید که وقتی برنامه شما به سهمیه اجرای خود میرسد، چه اتفاقی برای کار تسریعشده میافتد. برای ادامه، میتوانید setExpedited() را ارسال کنید:
-
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUESTکه باعث میشود کار به عنوان یک درخواست کار معمولی اجرا شود. قطعه کد قبلی این را نشان میدهد. -
OutOfQuotaPolicy.DROP_WORK_REQUESTکه باعث میشود در صورت عدم وجود سهمیه کافی، درخواست لغو شود.
کار تسریعشدهی معوق
سیستم سعی میکند یک کار تسریعشدهی معین را در اسرع وقت پس از فراخوانی آن اجرا کند. با این حال، مانند سایر انواع کارها، سیستم ممکن است شروع کار تسریعشدهی جدید را به تعویق بیندازد، مانند موارد زیر:
- بار : بار سیستم خیلی زیاد است، که میتواند زمانی رخ دهد که کارهای زیادی در حال اجرا هستند یا سیستم حافظه کافی ندارد.
- سهمیه : محدودیت سهمیه کار تسریعشده از حد مجاز فراتر رفته است. کار تسریعشده از یک سیستم سهمیهبندی استفاده میکند که مبتنی بر سطلهای آمادهبهکار برنامه است و حداکثر زمان اجرا را در یک پنجره زمانی متغیر محدود میکند. سهمیههای مورد استفاده برای کار تسریعشده محدودکنندهتر از سهمیههای مورد استفاده برای سایر انواع کارهای پسزمینه هستند.
برنامهریزی کار دورهای
ممکن است برنامه شما گاهی اوقات نیاز داشته باشد که کارهای خاصی به صورت دورهای اجرا شوند. برای مثال، ممکن است بخواهید به صورت دورهای از دادههای خود نسخه پشتیبان تهیه کنید، محتوای جدید را در برنامه خود دانلود کنید یا گزارشها را به یک سرور آپلود کنید.
در اینجا نحوه استفاده از PeriodicWorkRequest برای ایجاد یک شیء WorkRequest که به صورت دورهای اجرا میشود، آورده شده است:
کاتلین
val saveRequest =
PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
// Additional configuration
.build()
جاوا
PeriodicWorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
// Constraints
.build();
در این مثال، کار با فاصله یک ساعت برنامهریزی شده است.
دوره وقفه به عنوان حداقل زمان بین تکرارها تعریف میشود. زمان دقیقی که worker قرار است اجرا شود، به محدودیتهایی که در شیء WorkRequest خود استفاده میکنید و بهینهسازیهای انجام شده توسط سیستم بستگی دارد.
فواصل زمانی انعطافپذیر برای دویدن
اگر ماهیت کار شما به گونهای است که به زمانبندی اجرا حساس است، میتوانید PeriodicWorkRequest خود را طوری پیکربندی کنید که در یک دوره انعطافپذیر در هر دوره وقفه اجرا شود، همانطور که در شکل 1 نشان داده شده است.

شکل ۱. نمودار، فواصل تکرارشونده را با دوره انعطافپذیری که کار میتواند در آن اجرا شود، نشان میدهد.
برای تعریف کار دورهای با یک دوره flex، هنگام ایجاد PeriodicWorkRequest ، یک flexInterval به همراه repeatInterval ارسال میکنید. دوره flex از repeatInterval - flexInterval شروع میشود و تا انتهای بازه ادامه مییابد.
در ادامه مثالی از کار دورهای که میتواند در ۱۵ دقیقه آخر هر دوره یک ساعته انجام شود، آورده شده است.
کاتلین
val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
1, TimeUnit.HOURS, // repeatInterval (the period cycle)
15, TimeUnit.MINUTES) // flexInterval
.build()
جاوا
WorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class,
1, TimeUnit.HOURS,
15, TimeUnit.MINUTES)
.build();
بازه تکرار باید بزرگتر یا مساوی PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS باشد و بازه انعطافپذیر باید بزرگتر یا مساوی PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS` باشد.
تأثیر محدودیتها بر کار دورهای
شما میتوانید محدودیتهایی را برای کارهای دورهای اعمال کنید. برای مثال، میتوانید محدودیتی به درخواست کار خود اضافه کنید به طوری که کار فقط زمانی اجرا شود که دستگاه کاربر در حال شارژ باشد. در این حالت، حتی اگر فاصله تکرار تعریف شده بگذرد، PeriodicWorkRequest تا زمانی که این شرط برآورده نشود، اجرا نخواهد شد. این میتواند باعث شود که اجرای خاصی از کار شما به تأخیر بیفتد یا حتی اگر شرایط در فاصله اجرا برآورده نشوند، نادیده گرفته شود.
محدودیتهای کاری
Constraints تضمین میکنند که کار تا زمان برآورده شدن شرایط بهینه به تعویق بیفتد. محدودیتهای زیر در WorkManager موجود است:
| نوع شبکه | نوع شبکه مورد نیاز برای اجرای کار شما را محدود میکند. برای مثال، وایفای ( UNMETERED ). |
| باتری ضعیف نیست | وقتی روی true تنظیم شده باشد، اگر دستگاه در حالت باتری کم باشد، کار شما اجرا نخواهد شد. |
| نیاز به شارژ دارد | وقتی روی true تنظیم شود، کار شما فقط زمانی اجرا میشود که دستگاه در حال شارژ باشد. |
| دستگاه بیکار | وقتی روی true تنظیم شود، این مستلزم آن است که دستگاه کاربر قبل از اجرای کار، در حالت آماده به کار (idle) باشد. این میتواند برای اجرای عملیات دستهای مفید باشد که در غیر این صورت ممکن است تأثیر منفی بر عملکرد سایر برنامههای فعال روی دستگاه کاربر داشته باشد. |
| فضای ذخیرهسازی کم نیست | وقتی روی true تنظیم شده باشد، اگر فضای ذخیرهسازی کاربر روی دستگاه خیلی کم باشد، کار شما اجرا نخواهد شد. |
برای ایجاد مجموعهای از محدودیتها و مرتبط کردن آن با برخی کارها، با استفاده از Constraints.Builder() یک نمونه Constraints ایجاد کنید و آن را به WorkRequest.Builder() خود اختصاص دهید.
برای مثال، کد زیر یک درخواست کاری ایجاد میکند که فقط زمانی اجرا میشود که دستگاه کاربر هم در حال شارژ باشد و هم به وایفای متصل باشد:
کاتلین
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build()
val myWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
.setConstraints(constraints)
.build()
جاوا
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build();
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setConstraints(constraints)
.build();
وقتی چندین قید مشخص شده باشد، کار شما فقط زمانی اجرا میشود که همه قیدها رعایت شده باشند.
در صورتی که در حین اجرای کار، قیدی برآورده نشود، WorkManager ورکر شما را متوقف میکند. سپس وقتی همه قیدها برآورده شدند، کار دوباره امتحان میشود.
کار با تأخیر
در صورتی که کار شما هیچ محدودیتی نداشته باشد یا تمام محدودیتها هنگام قرار گرفتن در صف برآورده شوند، سیستم ممکن است تصمیم بگیرد که کار را فوراً اجرا کند. اگر نمیخواهید کار فوراً اجرا شود، میتوانید کار خود را طوری تنظیم کنید که پس از حداقل تأخیر اولیه شروع شود.
در اینجا مثالی از نحوه تنظیم اجرای کار شما حداقل 10 دقیقه پس از قرار گرفتن در صف آورده شده است.
کاتلین
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
جاوا
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setInitialDelay(10, TimeUnit.MINUTES)
.build();
اگرچه این مثال نحوه تنظیم تأخیر اولیه برای یک OneTimeWorkRequest را نشان میدهد، اما میتوانید برای یک PeriodicWorkRequest نیز یک تأخیر اولیه تنظیم کنید. در این صورت، فقط اولین اجرای کار دورهای شما به تأخیر میافتد.
سیاست تلاش مجدد و عدم تلاش
اگر نیاز دارید که WorkManager کار شما را دوباره امتحان کند، میتوانید Result.retry() از worker خود برگردانید. سپس کار شما طبق یک سیاست تأخیر و backoff دوباره برنامهریزی میشود.
تأخیر برگشت، حداقل زمان لازم برای انتظار قبل از تلاش مجدد برای انجام کار پس از اولین تلاش را مشخص میکند. این مقدار نمیتواند کمتر از 10 ثانیه (یا MIN_BACKOFF_MILLIS ) باشد.
سیاست Backoff تعریف میکند که چگونه تأخیر Backoff باید برای تلاشهای مجدد بعدی با گذشت زمان افزایش یابد. WorkManager از دو سیاست Backoff پشتیبانی میکند:
LINEARوEXPONENTIAL.
هر درخواست کار دارای یک سیاست بازگشت به کار (backoff policy) و یک تاخیر بازگشت به کار (backoff delay) است. سیاست پیشفرض، EXPONENTIAL با تاخیر 30 ثانیهای است، اما میتوانید این را در پیکربندی درخواست کار خود لغو کنید.
در اینجا مثالی از سفارشیسازی تأخیر و سیاست backoff آورده شده است.
کاتلین
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
WorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
جاوا
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
WorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build();
در این مثال، حداقل تأخیر برگشت روی حداقل مقدار مجاز، یعنی ۱۰ ثانیه تنظیم شده است. از آنجایی که این سیاست LINEAR است، فاصله زمانی تلاش مجدد با هر تلاش جدید تقریباً ۱۰ ثانیه افزایش مییابد. به عنوان مثال، اولین اجرا که با Result.retry() به پایان میرسد، پس از ۱۰ ثانیه دوباره تلاش میشود و پس از آن ۲۰، ۳۰، ۴۰ و غیره، اگر کار همچنان Result.retry() را پس از تلاشهای بعدی بازگرداند، ادامه مییابد. اگر سیاست برگشت روی EXPONENTIAL تنظیم شده باشد، توالی مدت زمان تلاش مجدد به ۲۰، ۴۰ و ۸۰ نزدیکتر خواهد بود.
کار برچسب
هر درخواست کار یک شناسه منحصر به فرد دارد که میتوان از آن برای شناسایی آن کار در آینده به منظور لغو کار یا مشاهده پیشرفت آن استفاده کرد.
اگر گروهی از کارهای مرتبط با منطق دارید، ممکن است برچسبگذاری آن موارد کاری نیز مفید باشد. برچسبگذاری به شما امکان میدهد تا با گروهی از درخواستهای کاری به طور همزمان کار کنید.
برای مثال، WorkManager.cancelAllWorkByTag(String) تمام درخواستهای کاری با یک تگ خاص را لغو میکند، و WorkManager.getWorkInfosByTag(String) لیستی از اشیاء WorkInfo را برمیگرداند که میتوانند برای تعیین وضعیت فعلی کار استفاده شوند.
کد زیر نشان میدهد که چگونه میتوانید یک تگ "cleanup" به کارتان اضافه کنید:
کاتلین
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.addTag("cleanup")
.build()
جاوا
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.addTag("cleanup")
.build();
در نهایت، میتوان چندین تگ را به یک درخواست کار واحد اضافه کرد. این تگها در داخل به صورت مجموعهای از رشتهها ذخیره میشوند. برای دریافت مجموعه تگهای مرتبط با WorkRequest میتوانید از WorkInfo.getTags() استفاده کنید.
از کلاس Worker خود، میتوانید مجموعه تگهای آن را با استفاده از ListenableWorker.getTags() بازیابی کنید.
اختصاص داده ورودی
ممکن است کار شما برای انجام کارش به دادههای ورودی نیاز داشته باشد. برای مثال، کاری که آپلود یک تصویر را مدیریت میکند، ممکن است نیاز داشته باشد که URI تصویر به عنوان ورودی آپلود شود.
مقادیر ورودی به صورت جفتهای کلید-مقدار در یک شیء Data ذخیره میشوند و میتوانند در درخواست کار تنظیم شوند. WorkManager هنگام اجرای کار، Data ورودی را به کار شما تحویل میدهد. کلاس Worker میتواند با فراخوانی Worker.getInputData() به آرگومانهای ورودی دسترسی پیدا کند. کد زیر نشان میدهد که چگونه میتوانید یک نمونه Worker ایجاد کنید که به دادههای ورودی نیاز دارد و چگونه آن را در درخواست کار خود ارسال کنید.
کاتلین
// Define the Worker requiring input
class UploadWork(appContext: Context, workerParams: WorkerParameters)
: Worker(appContext, workerParams) {
override fun doWork(): Result {
val imageUriInput =
inputData.getString("IMAGE_URI") ?: return Result.failure()
uploadFile(imageUriInput)
return Result.success()
}
...
}
// Create a WorkRequest for your Worker and sending it input
val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
.setInputData(workDataOf(
"IMAGE_URI" to "http://..."
))
.build()
جاوا
// Define the Worker requiring input
public class UploadWork extends Worker {
public UploadWork(Context appContext, WorkerParameters workerParams) {
super(appContext, workerParams);
}
@NonNull
@Override
public Result doWork() {
String imageUriInput = getInputData().getString("IMAGE_URI");
if(imageUriInput == null) {
return Result.failure();
}
uploadFile(imageUriInput);
return Result.success();
}
...
}
// Create a WorkRequest for your Worker and sending it input
WorkRequest myUploadWork =
new OneTimeWorkRequest.Builder(UploadWork.class)
.setInputData(
new Data.Builder()
.putString("IMAGE_URI", "http://...")
.build()
)
.build();
به طور مشابه، میتوانید از کلاس Data برای خروجی دادن یک مقدار بازگشتی استفاده کنید.
مراحل بعدی
در صفحه «ایالتها و مشاهدات» ، درباره وضعیت کار و نحوه نظارت بر پیشرفت کارتان بیشتر خواهید آموخت.