تخفیف تابستانی سال ۱۳۹۹ تاپایان: دوشنبه ۱۶ تیر ۱۳۹۹
بزن بریم فروشگاه
ساخت موزیک پلیر در اندروید استودیو

ساخت موزیک پلیر در اندروید استودیو

: ۴۰۱۴ بار
نوشته شده : 5 ماه پیش

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

 

  1. بخش اول ساختن سرویس پخش موسیقی در اندروید ( این بخش برای پخش موزیک در پس زمینه بسیار مهم است )
  2. تعامل با سرویس ساخته شده با استفاده از BroadcastReceiver مثل PLAY, PAUSE, NEXT, PREVIOUS چگونگی کنترل کردن موارد حاشیه ای مثل زنگ خوردن گوشی موقع پخش ، تغییر خروجی پخش صدا مثل هدفون و ...

 

گام اول - ساختن پروژه در اندروید استودیو موزیک پلیر در اندروید استودیو

محیط Android Studio خودتان را باز کنید و یک پروژه ی جدید ایجاد کنید و در AndroidManifest.xml پروژه مجوز های زیر را اضافه کنید . 

 

<uses-permission android:name="android.permission.INTERNET" />
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

در کد های بالا ابتدا مجوز اینترنت به برنامه داده میشود تا اگر خواستیم موسیقی های انلاین را هم پخش کنیم و عمل استریم موزیک انلاین را هم انجام بدیم . 

برای کنترل کردن پخش مدیا به مجوز MEDIA_CONTENT_CONTROL نیاز است . 

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

 

گام دوم - ساختن MediaPlayer Service موزیک پلیر در اندروید استودیو

هسته ی اصلی اپلیکیشن های موزیک پلیر یک سرویس است ، media player service . 

کلاس زیر مثالی است از این سرویس . 

این کلاس چندنوع MediaPlayer برای کنترل رویداد ها در هنگام پخش موزیک دارد . همانطور که میبینید از کلاس های زیادی استفاده شده است ولی اخرین پیاده سازی از روی AudioManager.OnAudioFocusChangeListener است که به برای کنترل کردن درخواست های AudioFocus از دیگر اپلیکیشن ها که میخواهند فایل های مدیا را پخش کنند.

 

public class MediaPlayerService extends Service implements MediaPlayer.OnCompletionListener,
      MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnSeekCompleteListener,
      MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener,

      AudioManager.OnAudioFocusChangeListener {


  // Binder given to clients
  private final IBinder iBinder = new LocalBinder();

  @Override
  public IBinder onBind(Intent intent) {
      return iBinder;
  }

  @Override
  public void onBufferingUpdate(MediaPlayer mp, int percent) {
      //Invoked indicating buffering status of
      //a media resource being streamed over the network.
  }

  @Override
  public void onCompletion(MediaPlayer mp) {
      //Invoked when playback of a media source has completed.
  }

  //Handle errors
  @Override
  public boolean onError(MediaPlayer mp, int what, int extra) {
      //Invoked when there has been an error during an asynchronous operation.
      return false;
  }

  @Override
  public boolean onInfo(MediaPlayer mp, int what, int extra) {
      //Invoked to communicate some info.
      return false;
  }

  @Override
  public void onPrepared(MediaPlayer mp) {
      //Invoked when the media source is ready for playback.
  }

  @Override
  public void onSeekComplete(MediaPlayer mp) {
      //Invoked indicating the completion of a seek operation.
  }

  @Override
  public void onAudioFocusChange(int focusChange) {
      //Invoked when the audio focus of the system is updated.
  }

  public class LocalBinder extends Binder {
      public MediaPlayerService getService() {
          return MediaPlayerService.this;
      }
  }

}

نمونه کد بالا یک قالب از تمام متدهای MediaPlayer برای کنترل کردن رویدادها است .

میشه گفت کد بالا نیازمندی های کلی Service شماست . شما باید این Service را تعریف کنید تا با Activity شما در ارتباط باشد و فایل های audio را دریافت کند . 

شما باید Service را به فایل AndroidManifest.xml  معرفی کنید . طبق کد زیر این کار را انجام دهید . 

 

<application
  <service android:name=".MediaPlayerService" />
  ...
</application>

 

گام سوم - بنا نهادن MediaPlayer موزیک پلیر در اندروید استودیو

فریمورک مالتی مدیای اندروید از بسیار فرمت های رسانه ای پشتیبانی میکند . 

یکی از کامپوننت های مهم این بخش کلاس  MediaPlayer  است که با تنظیمات کم و انجام کارهای ساده میتوانید  فایل های صوتی و ویدیویی را پخش کنید .

در ادامه متد های لازم از کلاس MediaPlayerService را به شما آموزش می دهیم . 

دو تعریف زیر را انجام دهید که یکی از نوع MediaPlayer و یکی از نوع String است .

 

private MediaPlayer mediaPlayer;
//path to the audio file
private String mediaFile;

حالا mediaPlayer را طبق کد زیر پیاده سازی کنید . 

 

private void initMediaPlayer() {
    mediaPlayer = new MediaPlayer();
    //Set up MediaPlayer event listeners
    mediaPlayer.setOnCompletionListener(this);
    mediaPlayer.setOnErrorListener(this);
    mediaPlayer.setOnPreparedListener(this);
    mediaPlayer.setOnBufferingUpdateListener(this);
    mediaPlayer.setOnSeekCompleteListener(this);
    mediaPlayer.setOnInfoListener(this);
    //Reset so that the MediaPlayer is not pointing to another data source
    mediaPlayer.reset();

    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    try {
        // Set the data source to the mediaFile location
        mediaPlayer.setDataSource(mediaFile);
    } catch (IOException e) {
        e.printStackTrace();
        stopSelf();
    }
    mediaPlayer.prepareAsync();
}

زمانی که میخواهید با مدیا کار کنید باید یه سری متد ها را override کنید تا بتوانید مدیا را کنترل کنید . این متد ها چیزهایی مثل Play, Stop, Pause و Resume هستند.

