# ANR이란?
Activity에서 사용자 이벤트가 발생하고 5초 이내에 처리하지 못할 경우
안드로이드 시스템이 액티비티를 강제 종료하는 것을 Application Not Responding이라고 한다.
주로 오래 걸리는 작업 결과를 이용해 화면 UI를 수정하려고 할 때 발생하며,
특히 네트워크가 안 좋은 환경에서 통신을 수행할 때 자주 발생한다.
# ANR 해결방법?
오래 걸릴 가능성이 있는 작업은
Activity에서 처리하지 말고, 별도의 스레드를 만들어서 병렬로 처리하는 것이다.
이렇게하면 데이터 수신 등의 오래걸리는 작업은 별도의 스레드에서 돌아가므로
액티비티는 사용자 이벤트에 5초 이내에 반응할 수 있게 되어
ANR이 발생하지 않게 된다.
# 안드로이드의 스레드(Thread)
개발자가 신경써야할 스레드는 대략 아래의 2개가 있다.
1. UI스레드(메인 스레드): 안드로이드 시스템이 액티비티를 활성화시키고 화면을 출력하는 스레드
2. 개발자 스레드: 개발자가 별도로 만든 스레드
주의할 점은 "개발자 스레드"에서는 액티비티의 View에 접근이 불가하다는 것이다.
액티비티의 화면을 조작할 수 있는 것은 오직 안드로이드 시스템이 생성한 UI스레드(메인스레드) 뿐이다.
예를 들어 아래와 같이 Thread를 만들어 사용하면,
컴파일 시에는 아무런 오류가 뜨지 않고 앱의 설치도 잘 되지만,
앱을 실제로 구동하여 해당 작업을 수행하기 시작하면 에러가 발생한다(이런걸 런타임 에러라고 한다.)
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++){
// textView의 값을 수정 -> 런타임 에러 발생
textView.setText("현재 i 의 값: " + i);
try {Thread.sleep(1000);} catch (InterruptedException e) { }
}
}
}
따라서 ANR 해결의 핵심은 아래와 같다.
1. 오래 걸리는 작업은 "개발자 스레드"로 수행한다.
2. 액티비티 화면의 UI 수정은 "UI스레드"로 수행한다.
그렇다면,
"개발자 스레드"에서 작업한 결과를 화면에 반영하기 위해
"UI스레드"에게 요청하는 방법은 무엇일까?
# UI스레드에 화면 작업 요청하는 방법
1. Handler 클래스
Hanler 클래스의 post(), sendMessage() 메서드 등을 활용하여 UI스레드에게 화면 작업을 요청할 수 있다.
2. AsyncTask 클래스 상속
AsyckTake를 상속받는 클래스를 생성하여, 아래의 메서드를 오버라이딩 하고,
해당 클래스의 객체를 생성하여 객체.execute() 를 호출해주면 된다.
- doInBackground()
: 오래 걸리는 작업 수행하며 화면 뷰에 접근할 수 없다. 아래의 2가지 방식을 데이터를 전달한다.
1) publishProgress(데이터) : onProgressUpdate() 에 데이터를 넣어 호출한다.
2) return 데이터; onPostExecute()에 데이터를 넣어 호출한다.
- onProgressUpdate()
: doInBackground()메서드에서 publishProgress() 호출시 콜백되는 메서드로 화면 뷰에 접근할 수 있다.
- onPostExecute()
: doInBackground()메서드에서 return시 콜백되는 메서드로 화면 뷰에 접근할 수 있다.
위의 두 클래스에 대한 자세한 사용 방법은 이 글의 범위를 넘어가므로 생략하며,
AsyncTask를 사용한 Java 샘플 코드만 첨부한다.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView textViewResult;
private Button buttonAsync;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textViewResult = findViewById(R.id.textViewResult);
Button buttonAsync = findViewById(R.id.buttonAsync);
buttonAsync.setOnClickListener(this);
}
// 버튼 클릭 이벤트
@Override
public void onClick(View view) {
AsyncTaskTest asyncTaskTest = new AsyncTaskTest();
asyncTaskTest.execute();
}
// AsyncTask 상속 클래스 생성
class AsyncTaskTest extends AsyncTask<Void, Integer, String>{
@Override
protected String doInBackground(Void... voids) {
for (int i = 0; i < 5; i++){
// 1초마다 i값을 전달 to onProgressUpdate()
publishProgress(i);
try{Thread.sleep(1000);}catch(Exception e){}
}
// 모든 작업이 끝나면 문자열 반환 to onPostExecute()
return "숫자 세기 끝!";
}
@Override
protected void onProgressUpdate(Integer... updateValues) {
super.onProgressUpdate(updateValues);
// doInBackground()에서 보낸 값을 textView에 설정
textViewResult.setText(String.valueOf(updateValues[0]));
}
@Override
protected void onPostExecute(String returnValue) {
super.onPostExecute(returnValue);
// doInBackground()에서 반환한 값을 textView에 설정
textViewResult.setText(returnValue);
}
}
}
- 실행 결과 (레이아웃 XML파일에 TextView, Button을 추가해둔 상태다)
'개발(Development) > Android(안드로이드)' 카테고리의 다른 글
[안드로이드] Constraint Layout의 chain weight(비율) 설정 View 배치 방법 (0) | 2021.07.19 |
---|---|
[안드로이드 Kotlin] 뷰바인딩(View Binding) 설정 및 사용 방법 (0) | 2021.07.19 |
[안드로이드 Java/Kotlin] 시스템 효과음 재생 방법: Ringtone (0) | 2021.07.15 |
[안드로이드 Java/Kotlin]시스템 기본 제공 진동패턴 사용 방법: createPredefined (0) | 2021.07.15 |
[안드로이드 Java/Kotlin] 커스텀 진동 패턴 및 반복 설정 방법: createWaveform (0) | 2021.07.15 |
댓글