안드로이드&IOS 앱 개발자 양성

안드로이드&IOS 앱 개발자 양성(74일차)

HRuler 2020. 7. 22. 09:31
더보기

** 안드로이드의 ANR(Application Not Responding)

** Thread

** Android에서의 Thread : GUI 프로그램은 모두 유사
1. 자바 API를 이용해서 스레드를 생성하는 방법
2. 일반 메소드에서 출력하는 코드는 마지막에 모아서 처리
3. 실습 : 별도의 스레드 생성없이 onCreate에서 1초마다 출력하는 작업을 10번 수행
4. onCreate에서 20초 동안 수행했던 작업을 스레드를 이용해서 수행하도록 코드를 변경하고 실행

** 안드로이드에서 스레드를 이용한 화면 갱신

** Handler
1. Mesage 클래스
2. 핸들러에게 메시지를 전달하는 메소드
3. 핸들러를 호출만 하는 메소드
4. send 대신에 post를 사용하면 다른 메시지가 전부 처리된 후에 처리해달라는 요청
5. Handler 객체와 Message 객체

** 실습
1. 실행 가능한 Activity를 생성(HandlerActivity)
2. 화면 디자인은 이전과 동일하게 작성
3. HandlerActivity.java 파일에 뷰에 대한 참조 변수를 생성
4. HandlerActivity.java 파일의 onCreate 메소드에 뷰를 찾아오는 코드를 작성
5. onCreate 메소드에서 스레드를 생성
6. HandlerActivity 클래스에 핸들러 객체를 생성

** PostMessage
1. handler의 handlerMessage 메소드를 삭제
2. 스레드 생성 코드를 수정

** 작업 스케쥴링

** 진행 상황 출력
1. HandlerActivity에 인스턴스 2개 선언
2. onCreate 메소드에서 버튼의 클릭 이벤트 작성
3. Activity 클래스에 핸들러 작성

** 스레드와 핸들러 사용
1. 실행 가능한 Activity 생성
2. 화면 디자인
3. Activity.java 파일에 뷰를 위한 인스턴스 변수를 생성
4. Activity.java 파일에 스레드와 핸들러를 작성
5. Activity.java 파일의 onCreate 메소드에서 버튼을 클릭하면 스레드를 시작하는 코드를 작성
6. AndroidManifest.xml 파일에 인터넷 사용 권한을 추가

** AsyncTask
1. 클래스 생성
2. 재정의하는 메소드
3. 객체 생성 및 실행
4. 실습

** Looper
1. 사용 방법
2. 주의할 점
3. 사용
4. Looper 실습

** 비동기적으로 실행하고 그 결과를 가지고 UI 갱신하는 방법
1. Thread(비동기적 실행) + Handler(UI 갱신)
2. AsyncTask(내부의 메소드들을 이용해서 비동기적 실행과 UI 갱신을 수행)
3. Looper(스레드를 만들고 핸들러를 만들어서 그 안에 별도의 메시지 큐를 만들어서 사용)이용 : 아주 많은 스레드가 별도로 동작해야 하는 경우 사용 - 게임

** 안드로이드의 ANR(Application Not Responding)

- Activity가 사용자의 이벤트에 반응하지 못하는 현상

- 안드로이드에서는 일정 시간동안 사용자의 이벤트에 반응하지 못하면 시스템이 Activity를 종료시켜 버린다.

- 서버에 처리를 요청을 한 경우 오래 걸리면 이런 현상이 벌어진다.

  서버에서는 빠르게 처리를 하지만 실제 안드로이드 폰 까지 그 결과가 도착하는데 오래 걸리는 경우가 발생해서 벌어지는 현상

  스마트 폰은 무선을 사용하는데 무선은 접속을 하는데 시간이 오래 걸리며 움직이면서 사용하기 때문에 네트워크 상황을 장담할 수 없다.

- 스마트 폰 API에서는 이런 작업은 스레드를 이용해서 처리하는 것을 권장한다.