ابتدا یک متغیر گلوبال برای عملیات pause/resume را مینویسیم.

 

//Used to pause/resume MediaPlayer
private int resumePosition;

و یک if نیاز است تا مطمئن شوید تا خطایی هنگام مدیا وجود ندارد . 

 

private void playMedia() {
    if (!mediaPlayer.isPlaying()) {
        mediaPlayer.start();
    }
}

private void stopMedia() {
    if (mediaPlayer == null) return;
    if (mediaPlayer.isPlaying()) {
        mediaPlayer.stop();
    }
}

private void pauseMedia() {
    if (mediaPlayer.isPlaying()) {
        mediaPlayer.pause();
        resumePosition = mediaPlayer.getCurrentPosition();
    }
}

private void resumeMedia() {
    if (!mediaPlayer.isPlaying()) {
        mediaPlayer.seekTo(resumePosition);
        mediaPlayer.start();
    }
}

اکنون شما متدهای ابتدایی مورد نیاز برای راه اندازی را نوشتید و وقت این است که متد های @Override را پیاده سازی کنیم تا قالب اولیه Service ساخته شود . 

این متد ها برای MediaPlayer مهم هستند چون زیرا تمام عملیات کلیدی پخش کننده موزیک از اینها استفاده خواهد کرد . 

کد درون Service را با کدهای زیر جایگزین کنید . 

 

@Override
public void onCompletion(MediaPlayer mp) {
    //Invoked when playback of a media source has completed.
    stopMedia();
    //stop the service
    stopSelf();
}

//Handle errors
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
    //Invoked when there has been an error during an asynchronous operation
    switch (what) {
        case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
            Log.d("MediaPlayer Error", "MEDIA ERROR NOT VALID FOR PROGRESSIVE PLAYBACK " + extra);
            break;
        case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
            Log.d("MediaPlayer Error", "MEDIA ERROR SERVER DIED " + extra);
            break;
        case MediaPlayer.MEDIA_ERROR_UNKNOWN:
            Log.d("MediaPlayer Error", "MEDIA ERROR UNKNOWN " + extra);
            break;
    }
    return false;
}

@Override
public void onPrepared(MediaPlayer mp) {
    //Invoked when the media source is ready for playback.
    playMedia();
}

 

گام چهارم - کنترل کردن Audio Focus موزیک پلیر در اندروید استودیو

برای اینکه یک موزیک پلیر دلچسب در اندروید استودیو بسازید باید به دیگر رسانه های پخش شده در سیستم اندروید یا دیگر اپ ها  هم توجه کنید .

برای اینکه مطمئن شوید چنین تجربه ی مثبتی ایجاد کنید MediaPlayerService رخداد های AudioFocus را کنترل میکند و توسط متد onAudioFocusChange() قابل انجام و مدیریت است . 

این متد مثل یک switch است و رخداد های focus مثل case های مختلف است . 

توجه کنید که این متد بعد از رخداد AudioFocus اجرا میشود که توسط سیستم یا دیگر اپ ها اتفاق میوفتد . 

برای حالت های مختلف case: چه اتفاق هایی میوفتد ؟ 

  • AudioManager.AUDIOFOCUS_GAIN : سرویس audio focus را بدست اورده است پس نیاز است که موسیقی را شروع کنی . 
  • AudioManager.AUDIOFOCUS_LOSS :‌ سرویس audio focus را از دست داد . شاید کاربر به مدیا پلایر دیگری رجوع کرده و آنرا پخش کرده است پس این مدیا را قطع کن . 
  • AudioManager.AUDIOFOCUS_LOSS_TRANSIENT : از دست دادن Focus به مدت خیلی کوتاه و در نهایت نگه داشتن MediaPlayer
  • AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK : از دست دادن Focus به مدت خیلی کوتاه . ممکن است یک چیزی مثل نوتیفیکشن در دستگاه دریافت شده است و در نهایت صدای MediaPlayer در حال پخش را کاهش میدهد . 

شما به override دو متد دیگر نیز نیاز دارید تا focus صدای MediaPlayer را درخواست یا آزاد کنید . در زیر نمونه کدهایی را میبینید که تمامی چیزهای مربوط به focus که بالا توضیح دادیم را نشان میدهد . از متد onAudioFocusChange() استفاده شده است . شما میتوانید درباره ی این متد در مستندات توسعه دهندگان اندروید بصورت کامل از اینجا بخوانید . 

در ابتدا برای Service یک متغیر گلوبال تعریف میکنیم .

 

private AudioManager audioManager;

متد onAudioFocusChange() را با کدهای زیر جایگزین کنید . 

 

@Override
public void onAudioFocusChange(int focusState) {
    //Invoked when the audio focus of the system is updated.
    switch (focusState) {
        case AudioManager.AUDIOFOCUS_GAIN:
            // resume playback
            if (mediaPlayer == null) initMediaPlayer();
            else if (!mediaPlayer.isPlaying()) mediaPlayer.start();
            mediaPlayer.setVolume(1.0f, 1.0f);
            break;
        case AudioManager.AUDIOFOCUS_LOSS:
            // Lost focus for an unbounded amount of time: stop playback and release media player
            if (mediaPlayer.isPlaying()) mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            // Lost focus for a short time, but we have to stop
            // playback. We don't release the media player because playback
            // is likely to resume
            if (mediaPlayer.isPlaying()) mediaPlayer.pause();
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
            // Lost focus for a short time, but it's ok to keep playing
            // at an attenuated level
            if (mediaPlayer.isPlaying()) mediaPlayer.setVolume(0.1f, 0.1f);
            break;
    }
}

private boolean requestAudioFocus() {
    audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
    if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        //Focus gained
        return true;
    }
    //Could not gain focus
    return false;
}

private boolean removeAudioFocus() {
    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
            audioManager.abandonAudioFocus(this);
}

 

گام پنجم - چرخه ی حیات متدهای Service موزیک پلیر در اندروید استودیو

در این بخش روی چرخه ی حیات Service تمرکز میکنیم . این متد ها برای MediaPlayer بسیار مهم هستند چون چرخه ی حیات Service ارتباط نزدیکی با MediaPlayer دارد . 

