90일차(1)/Android App(56) : mp3 파일 재생 예제 / 재생목록 출력(ListView)
- mp3 파일 여러개를 안드로이드 ListView에 넣어서 보여주기
- 음악파일에는 앨범,타이틀 이미지 등 여러 정보가 들어있다. 이 정보를 읽어오기!
- Spring 의 자원을 넣는 폴더 안에 넣어주기
- 서버에서는 json으로 출력
→ 안드로이드가 받아서 listview에 출력
→ 그 listview 셀을 클릭하면 재생할 준비를 하고 재생하도록!
- 안드로이드 리스트뷰 만들기
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">
<ListView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/listView"
android:choiceMode="singleChoice"/>
<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"/>
<ProgressBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:id="@+id/progress"/>
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:id="@+id/seek"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:id="@+id/time"/>
</LinearLayout>
android:choiceMode="singleChoice"
- 선택 모드. 하나씩 선택할 수 있는 레이아웃이다.
- 이런 형태로 레이아웃을 잡아준다!
MainActivity
package com.example.step23mp3player;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener , Util.RequestListener {
MediaPlayer mp;
//재생 준비가 되었는지 여부
boolean isPrepared=false;
ImageButton playBtn;
ProgressBar progress;
TextView time;
SeekBar seek;
//서비스의 참조값을 저장할 필드
MusicService service;
//서비스에 연결되었는지 여부
Boolean isConnected;
//Adapter에 연결된 모델
List<String> songs;
//adapter의 참조값
ArrayAdapter<String> adapter;
//서비스 연결 객체
ServiceConnection sConn=new ServiceConnection() {
//서비스에 연결이 되었을 때 호출되는 메소드
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
//MusicService 객체의 참조값을 얻어와서 필드에 저장
//IBinder 객체를 원래 type으로 캐스팅
MusicService.LocalBinder IBinder=(MusicService.LocalBinder)binder;
service=IBinder.getService();
//연결되었다고 표시
isConnected=true;
//핸들러에 메세지 보내기
handler.removeMessages(0); //만일 핸들러가 동작중에 있으면 메세지를 제거하고
handler.sendEmptyMessageDelayed(0,100); //다시 보내기
}
//서비스에 연결이 해제되었을 때 호출되는 메소드
@Override
public void onServiceDisconnected(ComponentName name) {
//연결이 해제되었다고 표시
isConnected=false;
}
};
//UI를 주기적으로 업데이트하기 위한 Handler
Handler handler=new Handler(){
/*
이 Handler에 메세지를 한번만 보내면 아래의 handleMessage() 메소드가 1/10초마다 반복적으로 호출된다.
handleMessage() 메소드는 UI 스레드 상에서 실행되기 때문에 마음대로 UI를 업데이트할 수 있다.
*/
@Override
public void handleMessage(@NonNull Message msg) {
if(service.isPrepared()){
//전체 재생시간
int maxTime=service.getMp().getDuration();
progress.setMax(maxTime);
seek.setMax(maxTime);
//현재 재생 위치
int currentTime=service.getMp().getCurrentPosition();
//음악 재생이 시작된 이후에 주기적으로 계속 실행이 되어야 한다.
progress.setProgress(currentTime);
seek.setProgress(currentTime);
//현재 재생 시간을 TextView에 출력하기.
String info=String.format("%d min, %d sec",
TimeUnit.MILLISECONDS.toMinutes(currentTime),
TimeUnit.MILLISECONDS.toSeconds(currentTime)
-TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(currentTime)));
time.setText(info);
}
//자신의 객체에 빈 메세지를 보내서 handleMessage() 가 일정 시간 이후에 호출되도록 한다.
handler.sendEmptyMessageDelayed(0, 100); // 1/10초 이후에
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//TextView의 참조값 얻어와서 필드에 저장
time=findViewById(R.id.time);
// %d는 숫자, %s 는 문자.
String info=String.format("%d min, %d sec", 0, 0);
time.setText(info);
//ProgressBar의 참조값 얻어오기
progress=findViewById(R.id.progress);
seek=findViewById(R.id.seek);
//재생 버튼
playBtn=findViewById(R.id.playBtn);
//재생 버튼을 눌렀을 때
playBtn.setOnClickListener(v -> {
//서비스의 playMusic() 메소드를 호출해서 음악이 재생되도록 한다.
service.playMusic();
});
//일시중지 버튼
ImageButton pauseBtn=findViewById(R.id.pauseBtn);
pauseBtn.setOnClickListener(v -> {
service.pauseMusic();
});
//알림 채널 만들기
createNotificationChannel();
//ListView 관련 작업
ListView listView=findViewById(R.id.listView);
//샘플 데이터
songs=new ArrayList<>();
//listView에 연결할 아답타
adapter=new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1, songs);
listView.setAdapter(adapter);
//listView에 아이템 클릭 리스너 등록
listView.setOnItemClickListener(this);
//재생할 곡 목록을 서버로부터 받아온다.
Util.sendGetRequest(1, "http://192.168.0.34:9000/boot07/api/music/list", null, this);
}
@Override
protected void onStart() {
super.onStart();
//MusicService 에 연결할 인텐트 객체
Intent intent=new Intent(this, MusicService.class);
//서비스 시작시키기
//startService(intent);
//Activity의 bindService() 메소드를 이용해서 연결한다.
//만일 서비스가 시작이 되지 않았으면 서비스 객체를 생성해서
//시작할 준비만 된 서비스에 바인딩이 된다.
bindService(intent, sConn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if(isConnected){
//서비스 바인딩 해제
unbindService(sConn);
isConnected=false;
}
}
//앱의 사용자가 알림을 직접 관리 할수 있도록 알림 체널을 만들어야 한다.
public void createNotificationChannel(){
//알림 채널을 지원하는 기기인지 확인해서
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//알림 채널을 만들기
//셈플 데이터
String name="Music Player";
String text="Control";
//알림 채널 객체를 얻어내서
//알림을 1/10 초마다 새로 보낼 예정이기 때문에 진동은 울리지 않도록 IMPORTANT_LOW 로 설정한다.
NotificationChannel channel=
new NotificationChannel(AppConstants.CHANNEL_ID, name, NotificationManager.IMPORTANCE_LOW);
//채널의 설명을 적고
channel.setDescription(text);
//알림 메니저 객체를 얻어내서
NotificationManager notiManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
//알림 채널을 만든다.
notiManager.createNotificationChannel(channel);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case 0:
//권한을 부여 했다면
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
}else{//권한을 부여 하지 않았다면
Toast.makeText(this, "알림을 띄울 권한이 필요합니다.",
Toast.LENGTH_SHORT).show();
}
break;
}
}
//listView의 cell을 클릭하면 호출되는 메소드
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//position은 클릭한 셀의 인덱스, id는 클릭할 셀의 id, view는 클릭한 cell view
String fileName=songs.get(position);
service.initMusic(AppConstants.MUSIC_URL+fileName);
}
@Override
public void onSuccess(int requestId, Map<String, Object> result) {
if(requestId == 1){
//[{"fileName":"mp3piano.mp3","title":"쇼팽 녹턴"},{"fileName":"Over_the_Horizon.mp3","title":"Over The Horizon"}]
String jsonStr=(String) result.get("data");
try {
JSONArray arr=new JSONArray(jsonStr);
//반복문 돌면서
for(int i=0; i<arr.length(); i++){
//JSONObject 객체를 얻어내서 재생목록(음악 목록)에 추가한다.
JSONObject obj=arr.getJSONObject(i);
String fileName=obj.getString("fileName");
songs.add(fileName);
}
//listView에 연결된 adapter에 모델이 바뀌었다고 알려서 listView에 목록이 출력되도록 한다.
adapter.notifyDataSetChanged();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onFail(int requestId, Map<String, Object> result) {
}
}
- ListView 초기화 작업
- 일단 샘플데이터를 넣어서 간단히 표시해준다.
- ListView를 쓰려면 아답타가 필요하다.
- 지금은 그냥 ArrayAdapter를 쓰는데, 아답타를 직접 만들고 싶다면 만들어도 된다.
- run 해보면 클릭한 셀의 색상이 다르게 나온다.
- simple_list_item_activated_1 와 singlechoice로 지정해서!
- 클릭하면 플레이되도록 준비할 수 있다.
- 음악1, 음악2 부분에는 재생할 음악의 제목이 출력되어야 한다.
- Activity에 onItemClickListener 구현
- songs 리스트를 필드로 옮기고 메소드안에서 songs에 값을 넣어준다.
- filneName이라는 변수를 설정하고, 이 파일명을 사용해서 요청 url을 구성한다.
- initMusic을 지우고 playMusic으로 수정
- initMusic은 여기에 넣어준다.
- 음악도 재생할 음악의 제목과 저장된 파일 경로를 따로 관리할 것이다.
- 이런 값은 상수로 관리하면 좋다.
AppConstants
package com.example.step23mp3player;
public class AppConstants {
public static final String ACTION_PLAY="action_start";
public static final String ACTION_PAUSE="action_pause";
public static final String ACTION_STOP="action_stop";
//채널의 아이디
public static final String CHANNEL_ID="my_channel";
//알림의 아이디
public static final int NOTI_ID=999;
public static final String MUSIC_URL="http://192.168.0.34:9000/boot07/resources/upload/";
}
- 위와 같이 상수화하여 관리하면 코드를 깔끔하게 만들 수 있다.
- 이제 각각의 셀을 클릭하면 음악이 나온다.
- STS의 안드로이드 컨트롤러 작성
AndroidContoller.java
package com.sy.boot07.api;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.sy.boot07.gallery.dao.GalleryDao;
import com.sy.boot07.gallery.dto.GalleryDto;
/*
* 안드로이드의 App요청을 처리할 컨트롤러
*/
@Controller
public class AndroidController {
@RequestMapping("/api/music/list")
@ResponseBody
public List<Map<String,Object>> getMusicList(){
Map<String,Object> song1=new HashMap<>();
song1.put("title", "쇼팽 녹턴");
song1.put("fileName", "mp3piano.mp3");
Map<String,Object> song2=new HashMap<>();
song2.put("title", "Over The Horizon");
song2.put("fileName", "Over_the_Horizon.mp3");
List<Map<String,Object>> list = new ArrayList<>();
list.add(song1);
list.add(song2);
return list;
}
/*
* JSON 문자열
* 1. @ResponseBody 어노테이션
* 2. Map 혹은 List 혹은 Dto 를 리턴하면 자동으로 JSON 문자열로 변환 되어서 응답된다.
*/
@RequestMapping("/api/send")
@ResponseBody
public Map<String, Object> send(String msg){
System.out.println(msg);
Map<String, Object> map=new HashMap<>();
map.put("isSuccess", true);
map.put("response", "hello client!");
map.put("num", 1);
return map;
}
@RequestMapping("/api/list")
@ResponseBody
public List<String> list(int pageNum){
List<String> names=new ArrayList<>();
names.add("바나나");
names.add("딸기");
names.add("복숭아");
return names;
}
//로그인 여부를 json으로 응답하는 메소드
@RequestMapping("/api/logincheck")
@ResponseBody
public Map<String, Object> logincheck(HttpSession session){
//테스트로 session의 아이디를 출력해보기
System.out.println("세션 아이디:"+session.getId());
Map<String, Object> map=new HashMap<>();
//세션 영역에 id라는 키값으로 저장된 값이 있는지 읽어와 본다.
String id=(String)session.getAttribute("id");
//만일 로그인을 하지 않았다면
if(id == null) {
map.put("isLogin", false);
System.out.println("로그인중이 아님요");
}else {
map.put("isLogin", true);
map.put("id", id);
System.out.println(id+" 로그인중...");
}
return map;
}
@RequestMapping("/api/login")
@ResponseBody
public Map<String, Object> login(String id, String pwd, HttpSession session){
//session.setMaxInactiveInterval(60*60*24); //로그인 유지시간
System.out.println(id+"|"+pwd);
Map<String, Object> map=new HashMap<>();
if(id.equals("gura") && pwd.equals("1234")) {
map.put("isSuccess", true);
map.put("id", id);
session.setAttribute("id", id);
}else {
map.put("isSuccess", false);
}
return map;
}
@RequestMapping("/api/logout")
@ResponseBody
public Map<String, Object> logout(HttpSession session){
session.invalidate();
Map<String, Object> map=new HashMap<>();
map.put("isSuccess", true);
return map;
}
@Autowired GalleryDao dao;
@RequestMapping("/api/gallery/list")
@ResponseBody
public List<GalleryDto> galleryList(){
//최신 갤러리 데이터 10개만 가져오기
GalleryDto dto=new GalleryDto();
dto.setStartRowNum(1);
dto.setEndRowNum(10);
return dao.getList(dto);
}
}
- 재생할 음악을 안드로이드에 JSON문자열로 전달하기
- spring에서 왼쪽과 같이 작성하면 이런 형식의 JSON을 응답받을 수 있다.
- map 에 곡 정보를 담고 list에 담았더니 이렇게 응답된다.
- 이 데이터를 안드로이드에서 요청해서 받아갈 수 있다.
- 이전에 사용했던 Util 클래스 복사해서 가져오기
Util
package com.example.step23mp3player;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.inputmethod.InputMethodManager;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Util {
//키보드 숨기는 메소드
public static void hideKeyboard(Activity activity){
InputMethodManager iManager=(InputMethodManager)
activity.getSystemService(Context.INPUT_METHOD_SERVICE);
if(activity.getCurrentFocus()==null)return;
iManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}
//메소드의 인자로 전달되는 View 객체의 포커스 해제하는 메소드
public static void releaseFocus(View view) {
ViewParent parent = view.getParent();
ViewGroup group = null;
View child = null;
while (parent != null) {
if (parent instanceof ViewGroup) {
group = (ViewGroup) parent;
for (int i = 0; i < group.getChildCount(); i++) {
child = group.getChildAt(i);
if(child != view && child.isFocusable())
child.requestFocus();
}
}
parent = parent.getParent();
}
}
public static interface RequestListener{
public void onSuccess(int requestId, Map<String, Object> result);
public void onFail(int requestId, Map<String, Object> result);
}
/*
1. 사용할때 RequestListener 인터페이스 Type 을 전달한다.
2. 결과는 RequestListener 객체에 전달된다.
3. Map<String,Object> 에서 응답 코드는
"code" 라는 키값으로 200, 404, 500, -1 중에 하나가 리턴되고
-1 이 리턴되면 Exception 발생으로 실패이다. onFail() 호출
*/
public static void sendGetRequest(int requestId,
String requestUrl,
Map<String,String> params,
RequestListener listener){
RequestTask task=new RequestTask();
task.setRequestId(requestId);
task.setRequestUrl(requestUrl);
task.setListener(listener);
task.execute(params);
}
private static class RequestTask extends AsyncTask<Map<String,String>, Void, Map<String,Object>> {
private int requestId;
private String requestUrl;
private RequestListener listener;
public void setListener(RequestListener listener) {
this.listener = listener;
}
public void setRequestId(int requestId) {
this.requestId = requestId;
}
public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
}
@Override
protected Map<String, Object> doInBackground(Map<String, String>... params) {
String requestUrl=this.requestUrl;
Map<String, String> param=params[0];
if(param!=null){//요청 파리미터가 존재 한다면
//서버에 전송할 데이터를 문자열로 구성하기
StringBuffer buffer=new StringBuffer();
Set<String> keySet=param.keySet();
Iterator<String> it=keySet.iterator();
boolean isFirst=true;
//반복문 돌면서 map 에 담긴 모든 요소를 전송할수 있도록 구성한다.
while(it.hasNext()){
String key=it.next();
String arg=null;
//파라미터가 한글일 경우 깨지지 않도록 하기 위해.
String encodedValue=null;
try {
encodedValue= URLEncoder.encode(param.get(key), "utf-8");
} catch (UnsupportedEncodingException e) {}
if(isFirst){
arg="?"+key+"="+encodedValue;
isFirst=false;
}else{
arg="&"+key+"="+encodedValue;
}
buffer.append(arg);
}
String data=buffer.toString();
requestUrl +=data;
}
//서버가 http 요청에 대해서 응답하는 문자열을 누적할 객체
StringBuilder builder=new StringBuilder();
HttpURLConnection conn=null;
InputStreamReader isr=null;
BufferedReader br=null;
//결과값을 담을 Map Type 객체
Map<String,Object> map=new HashMap<String,Object>();
try{
//URL 객체 생성
URL url=new URL(requestUrl);
//HttpURLConnection 객체의 참조값 얻어오기
conn=(HttpURLConnection)url.openConnection();
if(conn!=null){//연결이 되었다면
conn.setConnectTimeout(20000); //응답을 기다리는 최대 대기 시간
conn.setRequestMethod("GET");//Default 설정
conn.setUseCaches(false);//케쉬 사용 여부
//응답 코드를 읽어온다.
int responseCode=conn.getResponseCode();
//Map 객체에 응답 코드를 담는다.
map.put("code", responseCode);
if(responseCode==200){//정상 응답이라면...
//서버가 출력하는 문자열을 읽어오기 위한 객체
isr=new InputStreamReader(conn.getInputStream());
br=new BufferedReader(isr);
//반복문 돌면서 읽어오기
while(true){
//한줄씩 읽어들인다.
String line=br.readLine();
//더이상 읽어올 문자열이 없으면 반복문 탈출
if(line==null)break;
//읽어온 문자열 누적 시키기
builder.append(line);
}
//출력받은 문자열 전체 얻어내기
String str=builder.toString();
//아래 코드는 수행 불가
//console.setText(str);
//Map 객체에 결과 문자열을 담는다.
map.put("data", str);
}
}
}catch(Exception e){//예외가 발생하면
//에러 정보를 담는다.
map.put("code",-1);
map.put("data", e.getMessage());
}finally {
try{
if(isr!=null)isr.close();
if(br!=null)br.close();
if(conn!=null)conn.disconnect();
}catch(Exception e){}
}
//결과를 담고 있는 Map 객체를 리턴해준다.
return map;
}
@Override
protected void onPostExecute(Map<String, Object> map) {
super.onPostExecute(map);
int code=(int)map.get("code");
if(code!=-1){//성공이라면
listener.onSuccess(requestId, map);
}else{//실패 (예외발생)
listener.onFail(requestId, map);
}
}
}
//POST 방식 REQUEST
public static void sendPostRequest(int requestId, String requestUrl, Map<String, String> params, RequestListener listener){
PostRequestTask task=new PostRequestTask();
task.setRequestId(requestId);
task.setRequestUrl(requestUrl);
task.setListener(listener);
task.execute(params);
}
public static class PostRequestTask extends AsyncTask<Map<String, String>, Void, Map<String, Object>>{
private int requestId;
private String requestUrl;
private RequestListener listener;
public void setListener(RequestListener listener) {
this.listener = listener;
}
public void setRequestId(int requestId) {
this.requestId = requestId;
}
public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
}
@Override
protected Map<String, Object> doInBackground(Map<String, String>... params) {
Map<String, String> param=params[0];
String queryString="";
if(param!=null){//요청 파리미터가 존재 한다면
//서버에 전송할 데이터를 문자열로 구성하기
StringBuffer buffer=new StringBuffer();
Set<String> keySet=param.keySet();
Iterator<String> it=keySet.iterator();
boolean isFirst=true;
//반복문 돌면서 map 에 담긴 모든 요소를 전송할수 있도록 구성한다.
while(it.hasNext()){
String key=it.next();
String arg=null;
if(isFirst){
arg=key+"="+param.get(key);
isFirst=false;
}else{
arg="&"+key+"="+param.get(key);
}
buffer.append(arg);
}
queryString=buffer.toString();
}
//서버가 http 요청에 대해서 응답하는 문자열을 누적할 객체
StringBuilder builder=new StringBuilder();
HttpURLConnection conn=null;
InputStreamReader isr=null;
BufferedReader br=null;
PrintWriter pw=null;
//결과값을 담을 Map Type 객체
Map<String,Object> map=new HashMap<String,Object>();
try{
//URL 객체 생성
URL url=new URL(requestUrl);
//HttpURLConnection 객체의 참조값 얻어오기
conn=(HttpURLConnection)url.openConnection();
if(conn!=null){//연결이 되었다면
conn.setConnectTimeout(20000); //응답을 기다리는 최대 대기 시간
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setUseCaches(false);//케쉬 사용 여부
//전송하는 데이터에 맞게 값 설정하기
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); //폼전송과 동일
//출력할 스트림 객체 얻어오기
OutputStreamWriter osw=
new OutputStreamWriter(conn.getOutputStream());
//문자열을 바로 출력하기 위해 osw 객체를 PrintWriter 객체로 감싼다
pw=new PrintWriter(osw);
//서버로 출력하기
pw.write(queryString);
pw.flush();
//응답 코드를 읽어온다.
int responseCode=conn.getResponseCode();
//Map 객체에 응답 코드를 담는다.
map.put("code", responseCode);
if(responseCode==200){//정상 응답이라면...
//서버가 출력하는 문자열을 읽어오기 위한 객체
isr=new InputStreamReader(conn.getInputStream());
br=new BufferedReader(isr);
//반복문 돌면서 읽어오기
while(true){
//한줄씩 읽어들인다.
String line=br.readLine();
//더이상 읽어올 문자열이 없으면 반복문 탈출
if(line==null)break;
//읽어온 문자열 누적 시키기
builder.append(line);
}
//출력받은 문자열 전체 얻어내기
String str=builder.toString();
//아래 코드는 수행 불가
//console.setText(str);
//Map 객체에 결과 문자열을 담는다.
map.put("data", str);
}
}
}catch(Exception e){//예외가 발생하면
//에러 정보를 담는다.
map.put("code",-1);
map.put("data", e.getMessage());
}finally {
try{
if(pw!=null)pw.close();
if(isr!=null)isr.close();
if(br!=null)br.close();
if(conn!=null)conn.disconnect();
}catch(Exception e){}
}
//결과를 담고 있는 Map 객체를 리턴해준다.
return map;
}
@Override
protected void onPostExecute(Map<String, Object> map) {
super.onPostExecute(map);
int code=(int)map.get("code");
if(code!=-1){//성공이라면
listener.onSuccess(requestId, map);
}else{//실패 (예외발생)
listener.onFail(requestId, map);
}
}
}
}
- Util 클래스를 패키지에 복사해온다.
- 그려면 메인 액티비티에서 Util.sendGetRequest(); 으로 가져와서 사용할 수 있다.
- 리스너 역할을 하도록 Activity에 리스너 구현 추가!
- 요청 id, 경로, 패러미터, 리스너 를 인자로 넣어준다.
- 이것에 대한 응답은 onSuccess() 메소드로 들어온다.
- [ ] → JSONArray
{ } → JSONObject 와 대응된다.
- exception이 발생한다. jsonStr 문자열이 타입에 맞지 않을 수도 있으므로!
- 해당 부분을 try~catch문으로 묶어주기
- for문으로 반복문 돌면서 listView에 출력해주기!
- adapter의 참조값은 여기저기에서 필요하므로.. 필드로 빼준다.
- 곡 목록은 서버로부터 받아온다.
- 서버로부터 받아온 데이터가 ListView에 이렇게 출력된다.
- 셀 클릭시 각각의 음악이 재생된다.
- 이렇게 안드로이드+서버 프로그래밍을 결합해서 연습해볼 수 있다.
- 추가로 연습해보고 싶다면 코틀린으로도 바꿔보기!
'국비교육(22-23)' 카테고리의 다른 글
91일차(1)/Spring Boot(15) : mp3 파일 업로드, 재생 / AOP 활용 예제 (1) | 2023.02.19 |
---|---|
90일차(2)/Spring Boot(14) : mp3 파일 업로드, metadata 추출 (0) | 2023.02.17 |
89일차(1)/Android App(55) : mp3 파일 재생 예제 / Notification(3) (0) | 2023.02.16 |
88일차(2)/Android App(54) : mp3 파일 재생 예제 / Notification(2) (0) | 2023.02.15 |
88일차(1)/Android App(53) : mp3 파일 재생 예제 / Notification(1) (0) | 2023.02.14 |