- 네트워크 작업은 스레드를 이용하지 않으면 작업을 수행하지 않고 예외를 발생시킨다.

  iOS는 강제는 아니지만 일정 시간동안 반응하지 않으면 앱 자체를 reject시켜 버린다.

 

** Thread

- Process: 실행 중인 프로그램, Process끼리는 자원을 공유할 수 없음, Process는 하나의 Process가 종료 되어야만 다른 Process에게 제어권이 넘어간다.

- Thread: Process 안에 존재하는 자원 할당 및 실행의 단위이다

  실행 중에 제어권을 다른 Thread에게 넘길 수 있다.

  사용 중인 자원을 다른 스레드가 수정하게 되면 문제가 발생한다.

- 2개 이상의 스레드를 만들어서 실행 시키는 경우 공유 자원 문제에 신경 써야 한다.

- 동시 수정, 생산자와 소비자 문제, 데드락 문제에 대한 개념과 해결책을 아는 것이 중요하다.

- 데몬 스레드의 개념

 

 

** Android 에서의 Thread : GUI 프로그램은 모두 유사

- Android App 이 실행되면 운영체제는 App을 하나의 스레드를 할당해서 App을 실행 - 이 스레드를 main thread 라고 하는데 GUI 프로그램에서는 이 thread 만이 UI 변경을 할 수 있어서 UI Thread라고도 한다.

- main thread 이외의 thread에서 UI를 변경할 수 있게 되면 main thread가 스레드에 안전하지 않게 되서 자원의 공유 문제가 발생한다.

- Android 에서는 메인 스레드 이외의 스레드에서 UI를 갱신해도 에러가 발생하지 않는 경우도 있다.

  이러한 코드는 운영체제 버전 별로 다르게 동작한다.

- 안드로이드에서는 자바 기반의 스레드를 사용하는 것이 가능하다.

1. 자바 API를 이용해서 스레드를 생성하는 방법

- Runnable 인터페이스를 이용하여 생성하는 방법

- Thread 클래스를 이용하여 생성하는 방법

- Callable 인터페이스를 이용하여 생성하는 방법 : 스레드가 수행을 종료하고 데이터를 리턴할 수 있다.

2. 일반 메소드에서 출력하는 코드는 마지막에 모아서 처리

- onCreate 메소드에서 오랜 시간이 걸리는 작업을 스레드 없이 수행하게 되면 작업이 끝날 때 까지 화면을 출력하지를 못한다.

  onCreate에서 오랜 시간이 걸리는 작업을 할 때는 반드시 스레드로 처리를 해야 한다.

3. 실습 : 별도의 스레드 생성없이 onCreate에서 1초마다 출력하는 작업을 10번 수행

1) Application 생성

2) activity_main.xml 파일에 화면 디자인

=>TextView 1개와 버튼 1개 배치

3) MainActivity.java 파일의 onCreate 메소드에 작성

4) 실행

- 스레드를 이용하지 않아서 20초가 지나야만 화면에 UI가 출력됩니다.

 

4. onCreate 에서 20초 동안 수행했던 작업을 스레드를 이용해서 수행하도록 코드를 변경하고 실행

- 실행을 하면 20초를 기다리지 않고 UI는 출력되었지만 작업 내용을 화면에 출력하는 것은 안됩니다.

  Main thread를 제외하고는 화면을 갱신할 수 없습니다.

 

** 안드로이드에서 스레드를 이용한 화면 갱신

- 일반 스레드로는 화면 갱신을 할 수 없다.

- 스레드가 작업을 전부 수행한 후 Main Thread에게 화면 갱신을 요청하는 신호를 보내야 한다.

- 안드로이드에서는 Handler, AsyncTask, Looper 3가지 방법을 이용해서 Main Thread의 Message Queue에 명령을 전달해서 화면 갱신을 할 수 있다.

- Thread + Handler, AsyncTask, Looper 형태로 사용한다.

 

** Handler

- 스레드 간에 메시지나 Runnable 객체를 주고 받는 클래스

