معرفی فعالیت ها

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

این سند مفهوم فعالیت‌ها را معرفی می‌کند و سپس راهنمایی‌های مختصری در مورد نحوه کار با آنها ارائه می‌دهد. برای اطلاعات بیشتر در مورد بهترین شیوه‌ها در معماری برنامه خود، به راهنمای معماری برنامه مراجعه کنید.

مفهوم فعالیت‌ها

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

کلاس Activity برای تسهیل این الگو طراحی شده است. وقتی یک برنامه، برنامه‌ی دیگری را فراخوانی می‌کند، برنامه‌ی فراخوانی‌کننده، به جای کل برنامه، یک activity را در برنامه‌ی دیگر فراخوانی می‌کند. به این ترتیب، activity به عنوان نقطه‌ی ورود برای تعامل برنامه با کاربر عمل می‌کند. شما یک activity را به عنوان یک زیرکلاس از کلاس Activity پیاده‌سازی می‌کنید.

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

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

برای استفاده از اکتیویتی‌ها در برنامه خود، باید اطلاعات مربوط به آنها را در مانیفست برنامه ثبت کنید و آگاهی از چرخه حیات اکتیویتی‌ها تمرین خوبی است. ادامه این سند به معرفی این موضوعات می‌پردازد.

پیکربندی مانیفست

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

فعالیت‌ها را اعلام کنید

برای تعریف اکتیویتی خود، فایل مانیفست خود را باز کنید و یک عنصر <activity> را به عنوان فرزند عنصر <application> اضافه کنید. برای مثال:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

تنها ویژگی مورد نیاز برای این عنصر android:name است که نام کلاس activity را مشخص می‌کند. همچنین می‌توانید ویژگی‌هایی را اضافه کنید که ویژگی‌های activity مانند برچسب، آیکون یا تم UI را تعریف می‌کنند. برای اطلاعات بیشتر در مورد این ویژگی‌ها و سایر ویژگی‌ها، به مستندات مرجع عنصر <activity> مراجعه کنید.

فیلترهای هدف را اعلام کنید

فیلترهای Intent یکی از ویژگی‌های بسیار قدرتمند پلتفرم اندروید هستند. آن‌ها امکان راه‌اندازی یک فعالیت را نه تنها بر اساس یک درخواست صریح ، بلکه بر اساس یک درخواست ضمنی نیز فراهم می‌کنند. به عنوان مثال، یک درخواست صریح ممکن است به سیستم بگوید که "فعالیت ارسال ایمیل را در برنامه Gmail شروع کن". در مقابل، یک درخواست ضمنی به سیستم می‌گوید که "یک صفحه ارسال ایمیل را در هر فعالیتی که می‌تواند این کار را انجام دهد، شروع کن." وقتی رابط کاربری سیستم از کاربر می‌پرسد که از کدام برنامه برای انجام یک کار استفاده کند، این یک فیلتر intent در حال کار است.

شما می‌توانید با تعریف یک ویژگی <intent-filter> در عنصر <activity> از این ویژگی بهره ببرید. تعریف این عنصر شامل یک عنصر <action> و به صورت اختیاری، یک عنصر <category> و/یا یک عنصر <data> می‌شود. این عناصر با هم ترکیب می‌شوند تا نوع intent ای را که activity شما می‌تواند به آن پاسخ دهد، مشخص کنند. به عنوان مثال، قطعه کد زیر نحوه پیکربندی activity ای را نشان می‌دهد که داده‌های متنی و ایمیل ارسال می‌کند و درخواست‌هایی را از activity های دیگر برای انجام این کار دریافت می‌کند:

<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>
   <intent-filter>
        <action android:name="android.intent.action.SENDTO" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="mailto" />
    </intent-filter>
</activity>

در این مثال، عنصر <action> مشخص می‌کند که این فعالیت داده ارسال می‌کند. اعلام عنصر <category> به عنوان DEFAULT فعالیت را قادر می‌سازد تا درخواست‌های راه‌اندازی را دریافت کند. عنصر <data> نوع داده‌ای را که این فعالیت می‌تواند ارسال کند، مشخص می‌کند. قطعه کد زیر نحوه فراخوانی فعالیتی که در بالا توضیح داده شد را برای نوشتن ایمیل نشان می‌دهد:

fun composeEmail(addresses: Array<String>, subject: String) {
    val intent = Intent(Intent.ACTION_SENDTO).apply {
        data = Uri.parse("mailto:") // Only email apps handle this.
        putExtra(Intent.EXTRA_EMAIL, addresses)
        putExtra(Intent.EXTRA_SUBJECT, subject)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

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

مدیریت اهداف ورودی

مثال زیر الگویی را برای مدیریت چرخه حیات فعالیت در حین مدیریت چندین نوع هدف نشان می‌دهد: اشتراک‌گذاری متن واحد، تصاویر واحد و آرایه‌های تصویر چندگانه. با مسیریابی این ورودی‌های متنوع از طریق یک تابع handleIntent متمرکز، تضمین می‌شود که هر دو اقدام ACTION_SEND و ACTION_SEND_MULTIPLE به درستی تجزیه و تحلیل شده و برای به‌روزرسانی واکنشی رابط کاربری به ViewModel واگذار می‌شوند.

class ExampleActivity : ComponentActivity() {
  private val viewModel: MyViewModel by viewModels()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    handleIntent(intent)
    setContent {
      ComposeApp(viewModel)
    }
  }

  override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    setIntent(intent)
    handleIntent(intent)
  }

  private fun handleIntent(intent: Intent?) {
    when (intent?.action) {
      Intent.ACTION_SEND -> {
        if ("text/plain" == intent.type) {
          intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
            viewModel.handleText(it) // Update UI to reflect text being shared
          }
        } else if (intent.type?.startsWith("image/") == true) {
          (intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))?.let {
            viewModel.handleImage(it) // Update UI to reflect image being shared
          }
        }
      }

      Intent.ACTION_SEND_MULTIPLE -> {
          if (intent.type?.startsWith("image/") == true) {
              intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)?.let {
                  viewModel.handleMultipleImages(it) // Update UI to reflect multiple images being shared
              }
          } else {
              // Handle other types
          }
      }

      else -> {
          // Handle other intents
      }
    }
  }
}

