카테고리 없음

87 Android 오디오 재생

이즈라핀 2020. 8. 7. 16:58

** 오디오 재생

=> MediaPlayer 클래스를 이용하면 오디오 및 비디오 재생이 가능

=> MediaPlayer는 android.media 패키지에 존재 : C++라이브러리

=> 공식적으로 재생 가능한 포맷

Audio : wav, mp3, midi, ogg, 3gp

Video: H263, H264, Mpeg4

=> 설치된 코덱에 따라서 지원 포맷은 늘어나기도 한다.

 

1. 재상할 데이터를 설정

=> 메소드는 setDataSource : Overloading(하나의 클래스에 매개변수의 개수나 자료형을 다르게 해서 동일한 이름의 메소드가 2개 이상 존재하는 경우 - 동일한 작업을 수행하는 메소드가 매개변수 때문에 다른 이름을 가지면 일관성이 없기 때문에 이름을 동일하게 만드는 것을 권장한다. 객체 지향 언어에서 가능)이 되어 있다.

 

setDataSource(String path)

setDataSource(Context context, Uri uri)

setDataSource(FileDescriptor fd[, long offset, long length]) : fd 1개만 줘도 되고 fd, offset, length를 모두 대입해도 된다.

 

2. 재생 준비

=> 대용량 스트림인 경우 준비하는데 상당한 시간이 걸릴 수 있으므로 오픈 직후에 준비상태로 만들어 주어야 한다.

void prepare() : 이 메소드를 호출하면 작업이 끝날 때 까지 다음 작업으로 진행하지 않는다.

void prepareAsync() : 이 메소드의 수행이 비동기적이라서 다음 작업으로 바로 진행

 

=> 1번과 2번을 한번에 진행하는 정적 메소드인 create도 존재한다.

정적 메소드 : static 메소드

 

3. 재생 관련 메소드

start, stop, pause, setLooping(boolean looping), isLooping

 

4. 정리

restart();

reset();

 

** 파일 서버 만들기

=> Tomcat 기준 : webapps 디렉토리에 있는 디렉토리를 외부에서 접속할 수 있도록 해준다.

Tomcat의 시작 명령은 bin 디렉토리에 있는 startup.sh(Linux 나 Unix) 또는 startup.bat(Windows)

중지 명령은 bin 디렉토리에 있는 stop.sh 또는 stop.bat

=> 외부에서 데이터를 다운로드 받는 파일 서버를 만들고자 하면 webapps 디렉토리에 디렉토리를 만들고 파일을 저장한 후 Tomcat을 시작하면 된다.

 

외부에서는 http://IP:포트번호 또는 도메인/디렉토리이름/파일이름 으로 접속할 수 있다.

 

자체적으로 서버를 구착하는 곳들은 웹서버, 데이터베이스서버, 파일서버 등을 별도로 구축을 한다.

 

=> 파일을 제공하는 서버를 만들 때 파일 뿐 아니라 파일의 목록을 데이터베이스에 만들어 놓거나 아니면 알빈 파일로 만들어 두어야 한다.

모바일에 제공하는 서버인 경우 마지막에 업데이트된 날짜도 같이 저장을 한다.

 

클라이언트는 서버에 어떤 데이터가 있는지 모르기 때문에 데이터베이스나 파일을 읽어서 제공되는 데이터를 확인할 수 있어야 한다.

 

서버의 데이터를 다운로드 받아서 로컬에 저장할려고 하는 애플리케이션에서는 서버의 업데이트 정보를 알아야 하므로 서버가 업데이트 된 날짜를 인지 할 수 있어야 한다.

 

* 모든 애플리케이션이 공통으로 사용하는 디렉토리 이름

bin : 명령어 - 실행 파일

lib : 실행 파일들이 사용할 보조 파일

conf : 환경 설정 파일

 

logs : 로그 파일

temp : 임시 파일 저장

 

 

 

톰캣 bin 디렉토리에 있는 atartup.sh 파일을 실행 - 터미널에서

 

 

=> 테스트

브라우저에서 http://자신의IP:8080/webapps에 만든 디텍토리 이름/파일이름

 

