86일차(1)/Android App(51) : mp3 파일 재생 예제 / MediaPlayer(서버)
- Service 음악파일 재생 예제 코드 복습
1) Activity
2) BroadcastReceiver
3) Service
4) ContentProvider
- 1~3은 intent 객체로 활성화시킨다.
- 4 는 ContentResolver로 활성화시킨다.
- 사용하려면 모두 AndroidManifest.xml에 등록되어 있어야 한다.
- 액티비티/서비스 이름 앞에 . 이라고 써 있는것은 이 패키지를 말한다.
- 이 패키지 안에 있는 MusicService라는 뜻
@Override
protected void onStart() {
super.onStart();
// MusicService 에 연결할 인텐트 객체
Intent intent=new Intent(this, MusicService.class);
//액티비티의 bindService() 메소드를 이용해서 연결한다.
// 만일 서비스가 시작이 되지 않았으면 서비스 객체를 생성해서
// 시작할 준비만 된 서비스에 바인딩이 된다.
bindService(intent, sConn, Context.BIND_AUTO_CREATE);
}
- onStart() 안에 bind를 넣어두면 서비스가 실행중이면 바인딩한다는 뜻이다.
- 안드로이드 플랫폼 안에 있는 MusicService. 이것은 현재 실행중일 수도, 아닐 수도 있다.
- 어떤 기능,동작이 실행되려면 어떤 프로세스를 할당받아야 한다.
이 프로세스상에서 뭔가가 실행된다.
- 이 프로세스에서 Service가 실행중이어도 MainActivity는 실행중이 아닐 수도 있다.
(실행된다면 동일한 프로세스 상에 들어간다)
- 이 MainActivity에서 뭔가 MusicService랑 관련된 내용을 하려면 연결이 되어야 하는데,
참조값을 쉽게 바로 얻어낼 수 없다.
- MusicService의 입장에서 MainActivity도 그렇다
- 액티비티가 활성화될 때 서비스를 바인딩한다.(서비스와 연결한다)
- 바인딩을 하기위해서는 intent 객체를 사용하고, 서비스 커넥션 객체를 사용한다.
- 바인딩 객체 사용하기!
- 비동기 처리로 이루어진다(약간 시간이 걸릴 수 있는 작업)
- 객체에 인자(sConn)를 전달하고, 바인딩이 성공하면 2번 메소드가 실행된다.
- 바인딩 연결객체가 있으면 바인딩하고 메소드를 실행해준다.
- 바인더에 있는 getService() 메소드를 사용해서
백그라운드에서 돌던 서비스의 참조값을 액티비티의 필드에 저장해놓는 것이다.
- Binder는 IBinder를 구현한 것이므로
LocalBinder 객체는 이렇게 여러 타입으로 받을수있다.
- IBinder타입으로 받아서 원래 타입으로 캐스팅해서 사용하면 된다.
- 여기서 리턴해주는 서비스의 참조값을 액티비티에서 가져간다.
- 일시중지 버튼 : 액티비티의 pause 메소드
- 재생 버튼 : start() 메소드
- 이 경우, 서비스는 계속 유지되므로 액티비티가 destroy되어도
서비스는 프로세스를 할당받아서 계속 돌아가고 있다.
** 서버에 들어있는 음악파일 재생해보기!
- 이전 예제의 음악파일은 앱 내부에 들어가 있다.
- 실제로는 이렇게 고정적으로 앱 안에 갖고있는 경우는 드물다
* 재생할 mp3 파일의 위치
1. Phone 내부의 어딘가
- App의 내부 저장장치 혹은 외부 저장장치
- ContentProvider App의 내부 저장장치 혹은 외부 저장장치
2. 인터넷상의 특정 url을 가지고 있는 서버
- http://192.168.0.1:9000/boot07/resources/upload/xxx.mp3 (특정 서버)
- 미디어플레이어 객체는 이렇게 인터넷상에있는 특정 파일을 로딩하는 기능도 가지고 있다.
- Spring Boot 프로젝트 boot07 내부 폴더에 mp3 파일을 넣는다. 브라우저에서 재생해볼 것!
- 새 모듈 생성-empty activity
step23mp3player
androidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MyAndroid"
android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
- 인터넷 사용 표기, http 가능 설정 추가
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_media_play"
android:tooltipText="재생버튼"
android:id="@+id/playBtn"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_media_pause"
android:tooltipText="일시정지"
android:id="@+id/pauseBtn"/>
</LinearLayout>
- ImageButton 요소를 넣어준다. ic_media_play는 이렇게 ▶ 생긴 아이콘!
MainActivity
package com.example.step23mp3player;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageButton;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements MediaPlayer.OnPreparedListener {
MediaPlayer mp;
//재생 준비가 되었는지 여부
boolean isPrepared=false;
ImageButton playBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//재생 버튼
playBtn=findViewById(R.id.playBtn);
//재생 버튼을 사용 불가 상태로 일단 설정
playBtn.setEnabled(false);
playBtn.setOnClickListener(v -> {
//만일 준비되지 않았으면
if(!isPrepared){
return; //메소드를 여기서 종료
}
mp.start();
});
//일시중지 버튼
ImageButton pauseBtn=findViewById(R.id.pauseBtn);
pauseBtn.setOnClickListener(v -> {
mp.pause();
});
}
@Override
protected void onStart() {
super.onStart();
//음악을 재생할 준비를 한다.
try {
mp=new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setDataSource("http://192.168.0.34:9000/boot07/resources/upload/mp3piano.mp3");
mp.setOnPreparedListener(this);
//로딩하기
mp.prepareAsync();
}catch (Exception e){
Log.e("MainActivity", e.getMessage());
}
}
@Override
protected void onStop() {
super.onStop();
mp.stop();
mp.release();
}
//재생할 준비가 끝나면 호출되는 메소드
@Override
public void onPrepared(MediaPlayer mp) {
Toast.makeText(this, "로딩 완료!", Toast.LENGTH_SHORT).show();
isPrepared=true;
playBtn.setEnabled(true);
}
}
- 이 자원이 없을 수도 있기 때문에 exception이 발생한다.
- try문으로 해당 부분을 묶어주면 된다.
- 다른 메소드에서도 쓸 수 있도록 MediaPlayer mp; 를 필드로 선언하기!
- 이 prepare는 동기 작업이다. 네트워크 상황 등 속도가 느려지면 곤란할 수 있다.
- mp.pause() 로 일시정지 가능
mp.setOnPreparedListener();
mp.prepareAsync();
- 이것이 비동기 로딩 작업을 할 수 있는 메소드이다. 바꿔주기!
- 준비가 다 되었을 때 호출하는 리스너를 같이 붙여준다.
- 리스너를 this로 받기 위해 MainActivity에서 MediaPlayer.OnPreparedListener 를 구현해준다.
- onPrepared() 메소드 오버라이드
- 비동기로 로딩한 다음에, 로딩이 끝나면 onPrepared가 호출된다.(mp의 참조값도 같이 전달된다)
- 재생시 이렇게 토스트 메시지가 뜨고, 음악도 잘 재생된다.
- 로딩이 완료되었을 때 실행되는 메소드를 사용해서 비동기 처리로 인터넷상의 음악을 재생하기!!
- 버튼을 누르기전에 준비작업이 필요하다.
- prepareAsync() 에서 플레이를 누르기 전에 준비작업을 해준다.
- try~catch 문을 onStart() 안으로 옮겨주기
- mp.play(); 역시 onPrepared 안쪽이 아니라 재생 버튼을 클릭하면 재생되도록 넣어준다.
- 음악이 준비가 되었는지 안되었는지 파악할 수 있는 상태값이 있으면 좋겠다!
- 상태값 관리를 위한 필드를 만들어준다.(boolean)
- 준비 여부에 따라 이렇게 if문으로 분기할 수 있다.
-만약 준비되지않았다면 버튼이 아예 눌러지지 않게 할 수 있을까?
- UI 설정에서 clickable의 default값을 false로 만들 수도 있는데,
현재는 xml 설정으로는 안되고 코드로만 된다...
- MainActivity에서 버튼의 참조값에 .setEnabled(false) 로 작성하면 된다.
- play 설정의 참조값이 필요하므로 playBtn을 필드로 선언하고,
playBtn.setEnabled(true);
- 재생할 준비가 끝나면(onPrepared 메소드) 이 playBtn을 누를 수 있게 설정한다.
- 재생 준비가 되면 재생 버튼이 활성화되고, 재생 가능해진다.
- 초보 개발자 입장에서는 무언가를 개발하려고 하면 한번에 모든 코드가 떠오르지 않는다.
1) 간단한 기능, 떠오르는 기능을 하나씩 만들어 본다(단위기능, ...)
만들어보면 문제점이 떠오른다.
2) 문제점확인, 버그확인, 에러확인
ex) 음악재생: 웹브라우저 재생>안드로이드 재생 단계적으로 진행
3) 개선!
- 하나하나씩 구현해 나가면 된다.
이렇게 무한반복하다 보면 개선이 되고 차근차근 원하던 앱이 개발이 된다.
'국비교육(22-23)' 카테고리의 다른 글
88일차(1)/Android App(53) : mp3 파일 재생 예제 / Notification(1) (0) | 2023.02.14 |
---|---|
86일차(2)/Android App(52) : mp3 파일 재생 예제 / ProgressBar 사용 (0) | 2023.02.11 |
85일차(2)/Android App(50) : Service / Binder 객체로 mp3파일 재생하기 (0) | 2023.02.10 |
85일차(1)/Android App(49) : Notification(2) (0) | 2023.02.09 |
84일차(2)/Android App(48) : Notification(1) (0) | 2023.02.09 |