Android-bindService本地服務-音樂播放(后台播放)-下


在上一篇  Android-bindService本地服務-音樂播放-上,博客中是不能在后台中播放到,這次博客增加了一個后台播放

 

通常情況下,Activity切換到后台,Service提升到前台進程,既然Service提升到前台進程就會有一個通知。

      Activity ---> moveTaskToBack(true);

      Service ---> startForeground(1, builder1.getNotification());

 

進程優先級別:前台進程,可視進程,服務進程,后台進程,空進程  (前台進程是最穩定,系統內存不足是先回收 空進程)

為什么要把服務Service提升為前台進程,在內存不足時,前台進程不會那么容易被系統回收

把 服務進程 提升到 前台進程 會自動綁定通知

UI相關,當在播放當過程中點擊返回鍵,就需要告訴用戶是否在后台運行

 

點擊后台播放,Activity就會被切換到后台,想要再次啟動APP就可以點擊通知進入:

 

由於這個MainActivity會被多次啟動,為了保證單任務,需要設置啟動模式:android:launchMode="singleTask"

    <activity android:name=".MainActivity5"
                  android:launchMode="singleTask">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

        </activity>

 

 定義Binder擴展接口:

package liudeli.service1.service.inter;

import android.media.MediaPlayer;

public interface IMusicPlay {

    /**
     * 播放音樂
     * @param musicPath 音樂文件的路徑
     */
    void playMusic(String musicPath);

    /**
     * 暫停播放
     */
    void pausedPlay();

    /**
     * 繼續播放
     */
    void continuePlay();

    /**
     * 停止播放
     */
    void stopPlay();

    /**
     * 讓Activity可以獲取到服務使用到MediaPlayer
     * @return
     */
    MediaPlayer getMediaPlayer();
}

 

Service控制播放:

package liudeli.service1.service;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.RequiresApi;
import android.util.Log;

import java.io.IOException;

import liudeli.service1.MainActivity5;
import liudeli.service1.R;
import liudeli.service1.service.inter.IMusicPlay;

public class MyService5 extends Service {

    private final String TAG = MyService5.class.getSimpleName();

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onCreate() {
        super.onCreate();

        /**
         * 進程優先級別:前台進程,可視進程,服務進程,后台進程,空進程  (前台進程是最穩定,系統內存不足是先回收 空進程)
         *
         * 為什么要把服務Service提升為前台進程,在內存不足時,前台進程不會那么容易被系統回收
         *
         * 把 服務進程 提升到 前台進程 會自動綁定通知
         */

        // 需要用到通知,用戶點擊通知欄,就計划APP-->Activity

        // 這是以前到寫法,已經過時
        /*Notification notification = new
                Notification(R.mipmap.ic_launcher, "我的音樂播放器", System.currentTimeMillis());*/

        // 設置事件信息,點擊通知可以跳轉到指定Activity
        Intent intent = new Intent(this, MainActivity5.class);


        // 設置事件信息,點擊通知可以跳轉到指定Activity
        NotificationManager notificationManager = (NotificationManager)
                getSystemService(Context.NOTIFICATION_SERVICE);

        // 設置通知顯示相關信息
        Notification.Builder builder1 = new Notification.Builder(this);
        builder1.setSmallIcon(R.mipmap.ic_launcher); //設置圖標
        /*builder1.setTicker("顯示第二個通知");*/
        builder1.setContentTitle("播放中"); //設置標題
        builder1.setContentText("我的音樂播放器"); //消息內容
        builder1.setWhen(System.currentTimeMillis()); //發送時間
        builder1.setDefaults(Notification.DEFAULT_ALL); //設置默認的提示音,振動方式,燈光
        builder1.setAutoCancel(true);//打開程序后圖標消失

        // 延時意圖,所謂延時意圖就是不是馬上執行,需要用戶去點擊后才執行,其實就是對Intent對封裝
        PendingIntent pendingIntent =PendingIntent.getActivity(this, 0, intent, 0);
        builder1.setContentIntent(pendingIntent);
        Notification notification1 = builder1.build();
        notificationManager.notify(124, notification1); // 通過通知管理器發送通知

        // id=通知到唯一標示  notification=通知
        startForeground(1, builder1.getNotification());
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "綁定成功");
        return new PlayMusicBinder();
    }

    private MediaPlayer mediaPlayer;

    /**
     * 增強版Binder,擴展出播放音樂🎵行為
     */
    class PlayMusicBinder extends Binder implements IMusicPlay {

        public PlayMusicBinder() {
            mediaPlayer = new MediaPlayer();
        }

        /**
         * 播放音樂
         *
         * @param musicPath 音樂文件的路徑
         */
        @Override
        public void playMusic(String musicPath) {
            try {
                mediaPlayer.reset();
                mediaPlayer.setDataSource(musicPath);
                mediaPlayer.prepare();
                mediaPlayer.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 暫停播放
         */
        @Override
        public void pausedPlay() {
            mediaPlayer.pause();
        }

        /**
         * 繼續播放
         */
        @Override
        public void continuePlay() {
            mediaPlayer.start();
        }

        /**
         * 停止播放
         */
        @Override
        public void stopPlay() {
            mediaPlayer.stop();
        }

        /**
         * 讓Activity可以獲取到服務使用到MediaPlayer
         *
         * @return
         */
        @Override
        public MediaPlayer getMediaPlayer() {
            return mediaPlayer;
        }
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "解綁成功");

        // 為什么解綁服務了,音樂還在播放,應該MediaPlay內部是一個服務

        if (mediaPlayer != null) {
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.stop();
            }
            mediaPlayer.release(); // 釋放硬件播放資源
        }
        return super.onUnbind(intent);
    }
}

 