- 하나의 스레드와 같이 사용한다.

- 스레드가 메시지를 보내면 public void handleMessage(Message msg) 메소드가 호출된다.

1. Message 클래스

  int what : 동일한 메시지를 사용하는 핸들러를 구분하기 위한 값을 저장

  int arg1

  int arg2

  Object obj

- 위의 4개는 전부 데이터를 전달하기 위한 속성

- obj에 대입한 경우는 형 변환해서 사용해야 한다.

  Messenger replyTo: 메시지에 대한 응답을 받을 객체를 지정

2. 핸들러에게 메시지를 전달하는 메소드

  핸들러.sendMessage(Message msg); : 메시지 큐에 저장해서 실행 - 다른 메시지가 있으면 처리하고 수행

  핸들러.sendAtFrontOfQueue(Message msg); : 메시지 큐의 첫번째 저장해서 실행 - 다른 메시지 존재 여부에 상관없이 바로 수행

  핸들러.sendMessageAtTime(Message msg, long uptimeMillis); : 두번째 매개변수 시간에 수행

  핸들러.sendMessageDelayed(Message msg, long delayMillis); : 현재 시간에서 두번째 매개변수만큼 지난 다음에 수행

3. 핸들러를 호출만 하는 메소드

  핸들러.sendEmptyMessage(int what);

4. send 대신에 post를 사용하면 다른 메시지가 전부 처리된 후에 처리해달라는 요청

5. Handler 객체와 Message 객체

- Handler 객체는 Anonymous로 만드는 public void handleMessage 메소드를 오버라이딩 해야 한다.

  전에는 default constructor를 이용했는데 최근 API에서는 경고가 발생한다.

  Looper를 대입해서 생성한다.

- Message 객체는 default constructor로 직접 생성해도 되고 Handler의 obtain이라는 메소드를 이용해서 리턴받아서 사용해도 된다.

 

** 실습

- 이전 예제를 핸들러를 이용해서 주기적으로 값을 갱신

1. 실행 가능한 Activity를 생성(HandlerActivity)

2. 화면 디자인은 이전과 동일하게 작성

- 텍스트 뷰 1개와 버튼 1개 배치

3. HandlerActivity.java 파일에 뷰에 대한 참조 변수를 생성

4. HandlerActivity.java 파일의 onCreate 메소드에 뷰를 찾아오는 코드를 작성

5. onCreate 메소드에서 스레드를 생성

 

6. HandlerActivity 클래스에 핸들러 객체를 생성

- Thread에서 데이터를 만들고 Thread가 Handler를 호출해서 데이터를 출력

 

** PostMessage

- sendMessage 메소드는 Message를 매개변수로 받아서 메시지 큐에 저장하고 순서대로 처리를 한다.

- post 메소드는 Runnable 인터페이스의 객체를 매개변수로 받아서 다른 작업이 없으면 Runnable의 내용을 수행한다.

- 이 경우에는 Runnable 인터페이스의 run 메소드에 UI 갱신 코드를 작성해도 된다.

1. handler의 handleMessage 메소드를 삭제

2. 스레드 생성 코드를 수정

 

** 작업 스케쥴링

- 일정한 시간 후나 정해진 시간에 작업을 수행하도록 하는 것

   sendMessageAtTime(Message msg, long uptimeMillis);

   sendMessageDelayed(Message msg, long delayMillis);

   postAtTime(Runnable r, long uptimeMillis);

   postDelayed(Runnable, long delayMillis);

 

** 진행 상황 출력

- 스레드에서 오랜 시간이 걸리는 작업을 수행할 때는 작업의 진행 상황을 출력해주는 것이 좋다.

   작업의 진행 상황은 ProgressDialog 나 ProgressBar를 이용해서 출력

   안드로이드 8.0 부터는 ProgressDialog가 deprecated

