티스토리 뷰
9. 상주 서비스를 만들자
상주하는 서비스이 예로 간이 음악 플레이어를 만들겠습니다.
구현을 간단히 하고자 앱 내에 저장하기로 하고 음악 재생은 MediaPlayer 클래스로 합니다. 여기서는 음악의 재생과 정지라는 두 가지 기능만 구현 해봅시다.
음악 재생과 정지
MediaPlayer 인스턴스를 만들고 start()를 호출하면 음악이 재생됩니다. MediaPlayer.create()의 두번째 인수로 전달하는 R.raw.bensound_clearday 음악 파일 입니다.
음악 재생
MediaPlayer 클래스를 이용하기 위해 선언 한다.
private MediaPlayer mPlayer;
/**
* 음악을 재생한다
*/
public void play() {
Log.d(TAG, "play");
mPlayer = MediaPlayer.create(this, R.raw.bensound_clearday);
mPlayer.setLooping(true); // Set looping
mPlayer.setVolume(100, 100);
mPlayer.start();
}
MediaPlayer 객체를 초기화 하고 start(); 메소드를 실행한다.
음악을 정지하고 MediaPlayer 가 필요 없어지면 release() 를 호출해서 MediaPlayer를
해제합니다.
/**
* 음악을 정지한다. 모두 정지 중인 경우는 아무것도 하지 않는다
*/
public void stop() {
Log.d(TAG, "stop");
if (mPlayer.isPlaying()) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
}
서비스 만들기
액티비티가 최상위가 아니어도 백그라운드에서 재생되는 음악을 실행해 보자.
* 앱이 사작되면 서비스에 바인드하고, 현재 음악이 재생중인지 확인한다.
* 음악 정지 상태에서 액티비티가 정지한 경우는 서비스도 정지한다
* 음악 재생 상태에서 약티비티가 정지해도 서비스는 상주한 채로 두고 계속 음악을 재생한다.
시작시 처리
private BackgroundMusicService mServiceBinder;
@Override
protected void onResume() {
Log.d("activity", "onResume");
super.onResume();
if (mServiceBinder == null) {
// 서비스에 바인드
doBindService();
}
// ContextWrapper 자체 메소드
startService(new Intent(getApplicationContext(), BackgroundMusicService.class));
}
public void doBindService() {
Intent intent = null;
intent = new Intent(this, BackgroundMusicService.class);
// ContextWrapper 자체 메소드
bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
// 서비스와의 연결 콜백
private ServiceConnection myConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
mServiceBinder = ((BackgroundMusicService.MyBinder) binder).getService();
Log.d("ServiceConnection","connected");
updateButtonEnabled();
}
public void onServiceDisconnected(ComponentName className) {
Log.d("ServiceConnection", "disconnected");
mServiceBinder = null;
}
};
실제로 바인드하면 ServiceConnection 으로 구현한 콜백이 호출 됩니다.
서비스에 연결하면 onServiceConnected() 가 콜백됩니다. 두 번째 인수로 전달되는
IBinder(바인더 인터페이스) 로 부터 서비스의 인터페이스를 가져오고, 다른 메서드로부터도 참조할 수 있게 필드에 보관 합니다(BackgroundMusicService.class)
서비스가 정지하거나 unbindService() 를 호춣해 언바인드하면 서비스에서 벗어납니다. 서비스가 비정상적으로 종료해서 벗어난 경우에슨 onServiceDesconnected() 가 호출 됩니다.
음악 정지 상태에서 액티비티가 정지한 경우에는 서비스도 정지하고 음악이 재생 상태에서는 액티비티가 정지해도 서비스는 상주한 채로 음악은 계속 재생되게 해봅시다.
@Override
protected void onPause() {
// 화면이 obPause 일 때 !!
Log.d("activity", "onPause");
super.onPause();
if (mServiceBinder != null) {
mIsPlaying = mServiceBinder.isPlaying();
// 정지 중인 경우는 서비스를 계속 실행할 필요가 없으므로 정지 한다.
if (!mIsPlaying) {
mServiceBinder.stopSelf();
}
// 액티비티가 비표시일 때는 액티비티에서만 조작하므로 언바이드 한다
unbindService(myConnection);
mServiceBinder = null;
}
}
전체 소스
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.advanced_android.musicplayersample" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".BackgroundMusicService"
android:enabled="true"
android:exported="false" >
</service>
</application>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<Button android:id="@+id/btn_play" android:text="Play" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:enabled="false"/>
<Button android:id="@+id/btn_stop" android:text="Stop" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_toRightOf="@+id/btn_play" android:enabled="false"/>
</RelativeLayout>
public class BackgroundMusicService extends Service {
private static final String TAG = BackgroundMusicService.class.getSimpleName();
private final IBinder mBinder = new MyBinder();
private MediaPlayer mPlayer;
public BackgroundMusicService() {
}
@Override
public IBinder onBind(Intent arg0) {
Log.d("service","onBind");
return mBinder;
}
public class MyBinder extends Binder {
BackgroundMusicService getService() {
return BackgroundMusicService.this;
}
}
/**
* 음악재생중인지 아닌지 반환한다
*
* @return 음악재생중인 경우는 true. 나머지는 false.
*/
public boolean isPlaying() {
boolean isPlaying = false;
if (mPlayer != null) {
isPlaying = mPlayer.isPlaying();
}
return isPlaying;
}
/**
* 음악을 재생한다
*/
public void play() {
Log.d(TAG, "play");
mPlayer = MediaPlayer.create(this, R.raw.bensound_clearday);
mPlayer.setLooping(true); // Set looping
mPlayer.setVolume(100, 100);
mPlayer.start();
}
/**
* 음악을 정지한다. 모두 정지 중인 경우는 아무것도 하지 않는다
*/
public void stop() {
Log.d(TAG, "stop");
if (mPlayer.isPlaying()) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
}
public class MainActivity extends AppCompatActivity {
private Boolean mIsPlaying;
private View mBtnPlay;
private View mBtnStop;
private BackgroundMusicService mServiceBinder;
// 서비스와의 연결 콜백
private ServiceConnection myConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
mServiceBinder = ((BackgroundMusicService.MyBinder) binder).getService();
Log.d("ServiceConnection","connected");
updateButtonEnabled();
}
public void onServiceDisconnected(ComponentName className) {
Log.d("ServiceConnection", "disconnected");
mServiceBinder = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnPlay = findViewById(R.id.btn_play);
mBtnPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mServiceBinder != null) {
mServiceBinder.play();
}
updateButtonEnabled();
}
});
mBtnStop = findViewById(R.id.btn_stop);
mBtnStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mServiceBinder != null) {
mServiceBinder.stop();
}
updateButtonEnabled();
}
});
}
private void updateButtonEnabled() {
if (mServiceBinder != null) {
if (mServiceBinder.isPlaying()) {
mBtnPlay.setEnabled(false);
mBtnStop.setEnabled(true);
} else {
mBtnPlay.setEnabled(true);
mBtnStop.setEnabled(false);
}
} else {
mBtnPlay.setEnabled(false);
mBtnStop.setEnabled(false);
}
}
public void doBindService() {
Intent intent = null;
intent = new Intent(this, BackgroundMusicService.class);
// ContextWrapper 자체 메소드
bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onResume() {
Log.d("activity", "onResume");
super.onResume();
if (mServiceBinder == null) {
// 서비스에 바인드
doBindService();
}
// ContextWrapper 자체 메소드
startService(new Intent(getApplicationContext(), BackgroundMusicService.class));
}
@Override
protected void onPause() {
// 화면이 obPause 일 때 !!
Log.d("activity", "onPause");
super.onPause();
if (mServiceBinder != null) {
mIsPlaying = mServiceBinder.isPlaying();
// 정지 중인 경우는 서비스를 계속 실행할 필요가 없으므로 정지 한다.
if (!mIsPlaying) {
mServiceBinder.stopSelf();
}
// 액티비티가 비표시일 때는 액티비티에서만 조작하므로 언바이드 한다
unbindService(myConnection);
mServiceBinder = null;
}
}
}
'android' 카테고리의 다른 글
11.ContentProbider을 알자 (0) | 2018.05.26 |
---|---|
10.IntentService를 활용하자 (0) | 2018.05.26 |
8. Service의 종류와 수명주기를 이해하자 (0) | 2018.05.20 |
7. LocalBroadcastReceiver 를 이해하자 (0) | 2018.05.20 |
6. BroadcastReceiver로 브로드캐스트 이벤트를 수신하자 (0) | 2018.05.20 |