اعلام مجوزها

شما می‌توانید از تگ <activity> در مانیفست برای کنترل اینکه کدام برنامه‌ها می‌توانند یک اکتیویتی خاص را شروع کنند، استفاده کنید. یک اکتیویتی والد نمی‌تواند یک اکتیویتی فرزند را اجرا کند، مگر اینکه هر دو اکتیویتی مجوزهای یکسانی در مانیفست خود داشته باشند. اگر یک عنصر <uses-permission> برای یک اکتیویتی والد تعریف کنید، هر اکتیویتی فرزند باید یک عنصر <uses-permission> منطبق با آن داشته باشد.

برای مثال، اگر برنامه شما می‌خواهد از یک برنامه فرضی به نام SocialApp برای اشتراک‌گذاری پستی در رسانه‌های اجتماعی استفاده کند، خود SocialApp باید مجوزهایی را که برنامه‌ای که آن را فراخوانی می‌کند باید داشته باشد، تعریف کند:

<manifest>
<activity android:name="...."
   android:permission="com.google.socialapp.permission.SHARE_POST"

/>

سپس، برای اینکه برنامه شما اجازه فراخوانی SocialApp را داشته باشد، باید مجوزهای تعیین شده در مانیفست SocialApp را داشته باشد:

<manifest>
   <uses-permission android:name="com.google.socialapp.permission.SHARE_POST" />
</manifest>

برای اطلاعات بیشتر در مورد مجوزها و امنیت به طور کلی، به چک لیست امنیتی مراجعه کنید.

مدیریت چرخه حیات فعالیت‌ها

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

روی ایجاد

شما باید این تابع فراخوانی (callback) را پیاده‌سازی کنید، که وقتی سیستم activity شما را ایجاد می‌کند، اجرا می‌شود. پیاده‌سازی شما باید اجزای ضروری activity شما را مقداردهی اولیه کند: برای مثال، برنامه شما باید viewها را ایجاد کند و داده‌ها را به لیست‌ها در اینجا متصل کند.

در یک برنامه Compose، از این فراخوانی برای تنظیم میزبان composable خود با استفاده از setContent ، همانطور که در زیر نشان داده شده است، استفاده کنید:

class MyActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text(text = stringResource(id = R.string.greeting))
        }
    }
}

وقتی onCreate تمام می‌شود، فراخوانی بعدی همیشه onStart است.

شروع

با خروج از onCreate ، اکتیویتی وارد حالت Started می‌شود و اکتیویتی برای کاربر قابل مشاهده می‌شود. این فراخوانی شامل آماده‌سازی‌های نهایی اکتیویتی برای آمدن به پیش‌زمینه و تعاملی شدن است.

در رزومه

سیستم این فراخوانی را درست قبل از اینکه اکتیویتی شروع به تعامل با کاربر کند، فراخوانی می‌کند. در این مرحله، اکتیویتی در بالای پشته اکتیویتی قرار دارد و تمام ورودی‌های کاربر را دریافت می‌کند. بیشتر قابلیت‌های اصلی یک برنامه در متد onResume پیاده‌سازی شده است.

تابع فراخوانی onPause همیشه بعد از onResume اجرا می‌شود.

onPause

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

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

شما نباید onPause برای ذخیره داده‌های برنامه یا کاربر، برقراری تماس‌های شبکه یا اجرای تراکنش‌های پایگاه داده استفاده کنید. برای اطلاعات بیشتر در مورد ذخیره داده‌ها، به بخش ذخیره و بازیابی وضعیت گذرای رابط کاربری مراجعه کنید.

پس از اتمام اجرای onPause ، بسته به اینکه پس از ورود فعالیت به حالت Paused چه اتفاقی می‌افتد، فراخوانی بعدی onStop یا onResume خواهد بود.

آن‌استاپ

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

فراخوانی بعدی که سیستم فراخوانی می‌کند، اگر اکتیویتی برای تعامل با کاربر بازگردد، onRestart است، یا اگر این اکتیویتی به طور کامل خاتمه یابد، onDestroy .

روشن-راه‌اندازی

سیستم این تابع فراخوانی را زمانی فراخوانی می‌کند که یک فعالیت در حالت Stopped در شرف راه‌اندازی مجدد باشد. onRestart وضعیت فعالیت را از زمانی که متوقف شده بود، بازیابی می‌کند.

این فراخوانی مجدد همیشه با onStart دنبال می‌شود.

روی تخریب

سیستم قبل از اینکه یک اکتیویتی از بین برود، این تابع فراخوانی (callback) را فراخوانی می‌کند.

این فراخوانی، آخرین فراخوانی است که اکتیویتی دریافت می‌کند. onDestroy معمولاً برای اطمینان از آزاد شدن تمام منابع یک اکتیویتی هنگام از بین رفتن اکتیویتی یا فرآیند حاوی آن پیاده‌سازی می‌شود.

این بخش فقط مقدمه‌ای بر این موضوع ارائه می‌دهد. برای بررسی دقیق‌تر چرخه حیات فعالیت و فراخوانی‌های آن، به چرخه حیات فعالیت مراجعه کنید.