http://192.168.0.200:8080/song/list.txt : 노래 파일 목록

=> 서버 애플리케이션을실행하는 것은 톰캣을 시작하고 종료

이클립스는 배포와 실행과는 아무런 상관이 없다.

 

**서버의 노래를 재생하는 애플리케이션

1.모듈을 생성

 

2.레이아웃을 설정

=>텍스트 뷰 1개 버튼 4개 SeekBar 1개

텍스트 뷰에는 현재 재생 중인 노래 제목을 출력

버튼은 노래 재생, 중지, 이전 노래, 다음 노래 재생

SeekBar는 현재 재생 중인 노래의 진행 상황을 표시

 

<?xml version="1.0" encoding="utf-8"?>

 

    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">

 

    <!-- 안드로이드에서는 뷰를 xml 파일에 디자인 하는 것이 일반적

    디자인을 할 때 크기는 필수 요소이고 자바 코드로 동적으로

    변경하고자 할 때는 반드시 id를 설정 -->

    <TextView

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="노래 제목"

        android:id="@+id/filename"/>

    <!--버튼 들을 배치 -->

    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">

        

        <Button

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="재생"

            android:id="@+id/btnPlay"/>

        <Button

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="중지"

            android:id="@+id/btnStop"/>

        <Button

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="이전"

            android:id="@+id/btnPrev"/>

        <Button

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="다음"

            android:id="@+id/btnNext"/>

        

    </LinearLayout>

    <!--ProgressBar는 진행 상황을 표시해 줄 수 있지만

    사용자의 이벤트를 받을 수 없고 SeekBar는 

    thumb을 이용해서 사용자의 이벤트를 받을 수 있습니다. -->

    <SeekBar

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:max="100"

        android:progress="0"

android:id="@+id/progress" />

 

</LinearLayout>

 

3.레이아웃 파일에 디자인한 내용을 자바 코드에서 찾아오기

1)필요한 뷰들을 인스턴스 변수로 선언

   Button btnPlay, btnStop, btnPrev, btnNext;

    SeekBar progress;

    TextView filename;

 

2)onCreate 메소드에서 찾아오기

        btnPlay = findViewById(R.id.btnPlay);

        btnStop = findViewById(R.id.btnStop);

        btnPrev = findViewById(R.id.btnPrev);

        btnNext = findViewById(R.id.btnNext);

        

        progress = findViewById(R.id.progress);

        filename = findViewById(R.id.filename);

=>안드로이드에서는 리소스를 R.java 파일에 int  상수로 저장하고 관리합니다.

다른 Activity의 id도 보이게 됩니다.

다른 Activity의 id를 실수로 작성하는 경우가 있습니다.

 

4.Activity 클래스에 필요한 인스턴스를 선언

    //노래 제목들을 저장할 List

    //m의 의미는 인스턴스 변수를 의미

    ArrayList<String> mSongList;

    //현재 재생 중인 노래의 인덱스

    int mIdx;

    //재생 여부를 판달할 변수

    boolean isPlaying;

    

    //음악 재생기 변수 

    MediaPlayer mMediaPlayer;

 

5.Activity가 종료될 때 호출되는 메소드를 재정의

=>멀티미디어, 네트워크, 데이터베이스 등을 사용할 때는 반드시 종료되기 직전에 모든 자원의 연결을 해제해야 합니다. 

안드로이드나 iOS에서는 외부 데이터베이스에 직접 연결이 안되기 때문에 멀티미디어나 네트워크를 사용하는 경우만 정확하게 해제해주면 됩니다.

    //내가 만든 클래스가 아닌 클래스의 메소드를 오버라이딩 할 때는

    //상위 클래스의 메소드를 호출하는 것이 좋습니다.

    //안드로이드에서는 추상메소드가 아닌 경우 상위 클래스의 메소드를

    //호출하지 않으면 에러 

    //메소드 호출 순서는 리턴이 있거나 종료하는 메소드의 경우는 

    //마지막에 호출해야 하고 그렇지 않으면 먼저 호출해야 합니다.

    @Override

    public void onDestroy(){

        if(mMediaPlayer != null){

            mMediaPlayer.release();

            mMediaPlayer = null;

        }

        super.onDestroy();

    }

 