این متد ها پیاده سازی اولیه و مدیریت منابع MediaPlayer را انجام میدهند . 

برای درک بهتر لابلای کدهای زیر کامنت گذاشته شده است 

 

//The system calls this method when an activity, requests the service be started
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    try {
        //An audio file is passed to the service through putExtra();
        mediaFile = intent.getExtras().getString("media");
    } catch (NullPointerException e) {
        stopSelf();
    }

    //Request audio focus
    if (requestAudioFocus() == false) {
        //Could not gain focus
        stopSelf();
    }

    if (mediaFile != null && mediaFile != "")
        initMediaPlayer();

    return super.onStartCommand(intent, flags, startId);
}

متد onStartCommand() عمل پیاده سازی MediaPlayer را انجام میدهد و همچنین پیاده سازی اولیه focus request را انجام میدهد تا مطمئن شود در اندروید چیزی از قبل پخش نمیشود که صدا رو صدا نیوفته 

در متد onStartCommand()  یک بلاک try-catch اضافه شده است تا مطمئن شویم که توسط متد getExtras()  خطای NullPointerException اتفاق نمیوفته .

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

یکی از متد های مهم دیگر که باید پیاده سازی کنید متد onDestroy() است . در این متد باید منبع MediaPlayer آزاد گردد و Service باید نابود شود 

 

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

متد onDestroy() همچنین audio focus را آزاد میکند این یک انتخاب شخصی است و اگر از این روش استفاده کنید MediaPlayerService تا زمانی که نابود نشده audio focus را خواهد داشت ، البته اگر مزاحمتی توسط دیگر برنامه ها برای focus نباشد . 

اگر شما کنترل focus پویاتری میخواهید شما میتوانید هنگام شروع پخش موزیک audio focus را در متد onCompletion() درخواست کنید . 

 

گام ششم - بایند کردن Audio Player

در این بخش آخرین قدم ها برای bind کردن MediaPlayerService در MainActivity و آماده کردن آن برای پخش موزیک را پوشش خواهم داد . 

شما باید Service را به activity بایند کنید بنابراین این دو میتوانند با هم دیگر در ارتباط باشند . 

متغیرهای گلوبال زیر را به MainActivity اضافه کنید .

 

private MediaPlayerService player;
boolean serviceBound = false;

اولی یک instance از Service است و دومی وضعیت service را مشخص میکند که مشخص شود برای اکتیویتی bind شده است یا نه ! 

برای کنترل کردن bind شدن Service کدهای زیر را به MainActivity اضافه کنید .

 

//Binding this Client to the AudioPlayer Service
private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // We've bound to LocalService, cast the IBinder and get LocalService instance
        MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
        player = binder.getService();
        serviceBound = true;

        Toast.makeText(MainActivity.this, "Service Bound", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        serviceBound = false;
    }
};

خب الان وقت این است که چند موزیک پخش کنیم . تابع زیر یک instance از MediaPlayerService میسازد و یک فایل صوتی را برای پخش شدن میفرستد . خب پس کدهای زیر را هم به MainActivity خود اضافه کنید . 

 

private void playAudio(String media) {
    //Check is service is active
    if (!serviceBound) {
        Intent playerIntent = new Intent(this, MediaPlayerService.class);
        playerIntent.putExtra("media", media);
        startService(playerIntent);
        bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    } else {
        //Service is active
        //Send media with BroadcastReceiver
    }
}

تابع playAudio() کامل نیست 

من بعدا به این متد دوباره باز خواهم گشت زمانی که فایل صوتی به Service توسط BroadcastReceiver ارسال شود . 

در متد onCreate() درون اکتیویتی تابع playAudio() را صدا بزنید و منبع صوتی را به آن معرفی کنید . 

 

playAudio("https://upload.wikimedia.org/wikipedia/commons/6/6c/Grieg_Lyric_Pieces_Kobold.ogg");

 

گام هفتم - متدهای چرخه ی حیات اکتیویتی

در این بخش قصد داریم متد های چرخه ی حیات یا life cycle اکتیویتی MainActivity را بررسی کنیم . اگر تابع playAudio() را درون متد onCreate اکتیویتی  صدا زده اید Service شروع به پخش موزیک میکند ولی برنامه به سادگی کرش میکند . 

برای حل این مشکل متد زیر را به MainActivity اضافه کنید . تمام این متدها وضعیت serviceBound را ذخیره و بازیابی میکنند و service را زمانی که اپ بسته میشود unbind میکند . 

 

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    savedInstanceState.putBoolean("ServiceState", serviceBound);
    super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    serviceBound = savedInstanceState.getBoolean("ServiceState");
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (serviceBound) {
        unbindService(serviceConnection);
        //service is active
        player.stopSelf();
    }
}

 

موارد اضافی - فراخوانی و پخش موزیک از حافظه ی لوکال 

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

یک کلاس جدید جاوایی بسازید و بعنوان یک آبجکت audio در نظر بگیرید . 

در کد زیر شما موارد مورد نیاز یک فایل موزیک را مشاهده میکنید شما میتوانید خصوصیات بیشتری را خودتان اضافه کنید . 

 

public class Audio implements Serializable {

    private String data;
    private String title;
    private String album;
    private String artist;

    public Audio(String data, String title, String album, String artist) {
        this.data = data;
        this.title = title;
        this.album = album;
        this.artist = artist;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }
}

 

مجوز زیر را به فایل AndroidManifest.xml اضافه کنید . 

 

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

این مجوز برای خواندن فایل ها از حافظه ی دستگاه اندرویدی نیاز است . 

در کلاس MainActivity یک متغیر گلوبال از نوع ArrayList بسازید که وظیفه اش نگهداری آبجکت های از نوع Audio است .

 

ArrayList<Audio> audioList;

برای دریافت دیتا از دستگاه تابع زیر را به MainActivity اضافه کنید . این تابع اطلاعات را از دستگاه به ترتیب صعودی بازیابی می کند 

 

