پخش رسانه در پس زمینه

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

برای انجام این کار، MediaPlayer را در یک سرویس MediaBrowserServiceCompat جاسازی می‌کنید و از آن می‌خواهید با یک MediaBrowserCompat در فعالیت دیگری تعامل داشته باشد.

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

این صفحه دستورالعمل‌های ویژه‌ای را برای مدیریت MediaPlayer هنگام پیاده‌سازی آن در یک سرویس توضیح می‌دهد.

به صورت ناهمزمان اجرا شود

مانند یک Activity ، تمام کارهای یک Service به طور پیش فرض در یک رشته انجام می شود. در واقع، هنگامی که یک اکتیویتی و یک سرویس را از یک برنامه اجرا می کنید، آنها به طور پیش فرض از همان رشته («رشته اصلی») استفاده می کنند.

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

به عنوان مثال، هنگامی که از MediaPlayer از رشته اصلی خود استفاده می کنید، باید:

به عنوان مثال:

کاتلین

private const val ACTION_PLAY: String = "com.example.action.PLAY"

class MyService: Service(), MediaPlayer.OnPreparedListener {

    private var mMediaPlayer: MediaPlayer? = null

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        ...
        val action: String = intent.action
        when(action) {
            ACTION_PLAY -> {
                mMediaPlayer = ... // initialize it here
                mMediaPlayer?.apply {
                    setOnPreparedListener(this@MyService)
                    prepareAsync() // prepare async to not block main thread
                }

            }
        }
        ...
    }

    /** Called when MediaPlayer is ready */
    override fun onPrepared(mediaPlayer: MediaPlayer) {
        mediaPlayer.start()
    }
}

جاوا

public class MyService extends Service implements MediaPlayer.OnPreparedListener {
    private static final String ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mediaPlayer = null;

    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        if (intent.getAction().equals(ACTION_PLAY)) {
            mediaPlayer = ... // initialize it here
            mediaPlayer.setOnPreparedListener(this);
            mediaPlayer.prepareAsync(); // prepare async to not block main thread
        }
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

رسیدگی به خطاهای ناهمزمان

در عملیات همزمان، خطاها با یک استثنا یا یک کد خطا علامت گذاری می شوند. با این حال، هنگامی که از منابع ناهمزمان استفاده می کنید، باید برنامه خود را از خطاها به طور مناسب مطلع کنید. در مورد MediaPlayer ، یک MediaPlayer.OnErrorListener پیاده سازی می کنید و آن را در نمونه MediaPlayer خود تنظیم می کنید:

کاتلین

class MyService : Service(), MediaPlayer.OnErrorListener {

    private var mediaPlayer: MediaPlayer? = null

    fun initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer?.setOnErrorListener(this)
    }

    override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

جاوا

public class MyService extends Service implements MediaPlayer.OnErrorListener {
    MediaPlayer mediaPlayer;

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer.setOnErrorListener(this);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

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

از ویک لاک استفاده کنید

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

Wake lock سیگنالی به سیستم است مبنی بر اینکه برنامه شما از ویژگی‌هایی استفاده می‌کند که باید حتی زمانی که تلفن بیکار است در دسترس بماند.

برای اطمینان از ادامه کار CPU در حالی که MediaPlayer شما در حال پخش است، هنگامی که MediaPlayer خود را مقداردهی می کنید، متد setWakeMode() را فراخوانی کنید. MediaPlayer قفل مشخص شده را در حین بازی نگه می دارد و در صورت توقف یا توقف، قفل را آزاد می کند:

کاتلین

mediaPlayer = MediaPlayer().apply {
    // ... other initialization here ...
    setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}

جاوا

mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

با این حال، wake lock به دست آمده در این مثال تنها بیدار ماندن CPU را تضمین می کند. اگر در حال پخش رسانه از طریق شبکه هستید و از Wi-Fi استفاده می کنید، احتمالاً می خواهید WifiLock را نیز نگه دارید، که باید آن را به صورت دستی خریداری و آزاد کنید. بنابراین، هنگامی که شروع به آماده سازی MediaPlayer با URL راه دور می کنید، باید قفل Wi-Fi را ایجاد و خریداری کنید.

به عنوان مثال:

کاتلین

val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
    wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")

wifiLock.acquire()

جاوا

WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

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

کاتلین

wifiLock.release()

جاوا

wifiLock.release();

پاکسازی را انجام دهید

همانطور که قبلاً ذکر شد، یک شی MediaPlayer می تواند مقدار قابل توجهی از منابع سیستم را مصرف کند، بنابراین شما باید آن را فقط تا زمانی که نیاز دارید نگه دارید و پس از اتمام کار با آن، release() را فراخوانی کنید. مهم است که این روش پاکسازی را صریحاً به جای تکیه بر جمع‌آوری زباله‌های سیستم فراخوانی کنید، زیرا ممکن است مدتی طول بکشد تا جمع‌آورنده زباله MediaPlayer را بازیابی کند، زیرا فقط به نیازهای حافظه حساس است و به کمبود منابع مرتبط با رسانه دیگر حساس نیست. بنابراین، هنگامی که از یک سرویس استفاده می کنید، همیشه باید متد onDestroy() را لغو کنید تا مطمئن شوید که MediaPlayer را آزاد می کنید:

کاتلین

class MyService : Service() {

    private var mediaPlayer: MediaPlayer? = null
    // ...

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer?.release()
    }
}

جاوا

public class MyService extends Service {
   MediaPlayer mediaPlayer;
   // ...

   @Override
   public void onDestroy() {
       super.onDestroy();
       if (mediaPlayer != null) mediaPlayer.release();
   }
}

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

بیشتر بدانید

Jetpack Media3 راه حل پیشنهادی برای پخش رسانه در برنامه شما است. در مورد آن بیشتر بخوانید .

این صفحات موضوعات مربوط به ضبط، ذخیره و پخش صدا و تصویر را پوشش می دهند: