راه‌اندازی سرویس پیش‌زمینه‌ای

برای راه‌اندازی سرویس پیش‌زمینه‌ای از برنامه‌تان دو مرحله وجود دارد. ابتدا باید سرویس را با فراخوانی context.startForegroundService() راه‌اندازی کنید. سپس، از سرویس بخواهید ServiceCompat.startForeground() را فراخوانی کند تا خودش را به سرویس پیش‌زمینه‌ای ارتقا دهد.

پیش‌نیازها

بسته به اینکه برنامه شما کدام سطح میانای برنامه‌سازی کاربردی را هدف قرار می‌دهد، محدودیت‌هایی برای زمان راه‌اندازی سرویس پیش‌زمینه‌ای توسط برنامه وجود دارد.

  • برنامه‌هایی که Android 12 (سطح API‏ ۳۱) یا بالاتر را هدف قرار می‌دهند، به‌جز چند مورد استثنای خاص، مجاز نیستند وقتی برنامه در پس‌زمینه است سرویس پیش‌زمینه‌ای را شروع کنند. برای اطلاعات بیشتر و اطلاعات مربوط به استثناهای این قانون، محدودیت‌های شروع سرویس پیش‌زمینه‌ای از پس‌زمینه را ببینید.

  • برنامه‌هایی که Android 14 (سطح میانای برنامه‌سازی کاربردی ۳۴) یا بالاتر را هدف قرار می‌دهند باید اجازه‌های مناسب را برای نوع سرویس پیش‌زمینه‌ای درخواست کنند. وقتی برنامه تلاش می‌کند سرویسی را به پیش‌زمینه ارتقا دهد، سیستم اجازه‌های مناسب را بررسی می‌کند و اگر برنامه هرکدام از آن‌ها را نداشته باشد، SecurityException را برمی‌گرداند. برای مثال، اگر بخواهید سرویس پیش‌زمینه‌ای از نوع location راه‌اندازی کنید، سیستم بررسی می‌کند تا مطمئن شود برنامه‌تان ازقبل اجازه ACCESS_COARSE_LOCATION یا ACCESS_FINE_LOCATION را دارد. سند نوع سرویس پیش‌زمینه‌ای پیش‌نیازهای لازم برای هر نوع سرویس پیش‌زمینه‌ای را فهرست می‌کند.

راه‌اندازی سرویس

برای راه‌اندازی سرویس پیش‌زمینه‌ای، ابتدا باید آن را به‌عنوان سرویس معمولی (غیرپیش‌زمینه‌ای) راه‌اندازی کنید:

کاتلین

val intent = Intent(...) // Build the intent for the service
context.startForegroundService(intent)

جاوا

Context context = getApplicationContext();
Intent intent = new Intent(...); // Build the intent for the service
context.startForegroundService(intent);

نکات کلیدی درباره کد

  • تکه‌کد سرویسی را راه‌اندازی می‌کند. اما سرویس هنوز در پیش‌زمینه اجرا نمی‌شود. در داخل خود سرویس، باید ServiceCompat.startForeground() را فراخوانی کنید تا سرویس را به سرویس پیش‌زمینه‌ای ارتقا دهید.

ارتقا دادن سرویس به پیش‌زمینه

وقتی سرویسی درحال اجرا است، باید ServiceCompat.startForeground() را فراخوانی کنید تا درخواست کنید سرویس در پیش‌زمینه اجرا شود. معمولاً این روش را در روش onStartCommand() سرویس فراخوانی می‌کنید.

ServiceCompat.startForeground() پارامترهای زیر را می‌گیرد:

انواع سرویس پیش‌زمینه‌ای که به startForeground() منتقل می‌کنید انواع اعلام‌شده در مانیفست، بسته به مورد استفاده خاص. سپس، اگر لازم بود انواع خدمات بیشتری اضافه کنید، می‌توانید دوباره startForeground() را فراخوانی کنید.

برای مثال، فرض کنید یک برنامه تناسب اندام سرویس ردیاب دویدن را اجرا می‌کند که همیشه به اطلاعات location نیاز دارد، اما ممکن است به پخش رسانه نیاز داشته باشد یا نداشته باشد. باید location و mediaPlayback را در مانیفست اعلام کنید. اگر کاربری دویدن را شروع کند و فقط بخواهد مکانش ردیابی شود، برنامه‌تان باید startForeground() را فراخوانی کند و فقط اجازه ACCESS_FINE_LOCATION را ارسال کند. سپس، اگر کاربر بخواهد پخش صدا را شروع کند، دوباره startForeground() را فراخوانی کنید و ترکیب بیتی همه انواع سرویس پیش‌زمینه‌ای را (در این مورد، ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK) ارسال کنید.

مثال زیر کدی را نشان می‌دهد که سرویس دوربین برای ارتقا دادن خود به سرویس پیش‌زمینه استفاده می‌کند:

کاتلین

class MyCameraService: Service() {

  private fun startForeground() {
    // Before starting the service as foreground check that the app has the
    // appropriate runtime permissions. In this case, verify that the user has
    // granted the CAMERA permission.
    val cameraPermission =
            PermissionChecker.checkSelfPermission(this, Manifest.permission.CAMERA)
    if (cameraPermission != PermissionChecker.PERMISSION_GRANTED) {
        // Without camera permissions the service cannot run in the foreground
        // Consider informing user or updating your app UI if visible.
        stopSelf()
        return
    }

    try {
        val notification = NotificationCompat.Builder(this, "CHANNEL_ID")
            // Create the notification to display while the service is running
            .build()
        ServiceCompat.startForeground(
            /* service = */ this,
            /* id = */ 100, // Cannot be 0
            /* notification = */ notification,
            /* foregroundServiceType = */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
            } else {
                0
            },
        )
    } catch (e: Exception) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                && e is ForegroundServiceStartNotAllowedException) {
            // App not in a valid state to start foreground service
            // (e.g. started from bg)
        }
        // ...
    }
  }
}

جاوا

public class MyCameraService extends Service {

    private void startForeground() {
        // Before starting the service as foreground check that the app has the
        // appropriate runtime permissions. In this case, verify that the user
        // has granted the CAMERA permission.
        int cameraPermission =
            ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        if (cameraPermission == PackageManager.PERMISSION_DENIED) {
            // Without camera permissions the service cannot run in the
            // foreground. Consider informing user or updating your app UI if
            // visible.
            stopSelf();
            return;
        }

        try {
            Notification notification =
                new NotificationCompat.Builder(this, "CHANNEL_ID")
                    // Create the notification to display while the service
                    // is running
                    .build();
            int type = 0;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
            }
            ServiceCompat.startForeground(
                    /* service = */ this,
                    /* id = */ 100, // Cannot be 0
                    /* notification = */ notification,
                    /* foregroundServiceType = */ type
            );
        } catch (Exception e) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
                    e instanceof ForegroundServiceStartNotAllowedException
            ) {
                // App not in a valid state to start foreground service
                // (e.g started from bg)
            }
            // ...
        }
    }

    //...
}

نکات کلیدی درباره کد

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