private void loadAudio() {
  ContentResolver contentResolver = getContentResolver();

  Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
  String selection = MediaStore.Audio.Media.IS_MUSIC + "!= 0";
  String sortOrder = MediaStore.Audio.Media.TITLE + " ASC";
  Cursor cursor = contentResolver.query(uri, null, selection, null, sortOrder);

  if (cursor != null && cursor.getCount() > 0) {
      audioList = new ArrayList<>();
      while (cursor.moveToNext()) {
          String data = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
          String title = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
          String album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
          String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));

          // Save to audioList
          audioList.add(new Audio(data, title, album, artist));
      }
  }
  cursor.close();
}

بعد از اینکه داده ها از دستگاه فراخوانی میشود تابع playAudio() میتواند آنها را در Service پخش کند . 

در تابع onCreate() اکتیویتی MainActivity کد های زیر را اضافه کنید . 

مطمئن باشید که حداقل یک فایل صوتی را دارید که Service بتواند پخش کند در غیر اینصورت برنامه ممکن است کرش کند . 

loadAudio();
//play the first audio in the ArrayList
playAudio(audioList.get(0).getData());

 

استراحت !‌ تا اینجا کار خوب پیش اومدید ولی اگه بدون وقفه مقاله را خوانده یا تمرین کرده باشید حتما خسته شدید . اگر دوست داشتید یک استراحت کوتاه کنید و دوباره برگردید و ادامه ی مقاله را بخوانید . 

در ادامه من قصد دارم روی ارتباط کاربر با  MediaPlayerService تمرکز کنم و اتفاقات را کنترل کنیم . اتفاقاتی مثل زنگ زدن موبایل هنگام پخش موزیک ، تغییر  خروجی صوتی دستگاه یا هر چیز دیگری که برای موزیک پلیر کامل نیاز است . 

 کلید اصلی این قسمت کار کردن با BroadcastReceiver است .

 

BroadcastReceiver چیست ؟

کامپوننت ها و اپ های اندروید ارتباطات گسترده ای با دیگر اپ ها و یا خود سیستم اندروید با استفاده از متد های sendBroadcast() ، sendStickyBroadcast() و sendOrderedBroadcast() برقرار میکنند . 

intent های Broadcast برای ساختن یک سیستم event  و ارسال پیام از طرف کامپوننت های اپ یا خود سیستم اندرویدی به سمت اپلیکیشینی که منتظر یک اتفاق است و اتفاقات کلیدی سیستم به این شکل به تمام اپ ها اعلام میشود و آنها دریافت میکنند . 

وظیفه ی اصلی BroadcastReceiver گوش به زنگ بودن برای اتفاق خاصی است و اگر اون اتفاق خاص رخ داد باید BroadcastReceiver کاری که سپرده شده است را انجام دهد . 

زمانی که اتفاق متناظر با چیزی که منتظر است رخ میدهد متد onReceive() اجرا میشود . 

شما میتوانید به دو روش یک BroadcastReceiver را رجیستر کنید : بصورت استاتیک در فایل AndroidManifest.xml یا با صدا زدن تابع registerReceiver() در هر جایی .

برای این آموزش ما بصورت پویا BroadcastReciver را تعریف کردیم تا زمانی که MediaPlayerService قصد دارد به اتفاقات سیستم گوش دهد . 

 

برگردیم سر موزیک پلیر در اندروید استودیو

برای ساخت یک اپ موزیک پلیر خوب در اندروید استودیو من از RecyclerView استفاده میکنم و با کمک تابع loadAudio() فایل های صوتی موزیک موجود در دستگاه را در RecyclerView نمایش میدهیم . همچنین تغییرات گرافیکی مثل رنگ و ... روی این عنصر انجام خواهیم داد . در این آموزش قصد نداریم به صورت جز به جز به بحث RecyclerView بپردازیم . اگر RecyclerView را کار نکردید از کارگاه آموزشی کار با ریسایکلر ویو در زبان کاتلین استفاده کنید تا به این مورد مسلط شوید . 

 

تغییر خروجی صدای دستگاه (headphone removed)

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

در کلاس MediaPlayerService یک BroadcastReceiver بسازید که به رویداد ACTION_AUDIO_BECOMING_NOISY گوش میدهد . تابع زیر را به کلاس service اضافه کنید . 

//Becoming noisy
private BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        //pause audio on ACTION_AUDIO_BECOMING_NOISY
        pauseMedia();
        buildNotification(PlaybackStatus.PAUSED);
    }
};

private void registerBecomingNoisyReceiver() {
    //register after getting audio focus
    IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
    registerReceiver(becomingNoisyReceiver, intentFilter);
}

 

BroadcastReceiver گوش به فرمان باقی میماند و زمانی که سیستم پیام مربوط به ACTION_AUDIO_BECOMING_NOISY را منتشر میکند متوجه میشود و MediaPlayer را متوقف میکند . 

برای اینکه BroadcastReceiver را بتوانید استفاده کنید باید آنرا رجیستر کنید . 

تابع registerBecomingNoisyReceiver() این کار را انجام میدهد .

هنوز نیازی نیست تابع buildNotification() را پیاده سازی کنیم پس نگران خطاهایی که نمایش داده میشود نباشید . 

 

کنترل تماس های ورودی

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

ابتدا در کلاس MediaPlayerService متغیرهای گلوبال زیر را تعریف کنید .

//Handle incoming phone calls
private boolean ongoingCall = false;
private PhoneStateListener phoneStateListener;
private TelephonyManager telephonyManager;

 

و تابع زیر را اضافه کنید 

//Handle incoming phone calls
private void callStateListener() {
  // Get the telephony manager
  telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
  //Starting listening for PhoneState changes
  phoneStateListener = new PhoneStateListener() {
      @Override
      public void onCallStateChanged(int state, String incomingNumber) {
          switch (state) {
              //if at least one call exists or the phone is ringing
              //pause the MediaPlayer
              case TelephonyManager.CALL_STATE_OFFHOOK:
              case TelephonyManager.CALL_STATE_RINGING:
                  if (mediaPlayer != null) {
                      pauseMedia();
                      ongoingCall = true;
                  }
                  break;
              case TelephonyManager.CALL_STATE_IDLE:
                  // Phone idle. Start playing.
                  if (mediaPlayer != null) {
                      if (ongoingCall) {
                          ongoingCall = false;
                          resumeMedia();
                      }
                  }
                  break;
          }
      }
  };
  // Register the listener with the telephony manager
  // Listen for changes to the device call state.
  telephonyManager.listen(phoneStateListener,
          PhoneStateListener.LISTEN_CALL_STATE);
}