MainActivity調用Service代碼:

package liudeli.service1;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import liudeli.service1.service.MyService5;
import liudeli.service1.service.inter.IMusicPlay;

public class MainActivity5 extends Activity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main5);

        initView();
        initListener();
    }

    @Override
    protected void onStart() {
        super.onStart();
        // 綁定服務
        bindService(new Intent(this, MyService5.class), connection, BIND_AUTO_CREATE);
    }

    private Button btPlayMusic;
    private Button btPausedContinue;
    private Button btStop;

    private void initView() {
        btPlayMusic = findViewById(R.id.bt_play_music);
        btPausedContinue = findViewById(R.id.bt_paused_continue);
        btStop = findViewById(R.id.bt_stop);
    }

    private void initListener() {
        btPlayMusic.setOnClickListener(this);
        btPausedContinue.setOnClickListener(this);
        btStop.setOnClickListener(this);
    }

    private IMusicPlay iMusicPlay;

    private ServiceConnection connection = new ServiceConnection() {
        /**
         * 連接到服務
         * @param name
         * @param service
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iMusicPlay = (IMusicPlay) service;
        }

        /**
         * 斷開連接
         * @param name
         */
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 解綁服務:注意bindService后 必須要解綁服務,否則會報 連接資源異常
        if (null != connection) {
            unbindService(connection);
        }
    }

    // 音樂文件到路徑
    private final String MUSIC_PATH = Environment.getExternalStorageDirectory() + "/cjyy.mp3";

    @Override
    public void onClick(View v) {

        if (iMusicPlay == null) {
            Toast.makeText(this, "音樂播放服務連接失敗", Toast.LENGTH_LONG).show();
            return;
        }

        switch (v.getId()) {

            case R.id.bt_play_music:
                iMusicPlay.playMusic(MUSIC_PATH);
                break;

            case R.id.bt_paused_continue:
                if ("暫停播放".equals(btPausedContinue.getText().toString())) {
                    btPausedContinue.setText("繼續播放");
                    iMusicPlay.pausedPlay();
                } else {
                    btPausedContinue.setText("暫停播放");
                    iMusicPlay.continuePlay();
                }
                break;

            case R.id.bt_stop:
                iMusicPlay.stopPlay();
                break;

            default:
                break;
        }
    }

    /**
     * 用戶按返回鍵,系統會調用到此方法
     * @param keyCode
     * @param event
     * @return
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        super.onKeyDown(keyCode, event);

        // 判斷是否在播放,如果在播放中,才告知用戶 彈出對話框
        if (iMusicPlay == null) {
            return true;
        }

        MediaPlayer mediaPlayer = iMusicPlay.getMediaPlayer();

        if(mediaPlayer.isPlaying()) {

            switch (keyCode) {
                case KeyEvent.KEYCODE_BACK:
                    showAlertDialog();
                    break;
            }
        }
        return true;
    }

    /**
     * 彈出對話框
     */
    private void showAlertDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity5.this);
        builder.setTitle("提示");
        builder.setMessage("確定要關閉音樂播放?");
        builder.setNegativeButton("確定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                finish();
            }
        });
        builder.setNeutralButton("后台播放", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // 既然是后台播放,就是要把當前Activity切換到后台
                moveTaskToBack(true);
            }
        });
        builder.setPositiveButton("取消", null);
        AlertDialog dialog = builder.create();
        dialog.show();
    }
}

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM