**안드로이드의 ANR(Application Not Responding)
=> Activity가 사용자의 이벤트에 반응하지 못하는 현상
=> 안드로이드에서는 일정 시간동안 사용자의 이벤트에 반응하지 못하면 시스템이 Activity를 종료시킴
=> 서버에 처리를 요청한 경우 오래 걸리면 이런 현상이 벌어짐
- 서버에서는 빠르게 처리하지만 실제 안드로이드 기기까지 결과가 도착하는데 시간이 오래걸려 발생하는 현상
- 스마트폰은 무선 사용(무선은 접속에 시간이 많이 소요)하고, 움직이며 사용하므로 네트워크 상황 장담 불가
=> 스마트폰 API에서 이런 작업은 스레드를 이용하여 처리하는 것을 권장
=> 네트워크 작업은 스레드를 이용하지 않을 경우 작업을 수행하지 않고, 예외 발생
- iOS의 경우 강제는 아니지만 일정시간 반응하지 않을경우 앱 자체를 reject 시켜버림
**Thread
=> Process : 실행중인 프로그램(자원 공유 X)
- 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개 배치
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="@+id/txtdisplay"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="클릭"
android:id="@+id/btnstart" />
</LinearLayout>
3) MainActivity.java 파일의 onCreate메소드에 작성
//뷰 찾아오기
txtdisplay = (TextView)findViewById(
R.id.txtdisplay);
btnstart = (Button)findViewById(
R.id.btnstart);
//스레드를 사용하지 않고 작업을 수행
//스레드를 사용하지 않고 화면 갱신하는 작업을 하면
//작업이 모두 종료되고 화면 갱신이 수행됩니다.
int val = 0;
for(int i=0; i<20; i=i+1){
try{
txtdisplay.setText(val++ + "");
Thread.sleep(1000);
}catch(Exception e){}
}
4) 실행
=> 스레드를 이용하지 않아서 20초가 지나야만 화면에 UI가 출력
4.onCreate 에서 20초 동안 수행했던 작업을 스레드를 이용해서 수행하도록 코드를 변경하고 실행
//스레드 생성
Thread th = new Thread(){
//스레드로 수행할 내용을 작성하는 메소드
public void run(){
int val = 0;
for(int i=0; i<20; i=i+1){
try{
txtdisplay.setText(val++ + "");
Log.e("val" , val + "");
Thread.sleep(1000);
}catch(Exception e){}
}
}
};
//스레드 시작
th.start();
=>실행시 20초를 기다리지 않고 UI는 출력되었지만 작업 내용을 화면에 출력불가(Main thread 외에는 화면을 갱신불가)
**안드로이드에서 스레드를 이용한 화면 갱신
=> 일반 스레드로는 화면 갱신을 할 수 없음
=> 스레드가 작업을 전부 수행한 후 Main Thread에게 화면 갱신을 요청하는 신호를 보내야 함
=> 안드로이드는 Handler, AsyncTask, Lopper 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); : 두번째 매개변수 시간에 수행
- 핸들러.sendMessageDelayes(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이라는 메소드를 이용하여 리턴 받아 사용해도됨
**실습
=> 이전 예제를 핸들러를 이용하여 주기적으로 값을 갱신
5. onCreate 메소드에서 스레드를 생성
final Handler handler = new Handler();
//스레드 생성
Thread th = new Thread(){
//스레드로 수행할 내용 작성 메소드
public void run(){
for(int i=0; i<20; i+=1){
try {
//별도의 스레드에서 직접 출력하는 것은 안됨
//txtdisplay.setText(i + "");
//메시지 객체 생성
Message message = new Message();
//메시지에 데이터를 저장
message.what = i;
handler.sendMessage(message);
Thread.sleep(500);
}catch (Exception e){}
}
}
};
//스레드 시작
th.start();
6. Activity클래스에 핸들러 객체를 생성
//핸들러 객체 생성
final Handler handler = new Handler(Looper.getMainLooper()){
//핸들러에게 메시지를 보내면 이 메소드가 호출되어 메인스레드에게 작업 처리 요청
//이 메소드에서 데이터 출력을
@Override
public void handleMessage(Message msg){
//전송된 데이터 가져오기
int data = msg.what;
//텍스트 뷰에 출력
txtdisplay.setText(data + "");
}
};
=> Thread에서 데이터를 만들고 Thread가 Handler를 호출하여 데이터를 출력
**PostMessage
=> SendMessage 메소드는 Message를 매개변수로 받아서 메시지 큐에 저장하고 순서대로 처리
=> post메소드는 Runnable 인터페이스의 객체를 매개변수로 받아서 다른 작업이 없으면 Runnable의 내용을 수행
=> 이 경우에는 Runnable 인터페이스의 run메소드에 UI갱신 코드를 작성해도 됨
1. handler의 handleMessage 메소드를 삭제
2. Thread 생성 코드 수정
public void run(){
for(int i=0; i<20; i+=1){
try {
//별도의 스레드에서 직접 출력하는 것은 안됨
//txtdisplay.setText(i + "");
//메시지 객체 생성
Message message = new Message();
//메시지에 데이터를 저장
message.what = i;
//handler.sendMessage(message);
//다른 작업이 없을 때 처리하도록 전송
handler.post(new Runnable() {
@Override
public void run() {
txtdisplay.setText(val++ + "");
}
});
Thread.sleep(500);
}catch (Exception e){}
}
}
**작업 스케쥴링
=> 일정한 시간 후나 정해진 시간에 작업을 수행하도록 하는 것
- sendMessageAtTime(Message msg, long uptimeMillis);
- sendMessageDelayed(Message msg, long delayMillis);
- postAtTime(Runnable r, long uptimeMillis);
- postDelayed(Runnable r, 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개 선언
=> 대화상자와 대화상자가 없어졌는지 확인 할 변수
//대화상자
ProgressDialog progressDialog;
//대화상자가 표시중인지 여부를 저장할 변수
boolean isQuit;
//대화상자의 값으로 사용할 변수
int value;
2. onCreate 메소드에서 버튼의 클릭 이벤트 작성
//버튼의 클릭이벤트 처리
btnstart.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View view) {
value = 0;
//대화상자 만들기
progressDialog = new ProgressDialog(
MainActivity.this
);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.show();
isQuit = false;
progressHandler.sendEmptyMessage(0);
}
});
3. Activity 클래스에 핸들러 작성
**스레드와 핸들러 사용
Handler 핸들러 = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg){
//핸들러가 수행할 내용
//msg를 이용하여 전달된 데이터를 저장
//데이터를 출력
}
};
Thread 스레드 = new Thread(){
@Override
public void run(){
//스레드가 수행할 내용
//데이터 가져오기
//데이터 파싱
//핸들러 호출
Message 메시지 = new Message();
메시지.? = epdlxj;
핸들러.sendMessage(메시지);
//화면에 무엇인가를 출력하는 것은 안됨
}
};
=> 버튼을 누르면 https://www.naver.com의 내용을 읽어서 텍스트뷰에 출력하기
- 다운로드 받는 코드는 스레드에 작성해야 하고, 다운로드 받은 코드를 출력하는 핸들러를 이용
1. 실행가능한 Activity 생성
2. 화면 디자인
=> 버튼 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"
android:orientation="vertical"
tools:context=".DownloadActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/naverdownload"
android:text="네이버"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/naverhtml"/>
</LinearLayout>
3. Activity.java 파일에 뷰를 위한 인스턴스 변수 생성
public class DownloadActivity extends AppCompatActivity {
private Button naverdownload;
private TextView naverdisplay;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
naverdownload = (Button)findViewById(R.id.naverdownload);
naverdisplay = (TextView)findViewById(R.id.naverdisplay);
}
}
4. Activity.java 파일에 스레드와 핸들러 작성
final Handler handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message obj){
//데이터 읽어오기
String html = (String)obj.obj;
//데이터 출력
naverhtml.setText((html));
}
};
final Thread th = new Thread(){
@Override
public void run(){
//핸들러에게 전달할 데이터를 저장할 객체
Message message = new Message();
//데이터 가져오기
try {
URL url = new URL("https//www.naver.com");
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 html = sb.toString();
//데이터 저장
message.obj = html;
br.close();
con.disconnect();
}catch (Exception e) {}
handler.sendMessage(message);
}
};
5. Activity.java 파일의 onCreate 메소드에서 버튼 클릭시 스레드를 시작하는 코드 작성
naverdownload = (Button)findViewById(R.id.naverdownload);
naverhtml = (TextView)findViewById(R.id.naverhtml);
naverdownload.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View view) {
th.start();
}
});
6. AndroidManifest.xml 파일에 인터넷 사용 권한을 추가
<uses-permission android:name="android.permission.INTERNET"/>
**AsyncTask
=> Thread와 Handler의 역할을 합친 클래스
=> 이 클래스를 이용한 작업은 짧은 시간의 작업에만 추천, 장시간 작업은 Thread(Runnable or Callable) + Handler 조합을 추천
1. 클래스 생성
- class 클래스 extend 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개, PrograssBar 1개, Button 2개
<?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"
android:orientation="vertical"
tools:context=".AsyncActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="스레드 상태"
android:id="@+id/state"/>
<ProgressBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="스레드 시작"
android:id="@+id/threadstart"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="스레드 중지"
android:id="@+id/threadcancel"/>
</LinearLayout>
</LinearLayout>
3) Activity.java 파일에 스레드로 작업 수행, 작업 중이나 종료시 UI를 갱신할 수 있는 AsyncTask 클래스 생성하고 인스턴스 변수 선언
private TextView state;
private ProgressBar progress;
private Button threadstart;
private Button threadcancel;
//프로그래스의 값을 저장할 변수
int value;
//비동기적으로 실행하는 클래스를 생성
//첫번째 제너릭은 백그라운드 작업을 위한 doInBackground()의 매개변수
//두번째 제너릭은 doInBackground에서 중간중간에 호출하는 publishProgress 함수의 매개변수 자료
//세번째 제너릭은 doInBackground의 리턴 타입으로 onPostExecute()
//메소드의 매개변수 자료형
class BackgroundTask extends AsyncTask<Integer, Integer, Integer>{
//맨 처음 한번만 호출되는 메소드
@Override
public void onPreExecute(){
//초기화 작업 수행
value = 0;
progress.setProgress(value);
}
//백그라운드에서 작업하는 메소드
@Override
//execute 메소드를 호출할 때 대입하는 값이 values에 대입
protected Integer doInBackground(Integer... integers) {
while(isCancelled() == false){
//value의 값을 1씩 증가 - 100이 되면 중지
//100이 안되면 publishProgress를 호출하여 UI를 갱신
Random rand = new Random();
value += rand.nextInt(10);
if(value >= 100)
break;
else {
//이 메소드 호출시 onProgressUpdate가 호출
//진행율을 표시할 수 있게 해줌
publishProgress(value);
}
try{
Thread.sleep(300);
}catch (Exception e){}
}
return value;
}
@Override
//doInBackground에서 publishProgress를 호출하면
//호출되는 메소드 - 주기적인 UI 갱신
public void onProgressUpdate(Integer ... values){
//프로그래스 바 설정
progress.setProgress((values[0]));
state.setText("값: " + values[0]);
}
@Override
//doInBackground 수행이 정상 종료되었을 때 호출되는 메소드
//매개변수는 doInBackground의 리턴 값
public void onPostExecute(Integer result){
progress.setProgress(0);
state.setText("스레드 정상 종료");
}
@Override
//정상 종료가 되지 않고 강제 종료가 되었을 때 호출되는 메소드
public void onCancelled(){
progress.setProgress(0);
state.setText("스레드 강제 종료");
}
}
//AsyncTask 변수
private BackgroundTask task;
4) onCreate 메소드에 뷰를 찾아오고 버튼을 눌렀을 때 이벤트 처리를 수행
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async);
state = (TextView)findViewById(R.id.state);
progress = (ProgressBar) findViewById(R.id.progress);
threadstart = (Button) findViewById(R.id.threadstart);
threadstart.setOnClickListener(new Button.OnClickListener(){
public void onClick(View view){
//AsyncTask 객체를 생성하고 시작
//BackgroundTask의 onPreExecute() -> doInBackground(100)으로 호출
task = new BackgroundTask();
task.execute(100);
}
});
threadcancel = (Button)findViewById(R.id.threadcancel);
threadcancel.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View view) {
//AsyncTask 중지
task.cancel(true);
}
});
}
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) 레이아웃 작성
<?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"
android:orientation="horizontal"
tools:context=".LooperActivity">
<ListView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textAlignment="center"
android:id="@+id/lv_left"/>
<ListView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textAlignment="center"
android:id="@+id/lv_right"/>
</LinearLayout>
3) Activity 파일에 필요한 인스턴스 변수 선언
//뷰 객체
private ListView lv_left;
private ListView lv_right;
//ListView는 MVC 패턴을 구현하기 위해서
//List와 ListAdapter를 이용하여 출력
//ListView에 출력할 데이터 변수
ArrayList<String> leftlv, rightlv;
//List와 ListView를 연결해 줄 컨트롤러 변수
ArrayAdapter<String> leftAdapter, rightAdapter;
//핸들러 변수
Handler handler;
//화면을 갱신하는 스레드
class OneThread extends Thread{
//핸들러를 인스턴스 변수로 만든이유는 다른 스레드에서
// 이 핸들러에 메시지 전달하기 위해
Handler oneHandler;
public void run(){
Looper.prepare();
oneHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg){
//안드로이드에서 예외처리없이 대기
SystemClock.sleep(500);
//anonymous class에서 사용하기 위해 final 변수로 변환
//못고치게 하는 게 목적이면 final int DATA로 표기
final int data = msg.arg1;
if(msg.what == 0){
handler.post(new Runnable() {
@Override
public void run() {
rightlv.add("right: " + data);
//리스트 뷰를 재출력
rightAdapter.notifyDataSetChanged();
}
});
}else{
handler.post(new Runnable() {
@Override
public void run() {
leftlv.add("left: " + data);
//리스트 뷰를 재출력
leftAdapter.notifyDataSetChanged();
}
});
}
}
};
Looper.loop();
}
}
//위의 스레드에 대한 변수 생성
OneThread oneThread;
TwoThread twoThread;
//데이터를 생성해주는 스레드
//0.1초마다 랜덤한 정수를 생성하여 OneThread의 oneHandler에게 메시지를 전송하는 스레드
class TwoThread extends Thread{
public void run(){
Random rand = new Random();
for(int i=0; i<10; i +=1){
int data = rand.nextInt(100);
SystemClock.sleep(100);
//UI갱신을 위해 핸들러에게 메시지를 전송
Message message = new Message();
//what은 구분하기 위해서 주로 사용
if(data % 2 == 0)
message.what = 0;
else
message.what = 1;
//arg는 데이터 전달을 위해 주로 사용됨
message.arg1 = data;
message.arg2 = i;
//핸들러에게 메시지를 전송
oneThread.oneHandler.sendMessage(message);
}
}
}
4) onCreate 메소드에 스레드 객체들을 생성하고 실행하는 코드 작성
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_looper);
lv_left = (ListView)findViewById(R.id.lv_left);
lv_right = (ListView)findViewById(R.id.lv_right);
//뷰에 연결할 데이터를 생성 - Model
leftlv = new ArrayList<>();
rightlv = new ArrayList<>();
//뷰와 모델 연결
leftAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, leftlv);
rightAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, rightlv);
lv_left.setAdapter(leftAdapter);
lv_right.setAdapter(rightAdapter);
//핸들러와 스레드 객체를 생성하고 시작
handler = new Handler();
oneThread = new OneThread();
twoThread = new TwoThread();
oneThread.start();
twoThread.start();
}
**비동기적으로 실행하고 그 결과를 가지고 UI 갱신하는 방법
1. Thread(비동기적 실행) + Handler(UI갱신)
2. AsyncTask(내부의 메소드들을 이용해서 비동기적 실행과 UI 갱신을 수행)
3. Looper(스레드, 핸들러를 만들어 그 안에 별도의 메시지 큐를 만들어 사용)
=> 아주 많은 스레드가 별도로 동작해야 하는 경우 사용 - 게임
Tip!
1. 파일 입출력시 FileInputStream -> BufferedInputStream이 좋음
- FileInputStream -> FilePrintStream(모아서 작업)
2. 동시수행, 데드락, 생산자와 소비자 문제 개념 이해
3. 데몬스레드 - 다른 스레드가 동작중일 때만 동작(누군가에게 종속적으로 만드는 경우)
'수업 정리' 카테고리의 다른 글
75~76일차 수업 정리(Oracle&MyBatis) (0) | 2020.07.23 |
---|---|
75일차 수업정리(Android - Socket) (0) | 2020.07.22 |
73일차 수업 정리(Android - 파일 입출력, SQLite) (0) | 2020.07.20 |
72일차 수업정리(Android - Timer, Animation, LandScope) (0) | 2020.07.17 |
71일차 수업정리(Android - Toast, Dialog, EventHandling) (0) | 2020.07.16 |