تابع callStateListener() یک پیاده سازی از PhoneStateListener است که به تغییرات TelephonyManager گوش میدهد . TelephonyManager دسترسی به اطلاعات سرویس telephony را فراهم میکند و منتظر میماند تا اگر اتفاقی مثل تماس افتاد عکس العمل نشان دهد . 

 

تعریف مجدد متد ها 

من در این مقاله اشاره کردم که تغییراتی در متد ها د اده ام . همچنین روش واگزاری فایل های صوتی به Service را نیز تغییر دادم .

فایل های صوتی توسط تابع loadAudio() از حافظه ی دستگاه فراخوانی میشود وقتی کاربر میخواهد موزیک پخش شود تابع playAudio(int audioIndex) اجرا میشود - ورودی این تابع شماره index فایلی است که در ArrayList قرار دارد . 

وقتی برای اولین بار تابع playAudio() فراخوانی میشود ، ArrayList در حافظه ی SharedPreferences ذخیره میشود که شماره ی index فایل های صوتی ذخیره میشود که برای پخش موسیقی توسط MediaPlayerService از حافظه ی SharedPreferences به کار میرود . 

این یکی از راه های فراخوانی آرایه ی Audio  به سرویس است ولی روش های دیگری هم هست .

اکنون فایل build.gradle (app) را باز کنید و وابستگی های مورد نیاز برای کتابخانه ی GSON را اضافه کنید .

 

dependencies {
  ...
  compile group: 'com.google.code.gson', name: 'gson', version: '2.7', changing: true
}

 

کلاس زیر ذخیره سازی داده ها را کنترل می کند.

 

public class StorageUtil {

    private final String STORAGE = " com.valdioveliu.valdio.audioplayer.STORAGE";
    private SharedPreferences preferences;
    private Context context;

    public StorageUtil(Context context) {
        this.context = context;
    }

    public void storeAudio(ArrayList<Audio> arrayList) {
        preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);

        SharedPreferences.Editor editor = preferences.edit();
        Gson gson = new Gson();
        String json = gson.toJson(arrayList);
        editor.putString("audioArrayList", json);
        editor.apply();
    }

    public ArrayList<Audio> loadAudio() {
        preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
        Gson gson = new Gson();
        String json = preferences.getString("audioArrayList", null);
        Type type = new TypeToken<ArrayList<Audio>>() {
        }.getType();
        return gson.fromJson(json, type);
    }

    public void storeAudioIndex(int index) {
        preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();
        editor.putInt("audioIndex", index);
        editor.apply();
    }

    public int loadAudioIndex() {
        preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
        return preferences.getInt("audioIndex", -1);//return -1 if no data found
    }

    public void clearCachedAudioPlaylist() {
        preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();
        editor.clear();
        editor.commit();
    }
}

 

خب الان وقت این رسیده که تابع playAudio() را تغییر دهیم . ابتدا در MainActivity یک متغیر گلوبال و استاتیک از نوع string بسازید . 

 

public static final String Broadcast_PLAY_NEW_AUDIO = "ir.avasam.musicplayer";
// یادتون نره نام پکیج را در بالا اصلاح کنید و همنام با پروژه ی خود بکنید 

 

این متغیر string قرار است که intent های broadcast را به MediaPlayerService بفرستد برای اینکه کاربر میخواهد فایل صوتی جدیدی را پخش کند و کش index فایل های صوتی که قرار است پخش شود ، بروزرسانی میشود .

BroadcastReceiver که این intent را مدیریت میکند هنوز ساخته نشده است خب حالا تابع playAudio() که در MainActivity است بروزرسانی کنید و کد زیر را جایگزین کد قدیمی بکنید 

private void playAudio(int audioIndex) {
    //Check is service is active
    if (!serviceBound) {
        //Store Serializable audioList to SharedPreferences
        StorageUtil storage = new StorageUtil(getApplicationContext());
        storage.storeAudio(audioList);
        storage.storeAudioIndex(audioIndex);

        Intent playerIntent = new Intent(this, MediaPlayerService.class);
        startService(playerIntent);
        bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    } else {
        //Store the new audioIndex to SharedPreferences
        StorageUtil storage = new StorageUtil(getApplicationContext());
        storage.storeAudioIndex(audioIndex);

        //Service is active
        //Send a broadcast to the service -> PLAY_NEW_AUDIO
        Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
        sendBroadcast(broadcastIntent);
    }
}

 

فایل صوتی به Service توسط putExtra() ارسال نشده است ، بنابراین service باید اطلاعات را از SharedPreferences بخواند و به همین خاطر است که تابع onStartCommand() بازنویسی شود . 

در آخر این مقاله ی آموزشی دوباره به تابع onStartCommand() باز خواهیم گشت تا پیاده سازی آنرا کامل کنیم .

حالا متغیرهای گلوبایل زیر را به MediaPlayerService اضافه کنید . 

//List of available Audio files
private ArrayList<Audio> audioList;
private int audioIndex = -1;
private Audio activeAudio; //an object of the currently playing audio

 

پخش موزیک جدید با اعلام Broadcast

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

برای این کار باید راهی پیاده سازی شود تا Service گوش به زنگ باشد برای تغییر موزیک ! 

برای این کار یک BroadcastReceiver دیگر نیاز داریم 

در MediaPlayerService توابع زیر را اضافه کنید 