6.이벤트 처리하는 메소드에서 코드를 전부 작성하면 코드의 길이가 너무 길어서 코드를 분할하기 위해서 사용자 정의 메소드를 생성 - 접근 지정자를 private으로 설정

=>이 메소드들은 실제 구현을 할 때는 처음에는 없고 리팩토리링 단계에서 생성합니다.

//인덱스를 받아서 재생 가능한 노래인지 판단하는 메소드

    private void loadMedia(int idx){

        //핸들러에게 전송할 메시지

        Message message = new Message();

        //메시지를 구분할 번호를 저장

        message.what = idx;

        try{

            mMediaPlayer.setDataSource(

                    this, Uri.parse(

                            mSongList.get(idx)));

        }catch(Exception e){

            Log.e("노래 준비 실패", e.getMessage());

            message.obj = false;

        }

        //노래를 바로 재생할 수 있도록 재생 준비

        try{

            mMediaPlayer.prepare();

            message.obj = true;

        }catch(Exception e){

            Log.e("노래 준비 실패", e.getMessage());

            message.obj = false;

        }

 

        //핸들러 호출

        mMessageHandler.sendMessage(message);

 

    }

 

7.MediaPlayer의 이벤트 처리를 위한 리스너 생성

  //MediaPlayer의 이벤트를 처리할 리스너 생성

    //음원 재생이 끝났을 때 호출되는 리스너

    MediaPlayer.OnCompletionListener mOnComplete =

            new MediaPlayer.OnCompletionListener() {

                @Override

                public void onCompletion(

                        MediaPlayer mediaPlayer) {

                    //현재 노래 재생이 끝나면 다음 노래 재생

                    mIdx =

                            (mIdx == mSongList.size()-1

                                    ?0:mIdx+1);

                    mMediaPlayer.reset();

                    loadMedia(mIdx);

                    mediaPlayer.start();

 

                }

            };

 

    //노래 재생에 실패했을 때 호출되는 리스너

    MediaPlayer.OnErrorListener mOnError =

            new MediaPlayer.OnErrorListener() {

                @Override

                public boolean onError(

                        MediaPlayer mediaPlayer,

                        int i, int i1) {

                    Toast.makeText(MainActivity.this,

                            "재생 중 에러 발생",

                            Toast.LENGTH_LONG).show();

                    return false;

                }

            };

    //노래 재생 준비가 완료되었을 때 호출되는 리스너

    MediaPlayer.OnSeekCompleteListener mOnSeekCompleter =

            new MediaPlayer.OnSeekCompleteListener() {

                @Override

                public void onSeekComplete(MediaPlayer mediaPlayer) {

                    if(isPlaying){

                        mMediaPlayer.start();

                    }

                }

            };

 

8.SeekBar를 움직였을 때 호출되는 리스너

   //시크바의 위치가 변경되었을 때 호출되는 리스너

    SeekBar.OnSeekBarChangeListener mOnSeek =

            new SeekBar.OnSeekBarChangeListener() {

                //썸을 눌러서 이동하고 값이 변경된 후에 호출되는 메소드

                @Override

                public void onProgressChanged(

                        SeekBar seekBar, int i, boolean b) {

                    //boolean b 가 사람에 의해서 변경이 된것인지

                    //다른 이유로 변경되었는지 알려주는 변수

                    if(b){

                        mMediaPlayer.seekTo(i);

                    }

 

                }

                //썸을 처음 눌렀을 때 호출되는 메소드

                @Override

                public void onStartTrackingTouch(SeekBar seekBar) {

                    if(mMediaPlayer.isPlaying()){

                        isPlaying = mMediaPlayer.isPlaying();

                        mMediaPlayer.pause();

                    }

                }

                //썸에서 손을 뗐을 때 호출되는 메소드

                @Override

                public void onStopTrackingTouch(SeekBar seekBar) {

 

                }

            };

 

9.2개의 핸들러를 생성

