Thread(스레드)와 Handler(핸들러) 실험
Thread(스레드)와 Handler(핸들러)
실험
ㆍ 이 카테고리에 글을 쓰는게 5달만인것 같네요..
5월 22일날 Thread handler Looper란 제목으로
글을 하나 써놓고.. 아에 까먹고 있었습니다..
최근에 한분이 댓글을 달아주셔서 그때서야 생각난..
그래서 오늘에서야 두번째 글을 쓰게 되었습니다.
이 부분은 사실 바로 개발에 도움되기 보다는 개념적인
부분이라 지금 당장 도움이 되지는 않을 수 있습니다..
어쨋든 오늘은
첫번째 글에서 개념적인
부분에 대해서 설명했었는데
그때 글로만 설명드렸던
부분을 코드로 한번 짜볼까 합니다
이번글도 역시..
저도 공부하면서 쓰는글이라
틀리는 부분이 있을 수 있으니
발견하는 즉시 지적해주시면
감사하겠습니다
작업스레드는 왜 만들어야 하나요?
개념설명할때 이 부분이 있었죠
작업스레드를 만들어야 하는 이유!
그리고 아래와 같은 코드로 예를
들었습니다
btn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
for(int i = 0; i<10000000;i++)
{
for(int j = 0; j<1000;j++)
{
tv1.setText("" + i);
}
}
}
});
메인 스레드에서
위와 같은 작업을
하게 될 시에
앱이 멈추고
조금 지나면
경고창이 뜬다
여기서부터 시작을 해보겠습니다
메인스레드는
한가닥 실이라고 했습니다
그래서 위와 같이
시간이 오래걸리는
작업을 지시했을때
실이 하나이기 때문에
그 작업을 완료할때까지는
사용자가 다른 지시를 내려도
(터치를 하거나 뭐 스크롤을 하거나
다른 버튼을 누르거나)
그 지시를 따를 수가 없습니다
그래서
실에 매듭을 묶어서
두 갈래 길로 만들어야 하죠
여기서는 저 생성시점이
버튼을 눌렀을때가 됩니다
public class MainActivity extends AppCompatActivity
{
TextView tv;
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.tv);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
//작업스레드 생성해줘
startSubThread();
}
});
}
public void startSubThread()
{
//작업스레드 생성(매듭 묶는과정)
MyThread myThread = new MyThread();
myThread.setDaemon(true);
myThread.start();
}
public class MyThread extends Thread
{
@Override
public void run()
{
}
}
}
이제 실이 하나더
생겼으니 이중포문
부분을 작업스레드에
떠넘기면 되겠죠?
public void startSubThread()
{
//작업스레드 생성(매듭 묶는과정)
MyThread myThread = new MyThread();
myThread.setDaemon(true);
myThread.start();
}
public class MyThread extends Thread
{
@Override
public void run()
{
for(int i = 0; i<10000000;i++)
{
for(int j = 0; j<1000;j++)
{
tv.setText("" + i);
}
}
}
}
이렇게 시키면 될까요?
어디서 본 에러메시지 입니다.
첫 글에서 언급된던 내용입니다.
tv.setText("" + i);
이 놈이 문제입니다
작업스레드에서 UI에 접근했을때
위와 같은 에러가 뜹니다.
(왜 그런지는 이전글에서..)
그래서 한번 setText만
주석처리 해보면 에러메시지가
사라집니다
public class MyThread extends Thread
{
@Override
public void run()
{
for(i = 0; i<10000000;i++)
{
for(int j = 0; j<1000;j++)
{
//tv.setText("" + i);
}
}
Log.d("ITPANGPANG","i="+i);
}
}
작업스레드에서
UI변경을 하지 않도록
setText를 주석처리하고
이게 뭐 잘 돌아가나
어쩌나 눈으로 확인해보기
위해 로그로 i값을 한번
찍어보았습니다.
버튼을 누르고
몇 초 뒤에 로그값에
i값으로
10,000,000(천만)이
찍힌 것을 확인 할 수 있었습니다.
위에까지의
작업이
여기까지입니다.
작업스레드 매듭을
하나 묶고
숫자를 세줘!
그 다음 해야할
일은
일을 시켰으니
작업결과를
메인 스레드에
넘겨야 하겠죠?
그래서
메인스레드와 작업스레드가
통신할 수 있도록
핸들러(Handler)를 하나
만들어서 통신해보도록 하겠습니다
public class MainActivity extends AppCompatActivity
{
Button btn;
TextView tv;
int i;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.tv);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
//작업스레드 생성해줘
startSubThread();
}
});
}
public void startSubThread()
{
//작업스레드 생성(매듭 묶는과정)
MyRunnable myRunnable = new MyRunnable();
Thread myThread = new Thread(myRunnable);
myThread.setDaemon(true);
myThread.start();
}
android.os.Handler mainHandler = new android.os.Handler()
{
public void handleMessage(Message msg)
{
if (msg.what == 0)
{
tv.setText("" + i);
}
};
};
public class MyRunnable implements Runnable
{
@Override
public void run()
{
while(i<10000000)
{
i++;
try
{
if(i==10000000)
{
Message msg = Message.obtain();
msg.what = 0;
mainHandler.sendMessage(msg);
}
}
catch (Exception e)
{
}
}
}
}
}
코드가 살짝 길어졌는데
Handelr와 Runnable 부분만 보면
android.os.Handler mainHandler = new android.os.Handler()
{
public void handleMessage(Message msg)
{
if (msg.what == 0)
{
tv.setText("" + i);
}
};
};
이 부분이 중요하겠죠
핸들러는 메인스레드와
통신할 수 있으므로 핸들러에서
TextView에 접근해서 i값을 찍어줍니다
조건이 대신
if(msg.what ==0)
일때 입니다
public class MyRunnable implements Runnable
{
@Override
public void run()
{
while(i<10000000)
{
i++;
try
{
if(i==10000000)
{
Message msg = Message.obtain();
msg.what = 0;
mainHandler.sendMessage(msg);
}
}
catch (Exception e)
{
}
}
}
}
Runnable쪽을 보면
while문을 통해서
i값을 천만까지
올려주는 작업을 합니다
그리고 i값이 천만이 되는순간
Message msg 객체를 생성해서
핸들러에 그 값을 전달합니다
mainHandler.sendMessage(msg);
msg 객체를 보내주면
Handler에서 객체를 받아서
객체의 값이 0인지 확인하고
true면 UI를 변경시켜 줍니다.
1초마다 메인스레드와 작업스레드 통신
이번에는 거의
같은코드이지만
몇줄만 수정해서
1초마다 화면에
숫자를 카운트 하는
코드를 만들어보겠습니다.
public class MainActivity extends AppCompatActivity
{
Button btn;
TextView tv;
int i = 0;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.tv);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
//작업스레드 생성해줘
startSubThread();
}
});
}
public void startSubThread()
{
//작업스레드 생성(매듭 묶는과정)
MyRunnable myRunnable = new MyRunnable();
Thread myThread = new Thread(myRunnable);
myThread.setDaemon(true);
myThread.start();
}
android.os.Handler mainHandler = new android.os.Handler()
{
public void handleMessage(Message msg)
{
if (msg.what == 0)
{
tv.setText("" + i);
}
};
};
public class MyRunnable implements Runnable
{
@Override
public void run()
{
while(true)
{
i++;
Message msg = Message.obtain();
msg.what = 0;
mainHandler.sendMessage(msg);
try
{
Thread.sleep(1000);
}
catch (Exception e)
{
}
}
}
}
}
거의 달라진 것은
없습니다
Thread.sleep(1000);
가 새로 생겼다는 점 정도?
Thread.sleep은
느낌 그대로 1초동안
잠시 재우는 역할을 합니다.
하지만 sleep은 좀 알아둬야
하는 부분이 있습니다.
일단 위에 코드 결과를 먼저 보면
작업스레드가 생성되고
1초마다 핸들러를 통해서
UI를 업데이트 하는 화면입니다.
위에서 sleep에 대해 알아둬야할점
이라고 적었는데.
사실 sleep으로 일정시간을
제어하는것은 정확도가 떨어지는
부분이 있습니다.
디바이스 마다 다를수 있지만
전원버튼을 눌러서 Screen이 off
되었을때 thread.sleep이 정확도 100%
보장을 못합니다.
위에 코드대로라면
10이 찍힌상황에서 화면을 잠시 off하고
60초뒤에 화면을 켰을때 70이란 숫자가
찍혀야 하지만 예상과 다르게
60이라던가 65라던가 68등 다른숫자가
찍힐 가능성이 있습니다.
간단한 코드에서는 sleep을 써도
되지만 정확성을 요구하는 코드에서는
sleep 보다는 System쪽 시간을 건드려야
합니다
(휴대폰이 켜진 시간, 누적시간 등)
예를 들어
SystemClock.elapsedRealtime()
이것을 이용하면 정확한 1초를
계산할 수 있습니다.
(이 부분은 안드로이드-System
카테고리에 예~~전에 한번 썼던
기억이 있네요)
아니면
CountDownTimer를
잘 사용하면
위와 똑같은 결과를
만들 수 있습니다.
이번글은 이정도로
마무리 짓도록 하겠습니다.
깔끔하게 쓰고 싶었는데
뭔가 정리가 안 된 느낌이지만..
다음글에서는 좀 더 다듬어서
오도록 하겠습니다.
'안드로이드(android) > Background(백그라운드)' 카테고리의 다른 글
Thread(스레드)와 Handler(핸들러) 그리고 루퍼(Looper) (7) | 2016.05.22 |
---|