private BroadcastReceiver playNewAudio = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {

        //Get the new media index form SharedPreferences
        audioIndex = new StorageUtil(getApplicationContext()).loadAudioIndex();
        if (audioIndex != -1 && audioIndex < audioList.size()) {
            //index is in a valid range
            activeAudio = audioList.get(audioIndex);
        } else {
            stopSelf();
        }

        //A PLAY_NEW_AUDIO action received
        //reset mediaPlayer to play the new Audio
        stopMedia();
        mediaPlayer.reset();
        initMediaPlayer();
        updateMetaData();
        buildNotification(PlaybackStatus.PLAYING);
    }
};

private void register_playNewAudio() {
    //Register playNewMedia receiver
    IntentFilter filter = new IntentFilter(MainActivity.Broadcast_PLAY_NEW_AUDIO);
    registerReceiver(playNewAudio, filter);
}

موقع رهگیری PLAY_NEW_AUDIO ، این BroadcastReceiver بروزرسانی index و موزیک جدید را در activeAudio قرار میدهد و MediaPlayer ریست میشود و شروع میکند به پخش موزیک جدید . تابع buildNotification() هنوز ساخته نشده پس خطا میدهد .

 

رجیستر کردن BroadcastReceiver ها

در تابع onCreate مربوط به service رجیسری کردن BroadcastReceiver را انجام دهید . 

@Override
public void onCreate() {
    super.onCreate();
    // Perform one-time setup procedures

    // Manage incoming phone calls during playback.
    // Pause MediaPlayer on incoming call,
    // Resume on hangup.
    callStateListener();
    //ACTION_AUDIO_BECOMING_NOISY -- change in audio outputs -- BroadcastReceiver
    registerBecomingNoisyReceiver();
    //Listen for new Audio to play -- BroadcastReceiver
    register_playNewAudio();
}

وقتی که نیازی به BroadcastReceiver ها نیست باید آنها را از حالت رجیستری در بیاورید ، این اتفاق در متد onDestroy() مربوط به service انجام میگیرد . متد onDestroy() هم اکنون خود را با کدهای زیر جایگزین کنید . بازم بگم که نگران removeNotification() نباشید ، آنرا بعدا پیاده خواهیم کرد . 

@Override
public void onDestroy() {
    super.onDestroy();
    if (mediaPlayer != null) {
        stopMedia();
        mediaPlayer.release();
    }
    removeAudioFocus();
    //Disable the PhoneStateListener
    if (phoneStateListener != null) {
        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
    }

    removeNotification();

    //unregister BroadcastReceivers
    unregisterReceiver(becomingNoisyReceiver);
    unregisterReceiver(playNewAudio);

    //clear cached playlist
    new StorageUtil(getApplicationContext()).clearCachedAudioPlaylist();
}

زمانی که Service نابود میشود باید گوش به زنگ بودن برای تماس تلفنی متوقف شود و منابع TelephonyManager را آزاد کند . 

یکی از کارهای دیگری که Service موقع نابود شدن انجام میدهد این است که داده های ذخیره شده در SharedPreferences را پاکسازی کند .

 

تعامل با کاربر 

ارتباط برقرار کردن با MediaPlayerService یکی از ابزارهای کلیدی اپ موزیک پلیر است زیرا کاربر صرفا فقط نیاز به پخش موزیک ندارد بلکه باید بتواند کنترلی روی اپ داشته باشد .

کار با اپلیکیشن سخت به نظر میرسد چون servic که در پشت زمینه موزیک پخش میکند رابط کاربری ندارد

نسخه ی Lollipop اندروید ابزار جدیدی معرفی کرده است که MediaStyle notifications نام دارد .

Notification.MediaStyle به شما اجازه ی ساختن دکمه های مدیا بدون نیاز به ساختن نوتیفیکشن های اختصاصی را میدهد . در این مثال ما از کتابخانه ی MediaStyles و NotificationCompat.MediaStyle  برای ساپورت کردن نسخه های قدیمی اندروید استفاده خواهیم کرد . 

برای داشتن دسترسی کامل روی موزیک در حال پخش در MediaPlayerService باید یک instance از روی MediaSession بسازیم . MediaSession اجازه ی تعامل با کنترلرهای مدیا ، کلید های ولوم ، دکمه های مدیا و کنترل انتقال داده ها را میدهد . یک اپ یک instance از نوع MediaSession زمانی که میخواهد اطلاعات پخش یا کنترل کلید های مدیا را داشته باشد .

برای ساختن نوتیفیکشن MediaStyle برای این مثال ، MediaPlayerService از MediaSession استفاده خواهد کرد  تا به نوتیفیکشن کنترل کننده ها را اضافه کند و MetaData را منتشر کند بنابراین اندروید متوجه میشود که موزیک در حال پخش شدن است 

قبل از انجام ، متغیرهای زیر را در کلاس MediaPlayerService اضافه کنید.

public static final String ACTION_PLAY = "com.valdioveliu.valdio.audioplayer.ACTION_PLAY";
public static final String ACTION_PAUSE = "com.valdioveliu.valdio.audioplayer.ACTION_PAUSE";
public static final String ACTION_PREVIOUS = "com.valdioveliu.valdio.audioplayer.ACTION_PREVIOUS";
public static final String ACTION_NEXT = "com.valdioveliu.valdio.audioplayer.ACTION_NEXT";
public static final String ACTION_STOP = "com.valdioveliu.valdio.audioplayer.ACTION_STOP";

//MediaSession
private MediaSessionManager mediaSessionManager;
private MediaSessionCompat mediaSession;
private MediaControllerCompat.TransportControls transportControls;

//AudioPlayer notification ID
private static final int NOTIFICATION_ID = 101;

متغیرهای نوع String نشان میدهد که کدام اکشن از MediaSession صادر شده است . 

بقیه موارد به MediaSession و شناسه اطلاع رسانی مربوط می شوند تا به طور منحصر به فرد اعلان MediaStyle را شناسایی کنند.

توابع زیر دسته بندی اولیه MediaSession و تنظیم MetaData در یک سشن مربوط به اکشن است . 

بخش مهمی از تابع initMediaSession() زیر تنظیم پاسخ های MediaSession برای رسیدگی به حوادث ناشی از دکمه های اعلان است.