=>첫번째 핸들러는 loadMedia 가 호출하는 핸들러로 노래의 재생 가능 여부를 출력하기 위한 핸들러

=>두번째 핸들러는 노래가 재생될 때 0.2초마다 노래 재생 위치를 확인해서 SeekBar를 업데이트 해주는 핸들러

 

 

//화면 갱신을 위한 핸들러

    Handler mMessageHandler = new Handler(Looper.getMainLooper()){

        @Override

        public void handleMessage(Message message){

            //넘어온 결과 찾아오기

            boolean result = (Boolean)message.obj;

            String resultMsg = null;

            if(result == true){

                resultMsg = "재생 준비 완료";

                filename.setText(mSongList.get(

                        message.what));

                //재생할 노래의 길이로 seekbar의 길이 설정

                progress.setMax(mMediaPlayer.getDuration());

            }else{

                resultMsg = "재생 준비 실패";

            }

            Toast.makeText(MainActivity.this, resultMsg,

                    Toast.LENGTH_LONG).show();

        }

    };

    

    //0.2초마다 시크바의 값을 업데이트하는 핸들러

    Handler mProgressHandler = new Handler(

            Looper.getMainLooper()){

        @Override

        public void handleMessage(Message message){

            if(mMediaPlayer == null){

                return;

            }else if(mMediaPlayer.isPlaying()){

                progress.setProgress(

                        mMediaPlayer.getCurrentPosition());

            }

            //0.2초 다시 자기 자신을 호출 

            mProgressHandler.sendEmptyMessageDelayed(

                    0,200);

        }

    };

 

10.onCreate 메소드에서 초기화하는 작업을 수행

        mSongList = new ArrayList<>();

        //스레드를 생성해서 노래 목록을 다운로드 받기

        new Thread(){

            public void run(){

                try{

                    String addr = "http://192.168.0.200:8080/song/";

                    //노래 목록 파일 주소 생성

                    URL url = new URL(addr + "list.txt");

                    //연결

                    HttpURLConnection con =

                            (HttpURLConnection)url.openConnection();

                    //문자열 받기 위한 스트림 생성

                    BufferedReader br =

                            new BufferedReader(

                                    new InputStreamReader(

                                            con.getInputStream()));

                    StringBuilder sb = new StringBuilder();

                    while(true){

                        String line = br.readLine();

                        if(line == null){

                            break;

                        }

                        sb.append(line + "\n");

                    }

                    String data = sb.toString();

                    //문자열을 콤마로 분해

                    String [] songList = data.split(",");

                    for(String song : songList){

                        mSongList.add(addr + song + ".mp3");

                    }

 

                    //음원 재생기 생성

                    mMediaPlayer = new MediaPlayer();

                    mIdx = 0;

                    mMediaPlayer.setOnCompletionListener(mOnComplete);

                    mMediaPlayer.setOnErrorListener(mOnError);

                    mMediaPlayer.setOnSeekCompleteListener(

                            mOnSeekCompleter);

                    progress.setOnSeekBarChangeListener(mOnSeek);

                    //핸들러 호출

                    mProgressHandler.sendEmptyMessageDelayed(

                            0, 1000);

 

                    //버튼의 이벤트 핸들러

                    btnPlay.setOnClickListener(

                            new Button.OnClickListener(){

                        public void onClick(View view){

                            if(mMediaPlayer.isPlaying() == false){

                                mMediaPlayer.start();

                                btnPlay.setText("pause");

                            }else{

                                mMediaPlayer.pause();

                                btnPlay.setText("play");

                            }

                        }

                    });

                    btnStop.setOnClickListener(

                            new Button.OnClickListener(){

                                public void onClick(View view){

                                    mMediaPlayer.stop();

                                    btnPlay.setText("Play");

                                    progress.setProgress(0);

                                }

                            });

                    btnPrev.setOnClickListener(

                            new Button.OnClickListener(){

                                public void onClick(View view){

                                    //재생 중인지 여부를 저장

                                   boolean isPlaying =

                                           mMediaPlayer.isPlaying();

                                   //이전으로 이동

                                   mIdx = (mIdx == 0 ? mSongList.size()-1

                                           :mIdx-1);

                                   //플레이어 초기화

                                   mMediaPlayer.reset();

                                   //노래 재생 준비

                                   loadMedia(mIdx);

                                   //이전에 재생 중이면 바로 재생

                                   if(isPlaying){

                                       mMediaPlayer.start();;

                                       btnPlay.setText("Pause");

                                   }

                                }

                            });

                    btnNext.setOnClickListener(

                            new Button.OnClickListener(){

                                public void onClick(View view){

                                    //재생 중인지 여부를 저장

                                    boolean isPlaying =

                                            mMediaPlayer.isPlaying();

                                    //이전으로 이동

                                    mIdx = (mIdx == mSongList.size()-1 ?

                                            0

                                            :mIdx+1);

                                    //플레이어 초기화

                                    mMediaPlayer.reset();

                                    //노래 재생 준비

                                    loadMedia(mIdx);

                                    //이전에 재생 중이면 바로 재생

                                    if(isPlaying){

                                        mMediaPlayer.start();;

                                        btnPlay.setText("Pause");

                                    }

                                }

                            });

                }catch(Exception e){

                    Log.e("다운로드 예외", e.getMessage());

                }

            }

        }.start();

 

