این کد لبه بخشی از دوره آموزشی Android Kotlin Fundamentals است. اگر به ترتیب روی کدها کار کنید، بیشترین ارزش را از این دوره خواهید گرفت. همه کدهای دوره در صفحه فرود کد لبه های کدهای Android Kotlin Fundamentals فهرست شده اند.
مقدمه
یکی از اولویت های اصلی برای ایجاد یک تجربه کاربری بی عیب و نقص برای برنامه شما این است که مطمئن شوید رابط کاربری همیشه پاسخگو بوده و به خوبی اجرا می شود. یکی از راههای بهبود عملکرد UI، انتقال وظایف طولانیمدت، مانند عملیات پایگاه داده، به پسزمینه است.
در این کد لبه، شما بخش رو به روی کاربر از برنامه TrackMySleepQuality را با استفاده از کوروتین های Kotlin برای انجام عملیات پایگاه داده به دور از رشته اصلی پیاده سازی می کنید.
آنچه از قبل باید بدانید
باید با:
- ساخت یک رابط کاربری اولیه (UI) با استفاده از یک اکتیویتی، قطعات، نماها و کنترل کننده های کلیک.
- پیمایش بین قطعات و استفاده از
safeArgs
برای انتقال داده های ساده بین قطعات. - مشاهده مدلها، مشاهده کارخانههای مدل، تبدیلها و
LiveData
. - چگونه یک پایگاه داده
Room
ایجاد کنیم، یک DAO ایجاد کنیم و موجودیت ها را تعریف کنیم. - اگر با مفاهیم threading و multiprocessing آشنا هستید مفید است.
چیزی که یاد خواهید گرفت
- نحوه کار رشته ها در اندروید
- نحوه استفاده از کوروتین های Kotlin برای دور کردن عملیات پایگاه داده از موضوع اصلی.
- نحوه نمایش داده های فرمت شده در
TextView
.
کاری که خواهی کرد
- برنامه TrackMySleepQuality را برای جمع آوری، ذخیره و نمایش داده ها در داخل و از پایگاه داده گسترش دهید.
- برای اجرای عملیات طولانی مدت پایگاه داده در پس زمینه از کوروتین ها استفاده کنید.
- از
LiveData
برای راه اندازی ناوبری و نمایش نوار اسنک استفاده کنید. - از
LiveData
برای فعال و غیرفعال کردن دکمه ها استفاده کنید.
در این کد لبه، شما مدل نمایش، برنامههای مشترک و بخشهای نمایش داده برنامه TrackMySleepQuality را میسازید.
همانطور که در شکل زیر نشان داده شده است، برنامه دارای دو صفحه است که با قطعات نشان داده شده است.
اولین صفحه نمایش داده شده در سمت چپ دارای دکمه هایی برای شروع و توقف ردیابی است. صفحه نمایش تمام داده های خواب کاربر را نشان می دهد. دکمه Clear تمام داده هایی را که برنامه برای کاربر جمع آوری کرده است برای همیشه حذف می کند.
صفحه دوم که در سمت راست نشان داده شده است، برای انتخاب رتبه بندی کیفیت خواب است. در برنامه، امتیاز به صورت عددی نشان داده می شود. برای اهداف توسعه، برنامه هم نمادهای چهره و هم معادل های عددی آنها را نشان می دهد.
جریان کاربر به شرح زیر است:
- کاربر برنامه را باز می کند و با صفحه ردیابی خواب نمایش داده می شود.
- کاربر روی دکمه Start ضربه می زند. این زمان شروع را ثبت کرده و نمایش می دهد. دکمه Start غیرفعال است و دکمه Stop فعال است.
- کاربر روی دکمه Stop ضربه می زند. این کار زمان پایان را ثبت می کند و صفحه با کیفیت خواب را باز می کند.
- کاربر نماد کیفیت خواب را انتخاب می کند. صفحه بسته می شود و صفحه ردیابی زمان پایان خواب و کیفیت خواب را نشان می دهد. دکمه Stop غیرفعال و دکمه Start فعال است. برنامه برای یک شب دیگر آماده است.
- هر زمان که داده ای در پایگاه داده وجود داشته باشد، دکمه Clear فعال می شود. هنگامی که کاربر روی دکمه Clear ضربه میزند، تمام دادههای او بدون مراجعه پاک میشوند - "آیا مطمئن هستید؟" وجود ندارد. پیام
این برنامه از معماری ساده شده استفاده می کند، همانطور که در زیر در زمینه معماری کامل نشان داده شده است. این برنامه فقط از اجزای زیر استفاده می کند:
- کنترلر رابط کاربری
- مشاهده مدل و
LiveData
- پایگاه داده اتاق
در این کار، از TextView
برای نمایش داده های ردیابی خواب فرمت شده استفاده می کنید. (این رابط نهایی نیست. شما راه بهتری را در یک Codelab دیگر یاد خواهید گرفت.)
میتوانید با برنامه TrackMySleepQuality که در آزمایشگاه کد قبلی ساختهاید ادامه دهید یا برنامه شروع را برای این کد لبه دانلود کنید .
مرحله 1: برنامه شروع را دانلود و اجرا کنید
- برنامه TrackMySleepQuality-Coroutines-Starter را از GitHub دانلود کنید.
- برنامه را بسازید و اجرا کنید. برنامه رابط کاربری بخش
SleepTrackerFragment
را نشان می دهد، اما داده ای ندارد. دکمه ها به ضربه ها پاسخ نمی دهند.
مرحله 2: کد را بررسی کنید
کد شروع برای این Codelab همانند کد راه حل برای 6.1 Create a Room database codelab است.
- Res/layout/activity_main.xml را باز کنید. این طرح شامل قطعه
nav_host_fragment
است. همچنین به تگ<merge>
توجه کنید.
از تگmerge
می توان برای حذف طرح بندی های اضافی هنگام گنجاندن طرح بندی استفاده کرد و استفاده از آن ایده خوبی است. نمونه ای از یک طرح اضافی می تواند ConstraintLayout > LinearLayout > TextView باشد، جایی که سیستم ممکن است بتواند LinearLayout را حذف کند. این نوع بهینه سازی می تواند سلسله مراتب نمایش را ساده کرده و عملکرد برنامه را بهبود بخشد. - در پوشه پیمایش ، navigation.xml را باز کنید. شما می توانید دو قطعه و اقدامات ناوبری را که آنها را به هم متصل می کنند، مشاهده کنید.
- در پوشه layout ، روی قطعه ردیاب خواب دوبار کلیک کنید تا طرح XML آن را ببینید. به موارد زیر توجه کنید:
- داده های طرح بندی در یک عنصر
<layout>
پیچیده شده است تا اتصال داده ها را فعال کند. -
ConstraintLayout
و سایر نماها در داخل عنصر<layout>
مرتب شده اند. - این فایل دارای یک برچسب
<data>
است.
برنامه شروع همچنین ابعاد، رنگ ها و استایل را برای رابط کاربری ارائه می دهد. این برنامه شامل یک پایگاه داده Room
، یک DAO و یک موجودیت SleepNight
است. اگر نسخه کد قبلی را کامل نکردید، مطمئن شوید که این جنبه های کد را به تنهایی بررسی کرده اید.
اکنون که یک پایگاه داده و یک رابط کاربری دارید، باید داده ها را جمع آوری کنید، داده ها را به پایگاه داده اضافه کنید و داده ها را نمایش دهید. تمام این کارها در مدل view انجام می شود. مدل نمای ردیاب خواب شما با کلیک روی دکمه ها مدیریت می کند، از طریق DAO با پایگاه داده تعامل می کند و داده ها را از طریق LiveData
به رابط کاربری ارائه می دهد. تمام عملیات پایگاه داده باید از رشته UI اصلی دور شوند، و شما این کار را با استفاده از کوروتین ها انجام خواهید داد.
مرحله 1: SleepTrackerViewModel را اضافه کنید
- در بسته Sleeptracker ، SleepTrackerViewModel.kt را باز کنید.
- کلاس
SleepTrackerViewModel
را که در برنامه استارتر برای شما ارائه شده و در زیر نیز نشان داده شده است را بررسی کنید. توجه داشته باشید که کلاسAndroidViewModel()
را گسترش می دهد. این کلاس مانندViewModel
است، اما زمینه برنامه را به عنوان یک پارامتر می گیرد و آن را به عنوان یک ویژگی در دسترس قرار می دهد. بعدا به این نیاز خواهید داشت.
class SleepTrackerViewModel(
val database: SleepDatabaseDao,
application: Application) : AndroidViewModel(application) {
}
مرحله 2: SleepTrackerViewModelFactory را اضافه کنید
- در بسته Sleeptracker ، SleepTrackerViewModelFactory.kt را باز کنید.
- کدی که برای کارخانه برای شما ارائه شده است را بررسی کنید که در زیر نشان داده شده است:
class SleepTrackerViewModelFactory(
private val dataSource: SleepDatabaseDao,
private val application: Application) : ViewModelProvider.Factory {
@Suppress("unchecked_cast")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
return SleepTrackerViewModel(dataSource, application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
به موارد زیر توجه کنید:
-
SleepTrackerViewModelFactory
ارائه شده همان آرگومانViewModel
را می گیرد وViewModelProvider.Factory
را گسترش می دهد. - در داخل کارخانه، کد
create()
را لغو می کند، که هر نوع کلاسی را به عنوان آرگومان می گیرد وViewModel
را برمی گرداند. - در بدنه
create()
کد بررسی می کند که یک کلاسSleepTrackerViewModel
در دسترس است و اگر وجود داشته باشد، نمونه ای از آن را برمی گرداند. در غیر این صورت، کد یک استثنا ایجاد می کند.
مرحله 3: SleepTrackerFragment را به روز کنید
- در
SleepTrackerFragment
، یک مرجع به زمینه برنامه دریافت کنید. مرجع را درonCreateView()
زیرbinding
قرار دهید. برای ارسال به ارائهدهنده کارخانه view-model به برنامهای که این قطعه به آن متصل است، نیاز دارید.
اگر مقدارnull
باشد، تابعrequireNotNull
Kotlin یکIllegalArgumentException
ایجاد می کند.
val application = requireNotNull(this.activity).application
- شما نیاز به ارجاع به منبع داده خود از طریق ارجاع به DAO دارید. در
onCreateView()
قبل ازreturn
، یکdataSource
تعریف کنید. برای دریافت ارجاع به DAO پایگاه داده، ازSleepDatabase.getInstance(application).sleepDatabaseDao
کنید.
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
- در
onCreateView()
، قبل ازreturn
، یک نمونه ازviewModelFactory
ایجاد کنید. باید آن را بهdataSource
وapplication
کنید.
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
- اکنون که کارخانه دارید، یک مرجع به
SleepTrackerViewModel
دریافت کنید. پارامترSleepTrackerViewModel::class.java
به کلاس جاوا زمان اجرا این شی اشاره دارد.
val sleepTrackerViewModel =
ViewModelProviders.of(
this, viewModelFactory).get(SleepTrackerViewModel::class.java)
- کد تمام شده شما باید به شکل زیر باشد:
// Create an instance of the ViewModel Factory.
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
// Get a reference to the ViewModel associated with this fragment.
val sleepTrackerViewModel =
ViewModelProviders.of(
this, viewModelFactory).get(SleepTrackerViewModel::class.java)
در اینجا روش onCreateView()
تاکنون آمده است:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Get a reference to the binding object and inflate the fragment views.
val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
inflater, R.layout.fragment_sleep_tracker, container, false)
val application = requireNotNull(this.activity).application
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
val sleepTrackerViewModel =
ViewModelProviders.of(
this, viewModelFactory).get(SleepTrackerViewModel::class.java)
return binding.root
}
مرحله 4: پیوند داده را برای مدل view اضافه کنید
با نصب ViewModel
اصلی، برای اتصال ViewModel
به UI، باید تنظیمات اتصال داده را در SleepTrackerFragment
تمام کنید.
در فایل طرح بندی fragment_sleep_tracker.xml
:
- در داخل بلوک
<data>
، یک<variable>
ایجاد کنید که به کلاسSleepTrackerViewModel
ارجاع می دهد.
<data>
<variable
name="sleepTrackerViewModel"
type="com.example.android.trackmysleepquality.sleeptracker.SleepTrackerViewModel" />
</data>
در SleepTrackerFragment
:
- فعالیت فعلی را به عنوان مالک چرخه حیات اتصال تنظیم کنید. این کد را در
onCreateView()
قبل از دستورreturn
اضافه کنید:
binding.setLifecycleOwner(this)
- متغیر binding
sleepTrackerViewModel
را بهsleepTrackerViewModel
. این کد را داخلonCreateView()
، زیر کدی کهSleepTrackerViewModel
را ایجاد می کند، قرار دهید:
binding.sleepTrackerViewModel = sleepTrackerViewModel
- احتمالاً خطایی خواهید دید، زیرا باید شی binding را دوباره ایجاد کنید. پروژه را تمیز کرده و دوباره بسازید تا از شر خطا خلاص شوید.
- در نهایت، مثل همیشه، مطمئن شوید که کد شما بدون خطا ساخته و اجرا می شود.
در کاتلین، کوروتین ها راهی برای انجام وظایف طولانی مدت به زیبایی و کارآمدی هستند. کوروتین های Kotlin به شما امکان می دهند کد مبتنی بر تماس را به کد ترتیبی تبدیل کنید. کدهایی که به صورت متوالی نوشته میشوند معمولاً خواندن آسانتر هستند و حتی میتوانند از ویژگیهای زبانی مانند استثناها استفاده کنند. در پایان، کوروتینها و فراخوانها همین کار را انجام میدهند: منتظر میمانند تا نتیجهای از یک کار طولانیمدت در دسترس باشد و به اجرا ادامه میدهند.
کوروتین ها دارای ویژگی های زیر هستند:
- کوروتین ها ناهمزمان و غیر مسدود هستند.
- کوروتین ها از توابع تعلیق برای ایجاد کدهای ناهمزمان به ترتیب استفاده می کنند.
کوروتین ها ناهمزمان هستند.
یک کوروتین مستقل از مراحل اصلی اجرای برنامه شما اجرا می شود. این می تواند به صورت موازی یا روی یک پردازنده جداگانه باشد. همچنین ممکن است در حالی که بقیه برنامه منتظر ورودی هستند، شما به صورت مخفیانه در پردازش کمی قرار بگیرید. یکی از جنبه های مهم async این است که نمی توانید انتظار داشته باشید که نتیجه در دسترس باشد، مگر اینکه صریحاً منتظر آن باشید.
مثلاً فرض کنید سؤالی دارید که نیاز به تحقیق دارد و از یک همکار میخواهید که پاسخ آن را بیابد. آنها می روند و روی آن کار می کنند، که مثل این است که کار را "ناهمزمان" و "روی یک رشته جداگانه" انجام می دهند. تا زمانی که همکارتان برگردد و به شما بگوید جواب چیست، میتوانید کارهای دیگری را که به پاسخ بستگی ندارد ادامه دهید.
کوروتین ها غیر مسدود کننده هستند.
غیر مسدود کردن به این معنی است که یک کوروتین رشته اصلی یا رابط کاربری را مسدود نمی کند. بنابراین با استفاده از برنامههای مشترک، کاربران همیشه روانترین تجربه ممکن را دارند، زیرا تعامل رابط کاربری همیشه اولویت دارد.
کوروتین ها از توابع تعلیق برای ایجاد کدهای ناهمزمان به ترتیب استفاده می کنند.
کلمه کلیدی suspend
روشی است که کاتلین برای علامت گذاری یک تابع یا نوع تابع به عنوان در دسترس بودن در روال ها انجام می دهد. هنگامی که یک کوروتین تابعی را که با suspend
مشخص شده است فراخوانی میکند، به جای مسدود کردن تا زمانی که تابع مانند یک فراخوانی تابع عادی برگردد، کوروتین اجرا را تا زمانی که نتیجه آماده شود به حالت تعلیق در میآورد. سپس کار روتین از همان جایی که متوقف شد، با نتیجه از سر گرفته می شود.
در حالی که کوروتین به حالت تعلیق درآمده و منتظر نتیجه است، رشتهای را که روی آن در حال اجراست باز میکند. به این ترتیب، سایر توابع یا برنامهها میتوانند اجرا شوند.
کلمه کلیدی suspend
رشته ای را که کد روی آن اجرا می شود مشخص نمی کند. یک تابع تعلیق ممکن است روی یک رشته پسزمینه یا روی رشته اصلی اجرا شود.
برای استفاده از کوروتین ها در کاتلین، به سه چیز نیاز دارید:
- یک شغل
- یک اعزام کننده
- یک محدوده
شغل : اساساً شغل هر چیزی است که بتوان آن را لغو کرد. هر کوروتین کاری دارد و شما می توانید از آن کار برای لغو کوروتین استفاده کنید. مشاغل را می توان در سلسله مراتب والدین-فرزند مرتب کرد. لغو یک شغل والدین بلافاصله همه فرزندان کار را لغو می کند، که بسیار راحت تر از لغو هر برنامه به صورت دستی است.
Dispatcher: Dispatcher برنامه هایی را برای اجرا بر روی موضوعات مختلف ارسال می کند. به عنوان مثال، Dispatcher.Main
وظایف را روی رشته اصلی اجرا میکند و Dispatcher.IO
وظایف ورودی/خروجی را به یک مجموعه مشترک از رشتهها مسدود میکند.
محدوده: محدوده یک کوروتین زمینه ای را که در آن کوروتین اجرا می شود، تعریف می کند. یک محدوده اطلاعاتی در مورد شغل و توزیع کننده یک کوروتین ترکیب می کند. Scope ها برنامه های کاری را پیگیری می کنند. هنگامی که یک کوروتین را راهاندازی میکنید، "در یک محدوده" قرار دارد، به این معنی که شما مشخص کردهاید که کدام محدوده آن را دنبال میکند.
شما می خواهید که کاربر بتواند به روش های زیر با داده های خواب تعامل داشته باشد:
- هنگامی که کاربر روی دکمه Start ضربه می زند، برنامه یک شب خواب جدید ایجاد می کند و شب خواب را در پایگاه داده ذخیره می کند.
- وقتی کاربر روی دکمه توقف ضربه میزند، برنامه شب را با زمان پایان بهروزرسانی میکند.
- وقتی کاربر روی دکمه Clear ضربه میزند، برنامه دادههای پایگاه داده را پاک میکند.
این عملیات پایگاه داده می تواند زمان زیادی طول بکشد، بنابراین باید در یک رشته جداگانه اجرا شوند.
مرحله 1: کوروتین ها را برای عملیات پایگاه داده تنظیم کنید
هنگامی که دکمه Start در برنامه Sleep Tracker ضربه می زند، می خواهید تابعی را در SleepTrackerViewModel
فراخوانی کنید تا یک نمونه جدید از SleepNight
ایجاد کنید و نمونه را در پایگاه داده ذخیره کنید.
با ضربه زدن روی هر یک از دکمهها، عملیات پایگاه داده مانند ایجاد یا بهروزرسانی SleepNight
. به همین دلیل و موارد دیگر، شما از کوروتین ها برای پیاده سازی کنترل کننده های کلیک برای دکمه های برنامه استفاده می کنید.
- فایل
build.gradle
در سطح برنامه را باز کنید و وابستگی های کوروتین ها را پیدا کنید. برای استفاده از کوروتین ها به این وابستگی ها نیاز دارید که برای شما اضافه شده است.
$coroutine_version
در فایلbuild.gradle
پروژه به صورتcoroutine_version =
'1.0.0'
تعریف شده است.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
- فایل
SleepTrackerViewModel
را باز کنید. - در بدنه کلاس،
viewModelJob
را تعریف کرده و یک نمونه ازJob
به آن اختصاص دهید. اینviewModelJob
به شما این امکان را میدهد تا زمانی که مدل view دیگر استفاده نمیشود و از بین میرود، تمام برنامههایی که توسط این مدل view شروع شدهاند را لغو کنید. به این ترتیب، شما با برنامههایی که جایی برای بازگشت ندارند، مواجه نمیشوید.
private var viewModelJob = Job()
- در پایان بدنه کلاس،
onCleared()
را لغو کنید و همه برنامهها را لغو کنید. هنگامی کهViewModel
از بین می رود،onCleared()
فراخوانی می شود.
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
- درست در زیر تعریف
viewModelJob
، یکuiScope
برای کوروتین ها تعریف کنید. scope تعیین میکند که کوروتین روی چه رشتهای اجرا میشود، و دامنه نیز باید در مورد کار بداند. برای دریافت یک محدوده، یک نمونه ازCoroutineScope
را بخواهید، و در یک توزیع کننده و یک شغل عبور کنید.
استفاده از Dispatchers.Main
به این معنی است که روالهای راهاندازی شده در uiScope
روی رشته اصلی اجرا میشوند. این برای بسیاری از برنامههای معمولی که توسط ViewModel
شروع شدهاند، معقول است، زیرا پس از انجام برخی پردازشها، منجر به بهروزرسانی رابط کاربری میشوند.
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
- در زیر تعریف
uiScope
، یک متغیر به نامtonight
برای نگه داشتن شب جاری تعریف کنید. متغیرMutableLiveData
را بسازید، زیرا باید بتوانید داده ها را مشاهده کرده و تغییر دهید.
private var tonight = MutableLiveData<SleepNight?>()
- برای مقداردهی اولیه متغیر
tonight
در اسرع وقت، یک بلوکinit
زیر تعریفtonight
ایجاد کنید وinitializeTonight()
را فراخوانی کنید. در مرحله بعدinitializeTonight()
را تعریف می کنید.
init {
initializeTonight()
}
- در زیر بلوک
init
،initializeTonight()
را پیاده سازی کنید. درuiScope
، یک coroutine را راه اندازی کنید. در داخل، با فراخوانیgetTonightFromDatabase()
مقدارtonight
را از پایگاه داده دریافت کنید و مقدار را بهtonight.value
اختصاص دهید. در مرحله بعدgetTonightFromDatabase()
را تعریف می کنید.
private fun initializeTonight() {
uiScope.launch {
tonight.value = getTonightFromDatabase()
}
}
- پیاده سازی
getTonightFromDatabase()
. آن را بهعنوان یک تابعprivate suspend
تعریف کنید که یکSleepNight
قابل تهی را برمیگرداند، اگر جریانی وجود نداشته باشد کهSleepNight
شروع شده باشد. این شما را با یک خطا مواجه می کند، زیرا تابع باید چیزی را برگرداند.
private suspend fun getTonightFromDatabase(): SleepNight? { }
- در داخل بدنه تابع
getTonightFromDatabase()
، نتیجه را از یک کوروتین که در زمینهDispatchers.IO
اجرا می شود، برگردانید. از دیسپچر I/O استفاده کنید، زیرا دریافت داده از پایگاه داده یک عملیات I/O است و ربطی به رابط کاربری ندارد.
return withContext(Dispatchers.IO) {}
- در داخل بلوک برگشتی، اجازه دهید کوروتین امشب (جدیدترین شب) از پایگاه داده دریافت شود. اگر زمان شروع و پایان یکسان نیست، به این معنی که شب قبلاً کامل شده است،
null
را برگردانید. در غیر این صورت شب را برگردانید.
var night = database.getTonight()
if (night?.endTimeMilli != night?.startTimeMilli) {
night = null
}
night
تابع suspend getTonightFromDatabase()
کامل شده شما باید شبیه این باشد. دیگر نباید خطایی وجود داشته باشد.
private suspend fun getTonightFromDatabase(): SleepNight? {
return withContext(Dispatchers.IO) {
var night = database.getTonight()
if (night?.endTimeMilli != night?.startTimeMilli) {
night = null
}
night
}
}
مرحله 2: کنترل کننده کلیک را برای دکمه Start اضافه کنید
اکنون می توانید onStartTracking()
را پیاده سازی کنید، کنترل کننده کلیک برای دکمه Start . باید یک SleepNight
جدید ایجاد کنید، آن را در پایگاه داده وارد کنید و آن را به tonight
اختصاص دهید. ساختار onStartTracking()
بسیار شبیه initializeTonight()
خواهد بود.
- با تعریف تابع برای
onStartTracking()
شروع کنید. میتوانید کنترلکنندههای کلیک را در بالایonCleared()
در فایلSleepTrackerViewModel
قرار دهید.
fun onStartTracking() {}
- در داخل
onStartTracking()
یک coroutine درuiScope
راه اندازی کنید، زیرا برای ادامه و به روز رسانی UI به این نتیجه نیاز دارید.
uiScope.launch {}
- در داخل راهاندازی معمولی، یک
SleepNight
جدید ایجاد کنید که زمان فعلی را به عنوان زمان شروع ثبت میکند.
val newNight = SleepNight()
- هنوز در راه اندازی کوروتین،
insert()
را برای درجnewNight
در پایگاه داده فراخوانی کنید. یک خطا خواهید دید، زیرا هنوز این تابع تعلیقinsert()
را تعریف نکرده اید. (این تابع DAO به همین نام نیست.)
insert(newNight)
- همچنین در راه اندازی معمولی،
tonight
به روز رسانی کنید.
tonight.value = getTonightFromDatabase()
- در زیر
onStartTracking()
،insert()
را به عنوان یک تابعprivate suspend
تعریف کنید که یکSleepNight
را به عنوان آرگومان خود می گیرد.
private suspend fun insert(night: SleepNight) {}
- برای متن
insert()
یک coroutine را در زمینه I/O اجرا کنید و با فراخوانیinsert()
از DAO، شب را در پایگاه داده قرار دهید.
withContext(Dispatchers.IO) {
database.insert(night)
}
- در فایل طرح بندی
fragment_sleep_tracker.xml
، کنترل کننده کلیک برایonStartTracking()
را بهstart_button
با استفاده از جادوی اتصال داده که قبلاً تنظیم کردید اضافه کنید. نماد تابع@{() ->
یک تابع لامبدا ایجاد می کند که هیچ آرگومان نمی گیرد و کنترل کننده کلیک را درsleepTrackerViewModel
می کند.
android:onClick="@{() -> sleepTrackerViewModel.onStartTracking()}"
- اپلیکیشن خود را بسازید و اجرا کنید. روی دکمه Start ضربه بزنید. این عمل داده ایجاد می کند، اما شما هنوز چیزی را نمی توانید ببینید. بعدش شما درستش کنید
fun someWorkNeedsToBeDone { uiScope.launch { suspendFunction() } } suspend fun suspendFunction() { withContext(Dispatchers.IO) { longrunningWork() } }
مرحله 3: نمایش داده ها
در SleepTrackerViewModel
، متغیر nights
به LiveData
اشاره می کند زیرا getAllNights()
در DAO LiveData
را برمی گرداند.
این یک ویژگی Room
است که هر بار که داده های پایگاه داده تغییر می کند، nights
های LiveData
به روز می شود تا آخرین داده ها را نشان دهد. شما هرگز نیازی به تنظیم صریح LiveData
یا به روز رسانی آن ندارید. Room
داده ها را برای مطابقت با پایگاه داده به روز می کند.
با این حال، اگر nights
ها را در نمای متنی نمایش دهید، مرجع شی را نشان می دهد. برای دیدن محتویات شی، داده ها را به یک رشته فرمت شده تبدیل کنید. از یک نقشه Transformation
استفاده کنید که هر بار که nights
ها داده های جدیدی از پایگاه داده دریافت می کنند اجرا می شود.
- فایل
Util.kt
را باز کنید و کد مربوط به تعریفformatNights()
و عباراتimport
مرتبط را از حالت کامنت خارج کنید. برای لغو کامنت کد در Android Studio، تمام کدهایی که با//
مشخص شده اند را انتخاب کنید وCmd+/
یاControl+/
را فشار دهید. - توجه داشته باشید که
formatNights()
یک نوعSpanned
را برمی گرداند که یک رشته با فرمت HTML است. - strings.xml را باز کنید. به استفاده از
CDATA
برای قالب بندی منابع رشته ای برای نمایش داده های خواب توجه کنید. - SleepTrackerViewModel را باز کنید. در کلاس
SleepTrackerViewModel
، در زیر تعریفuiScope
، متغیری به نامnights
تعریف کنید. تمام شب ها را از پایگاه داده دریافت کنید و آنها را به متغیرnights
اختصاص دهید.
private val nights = database.getAllNights()
- درست در زیر تعریف
nights
، کدی را برای تبدیلnights
ها بهnightsString
کنید. از تابعformatNights()
ازUtil.kt
استفاده کنید.
nights
ها را به تابعmap()
از کلاسTransformations
منتقل کنید. برای دسترسی به منابع رشته خود، تابع نگاشت را به صورت فراخوانیformatNights()
تعریف کنید.nights
تامین و یک شیResources
.
val nightsString = Transformations.map(nights) { nights ->
formatNights(nights, application.resources)
}
- فایل طرح بندی
fragment_sleep_tracker.xml
را باز کنید. درTextView
، در ویژگیandroid:text
، اکنون می توانید رشته منبع را با ارجاع بهnightsString
کنید.
"@{sleepTrackerViewModel.nightsString}"
- کد خود را دوباره بسازید و برنامه را اجرا کنید. همه داده های خواب شما با زمان شروع باید اکنون نمایش داده شود.
- چند بار دیگر روی دکمه Start ضربه بزنید و داده های بیشتری را مشاهده خواهید کرد.
در مرحله بعد، عملکرد دکمه Stop را فعال می کنید.
مرحله 4: کنترل کننده کلیک را برای دکمه Stop اضافه کنید
با استفاده از همان الگوی مرحله قبل، کنترل کننده کلیک را برای دکمه Stop در SleepTrackerViewModel.
-
onStopTracking()
را بهViewModel
اضافه کنید. یک coroutine را درuiScope
راه اندازی کنید. اگر زمان پایان هنوز تنظیم نشده است،endTimeMilli
را روی زمان فعلی سیستم تنظیم کنید وupdate()
را با داده های شبانه فراخوانی کنید.
در Kotlin، نحوlabel
return@
، تابعی را که این عبارت از آن برمیگرداند، در میان چندین تابع تو در تو، مشخص میکند.
fun onStopTracking() {
uiScope.launch {
val oldNight = tonight.value ?: return@launch
oldNight.endTimeMilli = System.currentTimeMillis()
update(oldNight)
}
}
- با استفاده از همان الگویی که برای پیاده سازی
insert()
استفاده کردید،update()
را پیاده سازی کنید.
private suspend fun update(night: SleepNight) {
withContext(Dispatchers.IO) {
database.update(night)
}
}
- برای اتصال کنترل کننده کلیک به رابط کاربری، فایل طرح بندی
fragment_sleep_tracker.xml
را باز کنید و کنترل کننده کلیک را بهstop_button
اضافه کنید.
android:onClick="@{() -> sleepTrackerViewModel.onStopTracking()}"
- اپلیکیشن خود را بسازید و اجرا کنید.
- روی Start و سپس Stop ضربه بزنید. شما زمان شروع، زمان پایان، کیفیت خواب بدون ارزش و زمان خواب را مشاهده می کنید.
مرحله 5: کنترل کننده کلیک را برای دکمه Clear اضافه کنید
- به طور مشابه،
onClear()
وclear()
را پیاده سازی کنید.
fun onClear() {
uiScope.launch {
clear()
tonight.value = null
}
}
suspend fun clear() {
withContext(Dispatchers.IO) {
database.clear()
}
}
- برای اتصال کنترل کننده کلیک به رابط کاربری،
fragment_sleep_tracker.xml
را باز کنید و کنترل کننده کلیک را بهclear_button
اضافه کنید.
android:onClick="@{() -> sleepTrackerViewModel.onClear()}"
- اپلیکیشن خود را بسازید و اجرا کنید.
- برای خلاص شدن از شر تمام داده ها، روی Clear ضربه بزنید. سپس روی Start و Stop ضربه بزنید تا داده های جدید ایجاد شود.
پروژه Android Studio: TrackMySleepQualityCoroutines
- از
ViewModel
،ViewModelFactory
و data binding برای تنظیم معماری UI برای برنامه استفاده کنید. - برای اینکه رابط کاربری به خوبی اجرا شود، از کوروتین ها برای کارهای طولانی مدت، مانند تمام عملیات پایگاه داده استفاده کنید.
- کوروتین ها ناهمزمان و غیر مسدود هستند. آنها از توابع
suspend
برای ایجاد کدهای ناهمزمان به ترتیب استفاده می کنند. - هنگامی که یک برنامه مشترک یک تابع مشخص شده با
suspend
فراخوانی می کند، به جای مسدود کردن تا زمانی که آن تابع مانند یک فراخوانی تابع عادی برگردد، اجرا را تا زمانی که نتیجه آماده شود به حالت تعلیق در می آورد. سپس با نتیجه از همان جایی که متوقف شد، از سر می گیرد. - تفاوت بین مسدود کردن و تعلیق در این است که اگر یک نخ مسدود شود، کار دیگری اتفاق نمی افتد. اگر نخ معلق باشد، تا زمانی که نتیجه در دسترس باشد، کار دیگری انجام می شود.
برای راه اندازی یک کوروتین، به یک شغل، یک توزیع کننده و یک محدوده نیاز دارید:
- اساساً شغل هر چیزی است که بتوان آن را لغو کرد. هر کوروتین کاری دارد، و شما میتوانید از یک کار برای لغو یک کوروتین استفاده کنید.
- دیسپچر برنامههایی را برای اجرا در رشتههای مختلف ارسال میکند.
Dispatcher.Main
وظایف را روی رشته اصلی اجرا میکند وDispartcher.IO
برای بارگذاری وظایف مسدودکننده ورودی/خروجی در یک مجموعه مشترک از رشتهها است. - این محدوده اطلاعاتی از جمله یک شغل و توزیع کننده را برای تعریف زمینه ای که برنامه در آن اجرا می شود ترکیب می کند. Scope ها برنامه های کاری را پیگیری می کنند.
برای پیاده سازی کنترل کننده های کلیک که عملیات پایگاه داده را راه اندازی می کنند، از این الگو پیروی کنید:
- برنامهای را راهاندازی کنید که روی رشته اصلی یا UI اجرا میشود، زیرا نتیجه روی UI تأثیر میگذارد.
- برای انجام کار طولانی مدت، یک تابع تعلیق را فراخوانی کنید تا در زمان انتظار برای نتیجه، رشته رابط کاربری را مسدود نکنید.
- کار طولانی مدت ربطی به رابط کاربری ندارد، بنابراین به زمینه I/O بروید. به این ترتیب، کار می تواند در یک thread pool که بهینه شده است اجرا شود و برای این نوع عملیات کنار گذاشته شود.
- سپس تابع پایگاه داده را برای انجام کار فراخوانی کنید.
از نقشه Transformations
برای ایجاد یک رشته از یک شی LiveData
هر بار که شیء تغییر می کند استفاده کنید.
دوره بی ادبی:
مستندات برنامه نویس اندروید:
-
RoomDatabase
- استفاده مجدد از طرحبندیها با <include/>
-
ViewModelProvider.Factory
-
SimpleDateFormat
-
HtmlCompat
سایر اسناد و مقالات:
- الگوی کارخانه
- Coroutines Codelab
- کوروتین ها، اسناد رسمی
- زمینه و توزیع کنندگان کوروتین
-
Dispatchers
- از حد مجاز سرعت اندروید فراتر بروید
-
Job
-
launch
- بازگشت و پرش در کاتلین
- CDATA مخفف کاراکتر داده است. CDATA به این معنی است که دادههای بین این رشتهها شامل دادههایی هستند که میتوانند به عنوان نشانهگذاری XML تفسیر شوند، اما نباید چنین باشند.
این بخش، تکالیف احتمالی را برای دانشآموزانی که در این آزمایشگاه کد به عنوان بخشی از دورهای که توسط یک مربی هدایت میشود، فهرست میکند. این وظیفه مربی است که موارد زیر را انجام دهد:
- در صورت نیاز تکالیف را تعیین کنید.
- نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
- تکالیف را نمره دهید.
مربیان میتوانند از این پیشنهادات به اندازهای که میخواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر میکنند مناسب است به آنها اختصاص دهند.
اگر به تنهایی بر روی این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.
به این سوالات پاسخ دهید
سوال 1
کدام یک از موارد زیر از مزایای کوروتین ها هستند:
- غیر مسدود کننده هستند
- آنها به صورت ناهمزمان اجرا می شوند.
- آنها را می توان روی رشته ای غیر از نخ اصلی اجرا کرد.
- آنها همیشه باعث می شوند برنامه سریعتر اجرا شود.
- آنها می توانند از استثناها استفاده کنند.
- آنها را می توان به صورت کد خطی نوشت و خواند.
سوال 2
تابع تعلیق چیست؟
- یک تابع معمولی که با کلمه کلیدی
suspend
حاشیه نویسی شده است. - تابعی که می توان آن را درون کوروتین ها فراخوانی کرد.
- در حالی که یک تابع تعلیق در حال اجرا است، رشته فراخوانی به حالت تعلیق در می آید.
- توابع تعلیق همیشه باید در پسزمینه اجرا شوند.
سوال 3
تفاوت بین مسدود کردن و تعلیق یک موضوع چیست؟ تمام موارد واقعی را علامت بزنید.
- وقتی اجرا مسدود می شود، هیچ کار دیگری را نمی توان روی رشته مسدود شده اجرا کرد.
- هنگامی که اجرا به حالت تعلیق در میآید، نخ میتواند در حالی که منتظر تکمیل کار بارگذاری شده است، کار دیگری انجام دهد.
- تعلیق کارآمدتر است، زیرا ممکن است رشتهها منتظر نباشند و کاری انجام ندهند.
- خواه مسدود یا تعلیق شده باشد، اجرا همچنان منتظر نتیجه کار قبل از ادامه است.
شروع به درس بعدی:
برای پیوند به دیگر کدهای این دوره، به صفحه فرود کد لبههای کد پایه Android Kotlin Fundamentals مراجعه کنید.