توابع زیر را در کلاس MediaPlayerService اضافه کنید.

private void initMediaSession() throws RemoteException {
  if (mediaSessionManager != null) return; //mediaSessionManager exists

  mediaSessionManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
  // Create a new MediaSession
  mediaSession = new MediaSessionCompat(getApplicationContext(), "AudioPlayer");
  //Get MediaSessions transport controls
  transportControls = mediaSession.getController().getTransportControls();
  //set MediaSession -> ready to receive media commands
  mediaSession.setActive(true);
  //indicate that the MediaSession handles transport control commands
  // through its MediaSessionCompat.Callback.
  mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

  //Set mediaSession's MetaData
  updateMetaData();

  // Attach Callback to receive MediaSession updates
  mediaSession.setCallback(new MediaSessionCompat.Callback() {
      // Implement callbacks
      @Override
      public void onPlay() {
          super.onPlay();
          resumeMedia();
          buildNotification(PlaybackStatus.PLAYING);
      }

      @Override
      public void onPause() {
          super.onPause();
          pauseMedia();
          buildNotification(PlaybackStatus.PAUSED);
      }

      @Override
      public void onSkipToNext() {
          super.onSkipToNext();
          skipToNext();
          updateMetaData();
          buildNotification(PlaybackStatus.PLAYING);
      }

      @Override
      public void onSkipToPrevious() {
          super.onSkipToPrevious();
          skipToPrevious();
          updateMetaData();
          buildNotification(PlaybackStatus.PLAYING);
      }

      @Override
      public void onStop() {
          super.onStop();
          removeNotification();
          //Stop the service
          stopSelf();
      }

      @Override
      public void onSeekTo(long position) {
          super.onSeekTo(position);
      }
  });
}

private void updateMetaData() {
  Bitmap albumArt = BitmapFactory.decodeResource(getResources(),
          R.drawable.image); //replace with medias albumArt
  // Update the current metadata
  mediaSession.setMetadata(new MediaMetadataCompat.Builder()
          .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt)
          .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, activeAudio.getArtist())
          .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, activeAudio.getAlbum())
          .putString(MediaMetadataCompat.METADATA_KEY_TITLE, activeAudio.getTitle())
          .build());
}

 

تابع updateMetaData() یک تصویر Bitmap دارد و شما باید آنرا بسازید پس یک تصویر به پوشه ی drawable اضافه کنید . 

توابع  override شده ی Callback() از توابع اصلی پخش کننده رسانه استفاده میکند که قبلا آموزش داده ایم . 

سپس توابع مدیا پلایر که قبلا توضیح دادیم به service اضافه کنید . 

private void skipToNext() {

    if (audioIndex == audioList.size() - 1) {
        //if last in playlist
        audioIndex = 0;
        activeAudio = audioList.get(audioIndex);
    } else {
        //get next in playlist
        activeAudio = audioList.get(++audioIndex);
    }

    //Update stored index
    new StorageUtil(getApplicationContext()).storeAudioIndex(audioIndex);

    stopMedia();
    //reset mediaPlayer
    mediaPlayer.reset();
    initMediaPlayer();
}

private void skipToPrevious() {

    if (audioIndex == 0) {
        //if first in playlist
        //set index to the last of audioList
        audioIndex = audioList.size() - 1;
        activeAudio = audioList.get(audioIndex);
    } else {
        //get previous in playlist
        activeAudio = audioList.get(--audioIndex);
    }

    //Update stored index
    new StorageUtil(getApplicationContext()).storeAudioIndex(audioIndex);

    stopMedia();
    //reset mediaPlayer
    mediaPlayer.reset();
    initMediaPlayer();
}

حالا service یک روشی نیاز دارد تا بتواند نوتیفیکشن MediaStyle را بسازد اما service به روشی نیاز دارد که وضعیت پخش را بفهمد به همین مظور یک شمارشگر جدید باید بسازید . 

در پروژه ی خود کلاس های زیر را بسازید .

public enum PlaybackStatus {
    PLAYING,
    PAUSED
}

اکنون این service راهی برای پیگیری وضعیت پخش خود ، عملکرد زیر را برای ساخت نوتیفیکیشن ها اضافه کرده است.

private void buildNotification(PlaybackStatus playbackStatus) {

    int notificationAction = android.R.drawable.ic_media_pause;//needs to be initialized
    PendingIntent play_pauseAction = null;

    //Build a new notification according to the current state of the MediaPlayer
    if (playbackStatus == PlaybackStatus.PLAYING) {
        notificationAction = android.R.drawable.ic_media_pause;
        //create the pause action
        play_pauseAction = playbackAction(1);
    } else if (playbackStatus == PlaybackStatus.PAUSED) {
        notificationAction = android.R.drawable.ic_media_play;
        //create the play action
        play_pauseAction = playbackAction(0);
    }

    Bitmap largeIcon = BitmapFactory.decodeResource(getResources(),
            R.drawable.image); //replace with your own image

    // Create a new Notification
    NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
            .setShowWhen(false)
            // Set the Notification style
            .setStyle(new NotificationCompat.MediaStyle()
                    // Attach our MediaSession token
                    .setMediaSession(mediaSession.getSessionToken())
                    // Show our playback controls in the compact notification view.
                    .setShowActionsInCompactView(0, 1, 2))
            // Set the Notification color
            .setColor(getResources().getColor(R.color.colorPrimary))
            // Set the large and small icons
            .setLargeIcon(largeIcon)
            .setSmallIcon(android.R.drawable.stat_sys_headset)
            // Set Notification content information
            .setContentText(activeAudio.getArtist())
            .setContentTitle(activeAudio.getAlbum())
            .setContentInfo(activeAudio.getTitle())
            // Add playback actions
            .addAction(android.R.drawable.ic_media_previous, "previous", playbackAction(3))
            .addAction(notificationAction, "pause", play_pauseAction)
            .addAction(android.R.drawable.ic_media_next, "next", playbackAction(2));

    ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, notificationBuilder.build());
}

private void removeNotification() {
    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.cancel(NOTIFICATION_ID);
}