11.AndroidManifest.xml 파일에서 인터넷 권한 설정

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

 

    <application

        android:usesCleartextTraffic="true"

 

 

**기본 Activity가 없어서 실행이 안되는 경우가 발생

=>실행 가능한 Activity를 생성하고 코드를 복사해서 실행하면 됩니다.

클래스 이름과 layout 파일의 이름은 수정을 해야 합니다.

* 프로그램에서 말하는 메세지는 명령어를 뜻한다.

메소드를 호출하는 것을 메시지를 전송한다고 표현한다.

 

* 모든 GUI 프로그래밍에서는 기본적으로 Main Thread에서만 UI를 갱신할 수 있다.

 

* 안드로이드에서는 MainThread에게 작업을 부탁하는 클래스로 handler와 AsyncTask가 있다.

 

* 핸들러의 속성은 4개

=> Message.what : 정수

=> Message.arg1 : 정수 - LPARAM

=> Message.arg2 : 정수 - RPARAM

=> Message.obj : Object 타입의 데이터

 

* 기본 Activity가 없어서 실행이 안되는 경우가 발생

=> 실행 가능한 Activity를 생성하고 코드를 복사해서 실행하면 된다.

클래스 이름과 layout파일의 이름은 적절하게 수정

 

** Surface View

=> 그래픽 가속을 위한 뷰

=> 이전에는 그래픽을 출력할 때는 SurfaceView를 이용해서 좋은 성늘을 나타낸다고 했었다.

=> 최근의 안드로이드에서는 일반 뷰를 최적화해서 SurfaceView와 성능 차이가 거의 나지 않게 되었다.

=> 현재는 SurfaceView는 그래픽 가속 때문에 사용하는 것이 아니라 카메라로 사진이나 동영상을 활영할 때 카메라 뷰를 만들기 위한 용도로 주요 이용

카메라는 계속해서 변하는 데이터를 출력하기 때문에 약간의 성능차이가 크게 느껴질 수 있기 때문이다.

 

** 위치 정보 사용

=> 위치 정보를 여러 가지 방법으로 가져올 수 있다.

1. 위치 정보 제공자

=> 전화 기지국 : 가입된 통신사의 기지국을 기준으로 위치정보를 제공 - 부정확

=> 무선 네트워크를 제공하는 라우터 : 무선 네트워크를 이용하는 경우 가장 가까이에 있는 라우터의 위치 여기까지 2개는 고도 측정은 안된다.

=> GPS : 50m 정도의 오차를 갖는 미국방성 저퀘도 위성을 이용하는 방식

=> 갈릴레오 서비스 : 유럽에서 제공하는 위치 정보 제공자로 오차범위가 1m 이내

 

2. 위치 정보 제공자

public List<String> getAllPrividers() : 모든 위치 정보 제공자를 리스트로 리턴