- 대화상자의 구분

   Modal : 화면에 출력이 되면 다른 UI로의 전환이 안됩니다.

   다른 UI를 출력하거나 사용할려면 대화상자를 닫아야 한다.

   Modeless : 화면에 출력이 된 상태에서도 다른 UI로 전환이 가능한 대화상자

- ProgressDialog가 Modal Dialog

   Modal Dialog는 사용자와의 인터럭션이 좋지 않다고 한다.

   8.0이후에서는 ProgressDialog를 사용하지 않고 ProgressBar를 사용하는 것을 권장

 

- 버튼을 눌러서 ProgressDialog 출력해서 진행률을 표시

1. HandlerActivity에 인스턴스 2개 선언

- 대화상자와 대화상자가 없어졌는지 확인할 변수

2. onCreate 메소드에서 버튼의 클릭 이벤트 작성

3. Activity 클래스에 핸들러 작성

 

** 스레드 와 핸들러 사용

   Handler 핸들러 = new Handler(Looper.getMainLooper()){

 

   @Override

   public void handleMessage(Message msg){

   //핸들러가 수행할 내용

   //msg를 이용해서 전달된 데이터를 저장

 

   //데이터를 출력

 

   }

 

   };

 

   Thread 스레드 = new Thread(){

   @Override

   public void run(){

   //스레드가 수행할 내용

 

   //데이터 가져오기

 

   //데이터 파싱 - 핸들러에서 해도 됨

   //핸들러 호출

   Message 메시지 = new Message();

   메시지.? = 데이터;

   핸들러.sendMessage(메시지);

 

   //화면에 무엇인가를 출력하는 것은 안됩니다.

   }

   };

 

- 버튼을 누르면 https://www.naver.com의 내용을 읽어서 텍스트 뷰에 출력하기

   다운로드 받는 코드는 스레드에 작성해야 하고 다운로드 받은 코드를 출력하는 것은 핸들러를 이용해야 합니다.

 

1. 실행 가능한 Activity 생성

2. 화면 디자인

- 버튼 1개와 텍스트 뷰 1개

3. Activity.java 파일에 뷰를 위한 인스턴스 변수를 생성

4. Activity.java 파일에 스레드와 핸들러를 작성

5. Activity.java 파일의 onCreate 메소드에서 버튼을 클릭하면 스레드를 시작하는 코드를 작성

 

6. AndroidManifest.xml 파일에 인터넷 사용 권한을 추가

 

** AsyncTask

- Thread 와 Handler의 역할을 합친 클래스

- 이 클래스를 이용한 작업은 짧은 시간이 걸리는 작업에만 추천하고 긴 시간이 걸리는 작업은 Thread(Runnable 이나 Callable) + Handler 조합으로 하는 것을 추천

1. 클래스 생성

   class 클래스 extends AsyncTask<T1, T2, T3> 으로 생성

1) T1은 백그라운드 작업을 위한 doInBackground() 메소드의 매개변수 자료형

2) T2는 백그라운드 작업을 위한 doInBackground() 메소드에서 발생한 데이터를 publishProgress() 메소드를 이용해서 전달하는데 이 때 전달하는 데이터의 자료형

3) T3는 doInBackground()의 리턴 타입이면서 onPostExecute()의 매개변수 자료형

2. 재정의하는 메소드

1) doInBackground(): 필수

- 스레드로 동작할 코드를 작성

2) onPreExecute()

- doInBackground가 호출되기 전에 실행되는 메소드

- 프로그래스 바나 대화상자를 출력하고 데이터를 초기화

3) onProgressUpdate()

- doInBackground에서 publishProgress()를 호출하면 호출되는 메소드

- 주기적으로 호출해서 프로그래스 바나 대화상자를 업데이트

4) onPostExecute()

- doInBackground의 실행 종료 후 호출되는 메소드로 doInBackground의 리턴 값을 매개변수로 받는다.

5) onCancelled(): doInBackground() 수행 중 작업이 취소되면 호출되는 메소드

6) doInBackground를 제외하고는 전부 메인 스레드에서 수행됩니다.

