کلاس Activity (به معنی فعالیت)، یک کامپوننت حیاتی و بسیار مهمی در یک اپ اندرویدی میباشد. فعلا به صورت کلیشهای، بدانید که وقتی یک برنامه را اجرا کرده و عناصری را درون آن مشاهده میکنید، در واقع یک اکتیویتی اجرا شده است؛ که این عناصر نیز داخل آن برایتان نمایش داده میشوند!
برخلاف پارادایمها و الگوهای برنامهنویسی که در آن برنامهها با متد main() راهاندازی میشوند، سیستم اندروید کد را در یک نمونهی اکتیویتی آغاز میکند؛ که این کار را از طریق فراخوانیِ متدهای callback (کال بک) مربوط به مراحلِ خاصِ چرخهی حیات اکتیویتی انجام میدهد! (و شما خواهید توانست برای زمان ایجاد اکتیویتی، توقف اکتیویتی و …، کدهایی را بنویسید تا در آن لحظه اجرا شوند.)
این مقاله، به معرفی مفهوم اکتیویتیها پرداخته و سپس روش استفاده و کار کردن با آنها را توضیح میدهد.
مفهوم اکتیویتیها
تجربهی اَپِ موبایلی، که در آن تعامل کاربر با برنامه همواره از نقطهی یکسانی آغاز نمیشود، با همتای اپ دسکتاپی (کامپیوتری) خود متفاوت است. بعلاوه، تجربهی شخصی خود کاربر از کار کردن با برنامه، معمولا به طور قطعی و کامل نخواهد بود! برای نمونه، اگر یک برنامهی ایمیل را از طریق Home-Screen خود اجرا کنید، ممکن است لیستی از ایمیلها را مشاهده کنید؛ ولی در مقابل، اگر شما از یک اَپِ شبکهیاجتماعی استفاده کنید که بتواند برنامهی ایمیل شما را اجرا کند، ممکن است به طور مستقیم، به صفحهی نوشتن یک ایمیل جدید هدایت شوید!
و در واقع کلاس اکتیویتی، برای تسهیل این الگو طراحی شده است. یعنی زمانی که یک اپ، اپ دیگری را فراخونی میکند، اپ اول درواقع یک اکتیویتی را در اپ دوم فراخوانی میکند! به این ترتیب، اکتیویتی به عنوان نقطهی ورود برای یک برنامه جهت تعامل با کاربر عمل میکند! همچنین برای تبدیل یک کلاس به اکتیویتی، آن کلاس بایستی زیرکلاسی از کلاس Activity باشد. (یعنی از آن ارث بری کند)
یک اکتیویتی، پتحرهای را فراهم میکند که در آن برنامه بتواند UI (رابط کاربری) خود را ترسیم کند. این پنجره، به طور معمول تمام صفحهنمایش را پر میکند (پوشش میدهد)، ولی ممکن است از صفحه کوچکتر بوده و روی پنجرههای دیگری نیز شناور شود. به طور کلی یک اکتیویتی، یک صفحه (screen) را برای یک برنامه پیادهسازی میکند. برای نمونه، یکی از اکتویتیهای یک برنامه ممکن است صفحهی تنظیمات را پیادهسازی کند، در حالی که اکتیویتی دیگر، یک صفحهی انتخاب تصویر از گالری را پیادهسازی کرده باشد.
اکثر برنامهها از چندین صفحه تشکیل میشوند؛ به این معنا که آنها شامل چندین اکتیویتی هستند! معمولا یک اکتیویتی در هر برنامهای به عنوان اکتوییتی اصلی (main) در نظر گرفته میشود؛ که اولین صفحه برای زمانیست که کاربر اپلیکیشن را اجرا میکند. هر اکتیویتی، بعدا میتواند اکتیویتی دیگری را جهت انجام اقدامات مختلف، اجرا (شروع) کند! برای مثال، اکتیویتی اصلی ممکن است اکتیویتیهای دیگری را جهت فراهم کردن وظایفی مثل نوشتن ایمیل و یا بازکردن ایمیلهای شخصی اجرا کند.
اگرچه اکتیویتیها باهم کار میکنند تا یک تجربهی کاربری منسجم و معطوفی را در یک برنامه ایجاد کنند، با این حال، هر اکتیویتی به میزان کمی به اکتیویتیهای دیگر محدود است؛ و معمولا کمترین وابستگی بین اکتیویتیها در یک اپلیکیشنی وجود دارد. همچنین اکتیویتیها، اغلب اکتیویتیهای متعلق به برنامههای دیگر را باز میکنند؛ برای مثال، یک اپ مرورگر ممکن است اکتیویتی اشتراک گزاری (share) یک اپ شبکهی اجتماعی را اجرا کند.
جهت استفاده از اکتیویتیها در اپلیکیشن خود، شما باید اطلاعات آنها را در manifest برنامه ثبت کرده و سپس چرخهیحیات اکتیویتی را به درستی مدیریت کنید. در ادامهی مقاله، به معرفی این موضوعات خواهیم پرداخت.
پیکربندی منیفست
برای اپلیکیشنی که بخواهد از اکتیویتیها استفاده کند، شما بایستی اکتیویتیها و ویژگیهای آن را در منیفست (manifest) تعریف کنید.
تعریف اکتیویتیها
برای تعریف یک اکتیویتی، فایل manifest را باز کرده و یک المنت <activity> را به داخل المنت <application> اضافه کنید. برای مثال: (منظور از … ها، ادامهی کد هستند؛ یا همان «و غیره»)
<manifest ... > <application ... > <activity android:name=".ExampleActivity" /> ... </application ... > ... </manifest >
تنها ویژگی ضروری برای این المنت، android:name میباشد؛ که نام کلاس اکتیویتی را مشخص میکند. شما همچنین میتوانید ویژگیهایی را که مشصخات دیگر اکتیویتی، از جمله «عنوان»، «آیکون» و یا «تم رابط کاربری» آن را تعیین میکنند نیز به المنت اکتیویتی اضافه کنید. برای اطلاعات بیشتر دربارهی این موارد و ویژگیهای دیگر، لینک <activity> را مطالعه کنید.
تعریف intent filter ها
Intent filter ها، یکی از ویژگیهای قدرتمند در پلتفرم اندروید هستند. آنها قابلیت اجرای اکتیویتیها را به صورت ضمنی (implicit) نیز فراهم میکنند؛ چراکه در حالت عادی، به صورت صریح یا explicit اجرا میشوند. برای مثال، یک درخواست صریح ممکن است به سیستم بگوید که “اکتیویتیِ ارسالِ ایمیلِ مربوط به اپلیکیشنِ جیمیل را اجرا کن”. ولی در مقابل، یک درخواست ضمنی به سیستم میگوید که “صفحهی ارسال ایمیل را در هر اکتیویتی که میتواند این کار را انجام دهد اجرا کن”. که در این حالت، خود سیستم از کاربر درخواست میکند تا اپلیکیشن مورد نظر خود را از لیست اپلیکیشنهایی که این قابلیت را پشتیبانی میکنند انتخاب نماید.
شما میتوانید با تعریف اِلمنتِ <intent-filter> در داخل اِلمنتِ یک اکتیویتی، از این مزیت بهرهمند شوید. و همچنین جهت پیادهسازی این اِلمنت، بایستی حداقل یک اِلمنت <action> نیز درون آن استفاده کنید. و البته میتوانید از المنتهای <category> و <data> نیز در کنار آن استفاده کنید. این سه المنت، برای مشخص کردن نوع اهداف اکتیویتی شما میتوانند با یکدیگر ترکیب شوند. برای مثال، قطعه کد زیر نشان میدهد که چگونه یک اکتیویتی را برای ارسال دادههای متنی و دریافت درخواست از اکتیویتیهای دیگر پیکربندی کنیم:
<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon"> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity>
در این مثال، المنت <action> مشخص میکند که این اکتیویتی میتواند دادههایی را دریافت کرده و به جایی ارسال کند. تعریف المنت <category> در اینجا به عنوان DEFAULT
، این اکتیوتیتی را قادر میسازد تا درخواستهای راهاندازی را دریافت کند. و المنت <data> مشخص میکند که اکتیویتی قادر است چه نوع دادههایی را برای ارسال دریافت کند که در اینجا دادههای متنی میباشد. کد زیر نشان میدهد که چگونه میتوان یک درخواست ضمنی به اکتیویتیهایی که ویژگی بالا را دارند ایجاد کرد:
// Create the text message with a string Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.setType("text/plain"); sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); // Start the activity startActivity(sendIntent);
val sendIntent = Intent().apply { action = Intent.ACTION_SEND type = "text/plain" putExtra(Intent.EXTRA_TEXT, textMessage) } startActivity(sendIntent)
اکتیویتیهایی که نمیخواهید برای اپلیکیشنهای دیگر در دسترس باشند، نباید intent filter داشته باشند و شما میتوانید آنها را از طریق خود اپلیکیشن و به صورت اینتنت صریح، شروع کنید. برای اطلاعات بیشتر، لینک اینتنت چیست (فارسی) را مطالعه نمایید.
تعریف دسترسیها
شما میتوانید توسط تگ <activity> کنترل کنید که چه اپهایی بتوانند آن را شروع کنند. به این صورت که یک اکتیویتی در یک اپلیکیشن دیگر، نتواند اکتیویتی شما را اجرا کند، مگر اینکه اپلیکیشن اجرا کننده، دسترسی تعیین شده برای اکتیویتی شما را داشته باشد. یعنی شما دسترسی خاصی را برای اکتیویتی خود تعریف میکنید، و سپس اپلیکیشن درخواست کننده، باید المنت <uses-permission> مطابق دسترسی مشخص شده برای اکتیویتی شما را داشته باشد!
برای مثال، اگر اپلیکیشن شما بخواهد یک اپ فرضی به اسم SocialApp را جهت به اشتراک گزاری یک پست در شبکهی اجتماعی استفاده کند، SocialApp خودش باید به صورت زیر دسترسی مورد نیاز را برای اکتیویتی تعیین کرده باشد:
<activity android:name="...." android:permission=”com.google.socialapp.permission.SHARE_POST” />
سپس، برای اینکه اپلیکیشنتان بتواند اکتیویتی این اپ را صدا بزند، بایستی دسترسی اکتیویتی را در منیفست خود قرار داده باشد:
<manifest> <uses-permission android:name="com.google.socialapp.permission.SHARE_POST" /> </manifest>
برای اطلاعات بیشتر در مورد دسترسیها و امنیت عمومی، Security and Permissions را ببینید.
مدیریت چرخه حیات (lifecycle) اکتیویتی
اکتیویتی پس از ایجاد شدن، از حالتهای مختلفی عبور میکند؛ که شما میتوانید از یک سری callback (فراخوان)ها برای مدیریت گذرهای بین این حالتها استفاده کنید. بخشهای زیر، کالبکهای چرخهی حیات اکتیویتی را بهطور خلاصه معرفی میکنند:
onCreate()
شما حتما باید این کالبک را پیادهسازی کنید! این کالبک زمانی که سیستم اندروید، اکتیویتی شما را ایجاد میکند، اجرا خواهد شد. و در آن بایستی کامپوننتهای ضروری اکیتیویتی خود را مقداردهی کنید. برای مثال، اپ شما ویوهایی را ایجاد کرده و دادههایی را داخل ویوها قرار میدهد. همچنین مهمترین چیزی که در این کالبک استفاده میشود، اجرای متد setContentView() جهت اختصاص یک layout به رابطکاربری اکتیویتی میباشد.
پس از onCreate() ، کالبک onStart() بلافاصله اجرا خواهد شد.
onStart()
به محض خروج از وضعیت onCreate()، اکتیویتی وارد وضعیت Started میشود؛ و دراینجا اکتیویتی برای کاربر قابل نمایش خواهد بود. یعنی در onCreate() هنوز اکتیویتی برای نمایش داده شدن آماده نشده است، ولی به محض ورود به onStart()، پنجرهی اکتیویتی برای نمایش داده شدن آماده است.
پس از onStart()، کالبک onResume() بلافاصله اجرا خواهد شد.
onResume()
سیستم اندروید، این کال بک را درست قبل از شروع تعامل اکتیویتی با کاربر فراخوانی میکند. و در این حالت، اکتیویتی در بالاترین سطح پشتهی (Stack) اکتیویتیها قرار داشته و تمامی ورودیهای کاربر را ضبط میکند. اکثر قابلیتهای اصلی برنامه در متد onResume() پیادهسازی میشود.
کالبک onPause()، همیشه در ادامهی onResume() قرار دارد. (البته بلافاصله اجرا نمیشود و در صورت روی دادن شرایطی اجرا میشود).
onPause()
سیستم اندروید، زمانی کالبک onPause() را صدا میزند که اکتیویتی، فوکوس خود را از دست داده و وارد مرحلهی Pause (وقفه/مکث) شده باشد. این وضعیت زمانی اتفاق میافتد که برای مثال، کاربر روی دکمهی بازگشت کلیک کند! همچنین زمانی که سیستم این کالبک را برای اکتیویتی شما صدا بزند، از نظر فنی به این معنا است که اکتویتی شما هنوز تا حدی قابل مشاده است؛ اما اغلب نشانهای از این است که کاربر اکتیویتی را ترک کرده تا به اکتیویتی دیگری برود؛ که باعث توقف موقت اکتیویتی شده و اکتیویتی در آینده وارد وضعیت Stopped یا Resumed خواهد شد.
یک اکتیویتی در وضعیت Paused، میتواند همچنان به آپدیت یا بهروزرسانی UI ادامه دهد. که نمونههایی از این مدل اکتیویتیها، میتوان به نمایش یک صفحهی نقشهی ناوبری و یا مدیاپلیر اشاره کرد که حتی در صورت Paused شدن، مقادیر UI با توجه به تغییرات دادهها در حال بهروزرسانی هستند. درواقع حتی اگر چنین اکتیویتیهایی فوکوس خود را از دست بدهند، کاربر انتظار دارد تا UI به آپدیت خود ادامه دهد! (گرچه رابطکاربری برای کاربر نمایش داده نمیشود؛ ولی هنوز وجود دارد.)
شما نباید از onPause() برای ذخیرهی دادههای اپلیکیشن یا کاربر، ایجاد درخواستهای شبکه و یا اجرای دستورات دیتابیس استفاده کنید. برای اطلاعات بیشتر دربارهی ذخیرهی دادهها، این لینک «Saving and restoring activity state» را ببینید.
کالبک بعدی که پس از onPause() اجرا میشود، یکی از دو موارد زیر خواهد بود:
که بستگی دارد چه اتفاقی پس از ورود اکتیویتی به حالت Paused رخ دهد تا یکی از آن دو اجرا شوند.
onStop()
سیستم اندروید، زمانی کالبک onStop() را صدا میزند که اکتیویتی دیگر برای کاربر قابل مشاهده نباشد (در دسترس نباشد). که این ممکن است به دلیل نابود شدن (destroy) اکتیویتی، اجرای یک اکتیویتی جدید و .. رخ دهد. در این حالت، اکتیویتی، دیگر قابل مشاهده نخواهد بود!
کالبک بعدی که پس از این اجرا خواهد شد:
- onRestart(): در صورتی که اکتیویتی به تعامل مجدد خود با کربر برگدد
- onDestroy(): در صورتی که اکیویتی به طور کامل پایان داده شود
onRestart()
سیستم اندروید زمانی این callback را اجرا میکند که یک اکتیویتی در وضعیت توقف (Stopped) خود، در حال رفتن به ریستارت (راه اندازی مجدد) باشد. همچنین onRestart()، وضعیت اکتیویتی را از زمانی که متوقف شد بازیابی میکند.
onRestart()، تنها پس از onStart() ممکن است اجرا شود و سپس وارد onStart() میشود.
onDestroy()
سیستم اندروید، این کالبک را زمانی فراخونی میکند که یک اکتیویتی، دیگر نابود شده باشد! یعنی به طور کامل به پایان خود رسیده باشد!
onDestroy()، درواقع آخرین جاییست که یک اکتیویتی دریافت میکند؛ و معمولا زمانی پیادهسازی میشود که بخواهیم تمامی منابعی که اکتیویتی از آنها تغذیه میکند را آزاد کنیم تا در اشغال باقی نمانند! (مثل خواندن فایل و …)
منابع: Developer Android
خدا خیرت بده