public String getBestProvider() : 현재 상태에서 가장 성능이 좋은 위치 정보 제공자를 리턴

 

3. 위치 정보 제공자의 옵션 설정

setAltitudeRequired(고도)

setLatityueRquired(위도)

setLongitudeRequired(경도)

setBearingRequired(나침반)

=> 어떤 정보를 사용할 지 boolean으로 결정

 

setPowerRequiement(int level) : 전력 소모량

setAccuracy(int Accuracy) : 정밀도

=> 정밀도가 높아지면 배터리 소모량이 많아지고 정밀도를 낮추면 배터리 소모량이 감소함

=> 배터리 소모량이 정밀도를 설정

=> gps, 동영상, 블루투스, 와이파이는 배터리 소모량이 심하다.

 

4. 위치를 조사

=> 위치 갱신 리스너를 이용해야 한다.

=> 위치 정보 제공자를 가지고 requestLocationUpdates(String 위치정보제공자, long minTime, float minDistance, LocationListener listener[, liiper liiper]); 마지막 looper는 화면 갱신을 하고자 할 때 사용 - listener 안에서 핸들러를 호출해도 된다.

 

requestLicationUpdates(String 위치정보제공자, long minTime, float minDistance, PrndingIntent intent) : 위치정보가 갱신되면 Listener를 호출하는 것이 아니고 intent를 호출한다.

 

=> 더 이상 필요없을 때는 removeUpdates 메소드에 Listener 나 Intent를 대입하면 된다.

 

5. LocationListener

=> 위치 정보 갱신을 처리할 수 있는 리스너 인터페이스

=> onProviderEnabled, onProviderDiasabled, onStatusChanged, onLocationChanged(Location location) : 위치 정보가 갱신되었을 때 호출되는 메소드

위치 정보를 location에 대입되서 넘어온다.

getLatitude, getLongitude, getAltitude, getSpeed, getBearing, getAccuracy, getTimestamp

 

=> 주기적으로 전송되어온 위치정보를 사용할 때 타임스탬프에 유의 해야 한다.

스마트 폰의 위치 정보를 제공자는 변경될 수 있다.

현재는 GPS가 최적의 정보 제공자여서 GPS가 보내온 정보가 전송

이전에는 WIFI가 최적의 정보 제공자여서 정보를 전송

나중에 전송한 정보가 먼저 도착하기도 한다.

이전 정보보다 나중에 생성된 정보는 버려야 한다.

 

6. 위치 정보도 퍼미션이 있어야 하고 동적으로 권한 설정을 해야 한다.

FINE_LOCATION과 COASE_LOCATION이 필요

 

7. 현재 위치를 가져와서 출력하기

1) AndroidManifest.xml 파일에 권한을 설정

=> ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION

 

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

 

 

2) 동적인 권한 설정을 쉽게 하기 위해서 build.gradle에 AutoPermission 라이브러리의 의존성을 설정

 

allprojects{
    repositories{
        maven{url 'https://jitpack.io'}
    }
}
dependencies {
    implementation 'com.github.pedroSG94:AutoPermissions:1.0.3'

 

 

3) 실행가능한 Activity를 추가(LocationActivity)

 

4) 동적인 권한 요청이 필요한 부분을 처리하기 위한 코드 작성

=> Activity 클래스에 동적 권한 요청 인터페이스를 implements

 

=> onCreate 메소드에서 동적 권한 요청 메소드를 호출

 

=> 3개의 메소드를 오버라이딩

 

package com.example.androidmultimedia;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.Toast;

import com.pedro.library.AutoPermissions;
import com.pedro.library.AutoPermissionsListener;

public class LocationActivity extends AppCompatActivity implements AutoPermissionsListener {

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