- doInBackground를 제외하고는 UI를 갱신해도 됩니다.

3. 객체 생성 및 실행

   new 클래스(매개변수).execute(매개변수);

4. 실습

- 2개의 버튼과 1개의 ProgressBar를 배치해서 버튼을 누르면 ProgressBar의 값을 순차적으로 증가시키고 다른 버튼을 누르면 작업을 중지하도록 하기

- 이 작업을 Thread + Handler 조합으로 해도 됩니다.

1) 실행 가능한 Activity 추가

 

2) 레이아웃 설정

- TextView 1개, ProgressBar 1개, Button 2개

3) Activity.java 파일에 스레드로 작업을 수행하고 작업 도중 또는 작업이 끝날 때 UI를 갱신할 수 있는 AsyncTask 클래스 생성를 생성하고 인스턴스 변수 선언

4) onCreate 메소드에 뷰를 찾아오고 버튼을 눌렀을 때 이벤트 처리를 수행

5) Thread 와 Handler를 이용해서 구현한 경우에 중지

- Thread 클래스의 run 메소드에 InterruptedException이 발생하면 return하도록 만들고 스레드를 중지시키고자 하면 스레드 객체가 interrupt()를 호출하면 된다.

- daemon 스레드가 아닌 스레드는 자신의 수행코드를 전부 수행하고 종료된다.

- 앱은 화면에서 제거되었는데 스레드는 계속 작업하는 경우가 발생할 수 있습니다.

Thread th = new Thread(){

public void run(){

try{

 

}catch(InterruptedException e){

return;

}catch(Exception e){

 

}

}

}

 

** Looper

- 애플리케이션 내의 MessageQueue를 감시하고 있다가 필요한 경우 메시지를 추출하고 추출한 메시지를 핸들러의 handleMessage 메소드를 호출해서 전달하는 역할을 수행하는 객체

- 안드로이드 애플리케이션에는 내부적으로 1개의 Looper가 할당

   화면 갱신을 하고자 할 때는 미리 할당된 Looper를 이용

- 개발자가 만든 스레드끼리 통신을 하고자 할 때는 별도의 Looper를 생성해서 수행해야 한다.

1. 사용

- 스레드 내부에서 Looper.prepare 라는 메소드를 호출해서 준비하고 Looper.loop()라는 메소드를 호출해서 구동 시킨다.

2. 주의할 점

- Looper는 무한 루프로 반복해서 수행되기 때문에 반드시 quit()를 호출해서 종료시켜 주어야 한다.

- Activity가 파괴될 때(onDestroy) 메소드에서 루프를 포함한 핸들러.getLooper().quit()를 호출한다.

3. 사용

- 이전에는 Thread와 Handler를 별도로 만들어서 Thread에서 핸들러에게 메시지를 보내는 구조를 사용했지만 Thread 안에 Handler를 만들고 핸들러 객체를 생성하기 전에 Looper.prepare()를 호출하고 핸들러 작성을 끝내고 Looper가 loop()를 호출하는 형태로 만든다.

4. Looper 실습

- 랜덤하게 정수를 10개 생성해서 2개의 ListView에 출력

   1초에 1개씩 생성해서 출력

1) 실행 가능한 Activity를 생성

2) 레이아웃을 작성

- ListView를 세로로 2개 배치 - 50%의 영역을 차지

3) Activity 파일에 필요한 인스턴스 변수 선언

4) onCreate 메소드에 스레드 객체들을 생성하고 실행하는 코드를 작성

 

** 비동기적으로 실행하고 그 결과를 가지고 UI 갱신하는 방법

1. Thread(비동기적 실행) + Handler(UI 갱신)

2.AsyncTask(내부의 메소드들을 이용해서 비동기적 실행과 UI 갱신을 수행)

3.Looper(스레드를 만들고 핸들러를 만들어서 그 안에 별도의 메시지 큐를 만들어서 사용)이용 : 아주 많은 스레드가 별도로 동작해야 하는 경우 사용 - 게임