زمانی که این تابع صدا زده میشود یک نوتیفیکشن مطابق با PlaybackStatus ساخته میشود . 

هدف اصلی buildNotification() اینست که رابط کاربری نوتیفیکشن را بسازد و همچنین تمام عملیات هایی که کاربر با کلیک روی دکمه های روی نوتیفیکشن انتظار دارد . 

شما عملیات های متناسب با PendingIntent را از تابع playbackAction() میسازید . 

آنرا به MediaPlayerService اضافه کنید .

private PendingIntent playbackAction(int actionNumber) {
  Intent playbackAction = new Intent(this, MediaPlayerService.class);
  switch (actionNumber) {
      case 0:
          // Play
          playbackAction.setAction(ACTION_PLAY);
          return PendingIntent.getService(this, actionNumber, playbackAction, 0);
      case 1:
          // Pause
          playbackAction.setAction(ACTION_PAUSE);
          return PendingIntent.getService(this, actionNumber, playbackAction, 0);
      case 2:
          // Next track
          playbackAction.setAction(ACTION_NEXT);
          return PendingIntent.getService(this, actionNumber, playbackAction, 0);
      case 3:
          // Previous track
          playbackAction.setAction(ACTION_PREVIOUS);
          return PendingIntent.getService(this, actionNumber, playbackAction, 0);
      default:
          break;
  }
  return null;
}

زمانی که کاربر روی دکمه های روی نوتیفیکشن کلیک میکند باید بتوانید آن عملیات را مدیریت کنید . برای این کار کدهای زیر را به service اضافه کنید . 

private void handleIncomingActions(Intent playbackAction) {
  if (playbackAction == null || playbackAction.getAction() == null) return;

  String actionString = playbackAction.getAction();
  if (actionString.equalsIgnoreCase(ACTION_PLAY)) {
      transportControls.play();
  } else if (actionString.equalsIgnoreCase(ACTION_PAUSE)) {
      transportControls.pause();
  } else if (actionString.equalsIgnoreCase(ACTION_NEXT)) {
      transportControls.skipToNext();
  } else if (actionString.equalsIgnoreCase(ACTION_PREVIOUS)) {
      transportControls.skipToPrevious();
  } else if (actionString.equalsIgnoreCase(ACTION_STOP)) {
      transportControls.stop();
  }
}

تابع متوجه میشود که کدام یک از تابع ها درخواست شده است و یکی از توابع  MediaSession را اجرا میکند . 

متد های callback در تابع initMediaSession پیاده سازی شده اند تا تمام MediaPlayer را مدیریت کنند . 

 

گام های پایانی ساخت موزیک پلیر در اندروید استودیو

خب داریم کم کم به آخر این آموزش میرسیم و تنها چیزی که باقی مانده است تعریف کردن متد onStartCommand() در service است . 

این متد قرار است کار پیاده سازی اولیه MediaSession ، MediaPlayer ، فراخوانی کردن کش پلی لیست آهنگ ها و ساختن نوتیفیکشن MediaStyle را انجام دهد .

در کلاس service تابع قدیمی onStartCommand() را با کدهای زیر جایگزین کنید . 

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
  try {
      //Load data from SharedPreferences
      StorageUtil storage = new StorageUtil(getApplicationContext());
      audioList = storage.loadAudio();
      audioIndex = storage.loadAudioIndex();

      if (audioIndex != -1 && audioIndex < audioList.size()) {
          //index is in a valid range
          activeAudio = audioList.get(audioIndex);
      } else {
          stopSelf();
      }
  } catch (NullPointerException e) {
      stopSelf();
  }

  //Request audio focus
  if (requestAudioFocus() == false) {
      //Could not gain focus
      stopSelf();
  }

  if (mediaSessionManager == null) {
      try {
          initMediaSession();
          initMediaPlayer();
      } catch (RemoteException e) {
          e.printStackTrace();
          stopSelf();
      }
      buildNotification(PlaybackStatus.PLAYING);
  }

  //Handle Intent action from MediaSession.TransportControls
  handleIncomingActions(intent);
  return super.onStartCommand(intent, flags, startId);
}

 

در تابع initMediaPlayer() صدا زدن setDataSource() را با کد زیر جایگزین کنید . 

mediaPlayer.setDataSource(activeAudio.getData())

این جمع بندی برای پخش صوتی در یک service پس زمینه در Android است. اکنون برنامه را اجرا کرده و صوت را به روش صحیح پخش کنید. در زیر مثالی است از اینکه اپ من چطور کار میکنه . من یک RecyclerView به برنامه اضافه کردم و طرح ممکن است متفاوت به نظر برسد ، اما ظاهر اعلان و کنترل ها یکسان هستند.

آموزش ساخت موزیک پلیر در اندروید استودیو - 01
آموزش ساخت موزیک پلیر در اندروید استودیو - 01
آموزش ساخت موزیک پلیر در اندروید استودیو - 01

پایان 👍
برای استفاده ی دیگران و حمایت از ما در جامعه های زیر به اشتراک بگذارید

مهندس عباس نیک زاد


برای نوشتن نظر وارد شوید ورود
یا به عنوان یک میهمان نظر خود را بنویسید :
    1. اگر سوال شما طولانی است و نیاز به پشتیبانی خوبی دارد در پروفایل خود تیکت باز کنید تیم پشتیبان ما پاسخ میدهد
    2. سعی کنید نظر خود را بیش از چند جمله بنویسید
    3. نظرات شامل توهین و تهمت و نامرتبط تائید نخواهد شد

مستر بلک 817 / 3 ماه پیش

ادامه نداره آیا؟ آگه داره کجاس؟

برای دریافت جدید ترین آموزش ها ما را در فضای مجازی دنبال کنید
دوره ی آموزش پروژه محور ساخت کافه بازار دوره ی آموزش پروژه محور ساخت فروشگاه دیجیکالا آموزش لاراول دوره ی آموزش ویو جی اس
x
تبلیغات از درون سایت خودمون :)
X
دوره های پیشنهادی برای شما