        AutoPermissions.Companion.loadAllPermissions(this, 101);
    }
    //Activity의 메소드로 권한 요청을 설정했을 때 호출되는 메소드
    @Override
    //requestCode는 권한 요청 할 때 구분하기 위해서 부여한 번호
    //permissions는 요청한 권한의 배열
    //grantResults는 요청한 권한의 허용 여부 배열
    public void onRequestPermissionsResult(
            int requestCode, String permissions[], int [] grantResults){
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //AutoPermissions의 메소드를 호출하도록 설정
        AutoPermissions.Companion.parsePermissions(
                this, requestCode, permissions, this);
    }

    //권한 사용을 거부했을 때 호출되는 메소드
    @Override
    public void onDenied(int requestCode, String[] permissions) {
        Toast.makeText(this, "권한 사용을 거부함", Toast.LENGTH_LONG).show();
    }

    //권한 사용을 허용했을 때 호출되는 메소드
    @Override
    public void onGranted(int requestCode, String[] permissions) {
        Toast.makeText(this, "권한 사용을 허용함", Toast.LENGTH_LONG).show();
    }
}

 

 

5) 레이아웃 수정

=>버튼 1개와 텍스트 뷰 1개 배치

 

<?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=".LocationActivity"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="위치정보 가져오기"
        android:id="@+id/btnLocation"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/lblLocation"/>

</LinearLayout>

 

 

6) Activity 파일에 디자인 뷰를 사용하기 위한 인스턴스 변수 선언

 

private TextView lblLocation;
private Button btnLocation;

 

 

7) Activity 파일의 onCreate 메소드에서 디자안 한 뷰 찾아오기

 

//뷰 찾아오기
lblLocation = findViewById(R.id.lblLocation);
btnLocation = findViewById(R.id.btnLocation);

 

 

8) 위치정보가 갱신되었을때 호출될 리스너를 생성

=> LocationLintener를 implements 해야함

=> 현재 위치의 위도와 경도를 받아서 텍스트 뷰에 출력

 

//위치 정보가 갱신됄 때 호출될 리스너 객체
    class GPSLintener implements LocationListener {

        //위치정보가 변경이 되면 호출되는 메소드
        @Override
        public void onLocationChanged(@NonNull Location location) {
            //위도 경도 가져오기
            double latitude = location.getLatitude();
            double longitude = location.getLongitude();

            //출력
            String msg = String.format("위도:%.6f 경도:%.6f", latitude, longitude);
            lblLocation.setText(msg);
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(@NonNull String provider) {

        }

        @Override
        public void onProviderDisabled(@NonNull String provider) {

        }
    }

 

 

 

9) Activity 파일에 버튼을 눌렀을 때 호출될 메소드를 작성

=>위치 정보 수집을 위한 메소드

 

    //버튼을 눌렀을 때 호출될 메소드

    private void startLocationService(){

        //위치정보 사용 객체를 생성

        LocationManager manager = 

                (LocationManager)getSystemService(

                        Context.LOCATION_SERVICE);

        try{

            //위치정보 제공자를 설정 : 동적 권한 설정이 되어야 함

            //이 코드를 부르는 곳에서 설정 

            Location location = 

                    manager.getLastKnownLocation(

                            LocationManager.GPS_PROVIDER);

            if(location != null){

                //위도와 경도 가져오기

                double latitude = location.getLatitude();

                double longitude = location.getLongitude();

 

                //출력

                String msg =

                        String.format("위도:%.6f 경도:%.6f",

                                latitude, longitude);

                lblLocation.setText(msg);

            }

            

            //리스너를 생성

            GPSListener gpsListener = new GPSListener();

            //위치정보가 갱신될 때 gpsListener의 메소드를 호출하도록 설정

            //첫번째 매개변수는 위치 정보 갱신을 위한 정보 제공자를 설정

            //두번째 매개변수는 위치 정보를 측정할 시간 단위

            //세번째 매개변수는 위치 정보를 측정할 거리 단위

            //네번째 매개변수가 호출될 리스너

            manager.requestLocationUpdates(

                    LocationManager.GPS_PROVIDER,

                    10000, 10, gpsListener);

            

        }catch(Exception e){

            Log.e("위치 정보 사용 실패", e.getMessage());

        }

    }

 

10)Activity 파일의 onCreate 메소드에 버튼을 눌렀을 때 처리를 위한 이벤트 핸들러 작성

btnLocation.setOnClickListener(

                new Button.OnClickListener(){

            public void onClick(View view){

                startLocationService();

            }

        });