์ฐธ๊ณ
์๋๋ก์ด๋ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ ์ฑ
http://acornpub.co.kr/book/async-android
๊ฒฐ๋ก
๋ฐฑ๊ทธ๋ผ์ด๋์์ ์์ ํ๊ณ , ์์ ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ์ธ์ฐ๋ ๋๋ก ๋ณด๋ด UI์ ์ถ๋ ฅํ๋ ๋ฐฉ๋ฒ
> ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์์ ์์ ์ Handler๋ฅผ ํตํด ๋ฉ์ธ ์ค๋ ๋๋ก ๋ณด๋ด๊ธฐ
๋ฉ์ธ ์ค๋ ๋์์ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ์์ ๋ณด๋ด๊ธฐ, ํน์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ๋ค๋ฅธ ์ค๋ ๋๋ก ์์ ๋ณด๋ด๊ธฐ
> Handler์ HandlerThread๋ฅผ ์ด์ฉํ ์ ์๋ค.
์ค๋ช
๋ฃจํผ Looper ๋ฑ์ฅ
๋ฃจํผLooper๋ ๋ฌด์์ธ๊ฐ?
์์ํ ๋ฐ๋ณตํ๋ ํด๋์ค. ํ์ ์ถ๊ฐ๋๋ ๋ฉ์์งMessage๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ค๊ฐ Message๋ฅผ dispatchํ๋ค.
๋ฃจํผ ์ค๋ ๋๋ ๋ฌด์์ธ๊ฐ?
์ค๋ ๋ ๋ด๋ถ์์ ๋ฃจํผ๊ฐ ์๊ณ , ๋ฃจํผ์ ์ ์ ๋ฉ์๋์ธ prepare์ loop๋ฅผ ํธ์ถํ๋ค.
ํธ๋ค๋ฌ๋ก ๋ฃจํผ ์ค๋ ๋ ์์ ํ์ ๋ฉ์์ง๋ฅผ ์ถ๊ฐํ๋ค.
ํธ๋ค๋ฌ๋ ๋ฌด์์ธ๊ฐ?
1) ๋ฃจํผ์ ํ์ ๋ฉ์์ง๋ฅผ ์ถ๊ฐํ ์ ์๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค. ๋ฃจํผ์ ํ์ ๋ฉ์์ง๋ฅผ ์ถ๊ฐํ๋ ค๋ฉด ํธ๋ค๋ฌ๋ฅผ ์จ์ผํ๋ค๋ ์๋ฏธ๋ก ์ดํดํจ.
2) ๋ฃจํผ์ ๋ฉ์์ง๋ฅผ dispatchํ ๋ ๋ฉ์์ง ์ฒ๋ฆฌ๋ฅผ ์ํ ์ฝ๋ฐฑ์ ๊ตฌํํ๋ค.
๋ฉ์ธ ์ค๋ ๋๋ ์ค์ ๋ก ๋ฃจํผ ์ค๋ ๋์ด๋ค.
์กํฐ๋นํฐ ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ์์ ๋ฐ์ํ๋ ๋ชจ๋ ์ผ์ ๋ฉ์ธ ๋ฃจํผ๊ฐ ํธ์ถํ dispatchMessage ํธ์ถ์์ ์ผ์ด๋๋ค.
๋ค๋ฅธ ์ค๋ ๋์์ ๋ฉ์์ง๋ฅผ ๋ฉ์ธ ์ค๋ ๋๋ก ๋ณด๋ผ ์ ์๋ค.> ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์ ์์ ์ ๋ฉ์ธ ์ค๋ ๋๋ก ๋ณด๋ผ ์ ์๋ค.
๋ฐฑ๊ทธ๋ผ์ด๋์์ ์์ ํ๊ณ , ์์ ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ์ธ์ฐ๋ ๋๋ก ๋ณด๋ด UI์ ์ถ๋ ฅํ๋ ๋ฐฉ๋ฒ > ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์์ ์์ ์ Handler๋ฅผ ํตํด ๋ฉ์ธ ์ค๋ ๋๋ก ๋ณด๋ด๊ธฐ
1) ๋ฉ์ธ ์ค๋ ๋์ ์ฒ๋ฆฌํ๋ ์์ ์ ์ ์ถํ๋ ํธ๋ค๋ฌ ๋ง๋ค๊ธฐ.
๋ฉ์ธ ๋ฃจํผ ์ธ์คํด์ค์ ๋ํ ์ฐธ์กฐ๋ฅผ ํธ๋ค๋ฌ ์์ฑ์์ ์ ๋ฌํ๊ธฐ.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Handler handler = new Handler(Looper.getMainLooper);
}
2-1) post๋ฅผ ์ด์ฉํ๊ธฐ
handler.post(new Runnable() {
public void run() {
// ๋ฉ์ธ ์ค๋ ๋์์ ์์
์ํ
}
});
handler.postAtFrontOfQueue(new Runnable() {
public void run() {
// ๋ฉ์ธ ์ค๋ ๋์์ ์์
์ํ
// ํ์์ ๋ฌด์๋ณด๋ค๋ ์ฐ์ ์ํ๋ Runnable๋ก, ๊ธฐ์กด ์์
๋ณด๋ค ์์๊ฒ๋ ํ์ ๋งจ ์์ ๊ฒ์ํจ.
}
});
handler.postDelayed(new Runnable() {
public void run() {
//10์ด ์ง์ฐ ํ ๋ฉ์ธ ์ค๋ ๋์์ ์์
์ํ
}
}, TimeUnit.SECONDS.toMillis(10));
post๋ก ๊ฒ์ํ๋ Runnable์ Message์ callback์ผ๋ก ์ฐธ์กฐ๋๊ณ , ์ด Runnable์ด ์ฐธ์กฐ๋ ๋ฉ์์ง๊ฐ ๋ฃจํผ์ ํ์ ์ถ๊ฐ๋๋ค. ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ ๋์ด Runnable์ด ์คํ๋๋ค.
//Handler.java ์
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
//post๋ก ๋ฐ์ Runnable์ Message์ callback์ผ๋ก ์ฐธ์กฐํ๋ค.
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
//์ฌ๊ธฐ๊ฐ ์คํ๋๋ค.
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//post๋ก ๋ฐ์ Runnable์ ์คํ
private static void handleCallback(Message message) {
message.callback.run();
}
2-1-1) ๊ธฐ๋ค๋ฆฌ๋ Runnable ์ทจ์ํ๊ธฐ
Runnable ์ฝ๋ฐฑ์ Queue์์ ์ ๊ฑฐํด์ ๊ธฐ๋ค๋ฆฌ๋ ์์ ์ทจ์๊ฐ ๊ฐ๋ฅํ๋ค. ๋ฌด์์ ์ ๊ฑฐํ ์ง ์ง์ ํ๊ธฐ ์ํด Runnable ์ธ์คํด์ค์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ ์งํ๊ณ ๊ธฐ๋ค๋ฆฌ๋ ์์ ์๋ง ์ทจ์๊ฐ ์ ์ฉ๋๋ค. ์ด๋ฏธ ์คํ ์ค์ธ Runnable์ ์ค์งํ์ง๋ ์๋๋ค.
final Runnable runnable = new Runnable() {
public void run(){
// ... ์์
์ํ
}
}
handler.postDelayed(runnable, TimeUnit.SECONDS.toMilles(10));
...
handler.removeCallbacks(runnable);
2-2) send ์ด์ฉํ๊ธฐ
post๋ก Runnable๋ก ์์ ์ ๋ณด๋ด๋ฉด, ์์ ์ ๋ํ ์ฌ์ ์ ๋ณด๋ฅผ ํธ๋ค๋ฌ๊ฐ ์ ์ ์๋ค. ์์ ์ ๋ํ ์ฌ์ ์ ๋ณด๋ฅผ ์๊ณ ์ ์ ํ๊ฒ ๋ฐ์ํ ์ ์๋๋ก, ํธ๋ค๋ฌ์๊ฒ ๋ฉ์์ง๋ฅผ ๋ณด๋ด๊ณ , ๋ฉ์์ง์ ๋ฐ๋ผ ์ ์ ํ๊ฒ ๋ฐ์ํ๋ ํธ๋ค๋ฌ๋ฅผ ์ ์ํ ์ ์๋ค.
ํธ๋ค๋ฌ๋ฅผ ์์ํ๋ ๋ฐฉ๋ฒ
public static class ReactionHandler extends Handler {
public static final int WOW = 0;
public static final int OOPS = 1;
public static final int AWESOME = 2;
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
case WOW:
sayWord("wow");
break;
case OOPS:
sayWord("oops");
break;
case AWESOME:
sayWord("awesome");
break;
default:
super.handlerMessage(msg);
}
}
private void sayWord(String word) {...}
}
private Handler handler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new ReactionHandler(Looper.getMainLooper());
...
}
Hander.Callack์ ์์ํด callback์ผ๋ก ์กฐํฉํ๋ ๋ฐฉ๋ฒ
public static class ReactionCallback implements Handler.Callback {
public static final int WOW = 0;
public static final int OOPS = 1;
public static final int AWESOME = 2;
@Override
public boolean handleMessage(Message msg) {
switch(msg.what) {
case WOW:
sayWord("wow");
break;
case OOPS:
sayWord("oops");
break;
case AWESOME:
sayWord("awesome");
break;
default:
false;
}
return true;
}
private void sayWord(String word) {...}
}
private Handler handler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler(Looper.getMainLooper(), new ReactionCallback());
...
}
๋ ๋ฐฉ๋ฒ์ ๋ ๋ค ์ฌ์ฉํ ์๋ ์๋ค.
๋ฉ์์ง ๋ณด๋ด๋ ๋ฐฉ๋ฒ
handler.sendEmptyMessage(AWESOME);
// Handler.java ์
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
//sendEmptyMessage๋ก ๋ฐ์ int ๊ฐ์ด Message์ what์ผ๋ก ๋ฃ์ด์ ธ ํ์ ์ถ๊ฐ๋๋ค.
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
๊ธฐ๋ค๋ฆฌ๋ ๋ฉ์์ง ์ทจ์ํ๋ ๋ฐฉ๋ฒ
handler.removeMessage(AWESOME);
์ทจ์ํ๊ธฐ๋ฅผ ์ํ๋ ๋ฉ์์ง์ ์ฐธ์กฐ๋ฅผ ์ ์งํ ํ์๊ฐ ์๋ค. what ๋ณ์๋ก ๋ฉ์์ง๋ฅผ ๊ทธ๋ฅ ์ทจ์ํ ์ ์๋ค.
๋ฉ์์ง ์ ์ก vs Runnable ์ ์ก ๋น๊ต
์คํ ์๊ฐ ์ฐจ์ด : ํจ์จ์ฑ์ ๋ฌธ์
ํธ๋ค๋ฌ๊ฐ ์ํ ๋๋ง๋ค Runnable์ ์๋ก์ด ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ฉด ๊ฐ๋น์ง ์ปฌ๋ ์ ๊ณผ๋ถํ๋ฅผ ์๋ฐํ๊ฒ ๋๋ค.
๋ฉ์์ง ์ ์ก์ application-wide pool์์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฉ์์ง ์ธ์คํด์ค๋ฅผ ๊ณต๊ธํ๋ค.
๊ฐ๋ฐ ์๊ฐ ์ฐจ์ด
Runnable์ ์ฌ์ฉํ๋ฉด Runnable.run ์ฝ๋๋ก ์ฌ์ฉํ๊ธฐ ์ฝ๋ค... ์๊ฐ์ด ์ ๊ฒ ๊ฑธ๋ฆฐ๋ค...๊ณ ์ฑ ์์ ๋งํ๋ ๊ฑฐ ๊ฐ๋ค.
๋ฉ์์ง ์ฌ์ฉ์, ๋จ์ผ ์์น(Handler.handleMessage)์์ ์ค๋น ์์ ์ ์ ์ํด์ผ ํ๋๋ฐ ์๊ฐ์ด ๊ฑธ๋ฆฐ๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ด ํ๋กํ ํ์ ์ด๊ณ , ์๊ท๋ชจ ๋จ๋ฐ์ฑ์ผ๋ก ์ฌ์ฉํ๋ค๋ฉด Runnable ๊ฒ์๊ฐ ๋น ๋ฅด๊ณ ์ฝ๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ ๊ท๋ชจ๊ฐ ์ปค์ง๋ฉด ๋ฉ์์ง ์ ์ก์ด ์ข๋ค๊ณ ํ๋ค. ๋ฉ์์ง๊ฐ ๊ฐ๋น์ง ์ต์ํ๋ฅผ ์ ์งํ๊ณ ์ฑ์ ๋ถ๋๋ฝ๊ฒ ์คํํ๊ธฐ ์ํ ํ๋ซํผ์ผ๋ก ํต์ฉ๋๊ธฐ ๋๋ฌธ์ด๋ผ๊ณ ํ๋ค.
๋ฉ์ธ ์ค๋ ๋์์ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ์์ ๋ณด๋ด๊ธฐ, ํน์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ๋ค๋ฅธ ์ค๋ ๋๋ก ์์ ๋ณด๋ด๊ธฐ > Handler์ HandlerThread๋ฅผ ์ด์ฉํ ์ ์๋ค.
Handler์ HandlerThread์ ๋ฃจํผ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ.
HandlerThread thread = new HandlerThread("bg", Process.THREAD_PRIORITY_BACKGROUND);
thread.start(); // ๋ฐ๋์ ํ์
Handler handler = new Handler(thread.getLooper());
handler.post(new Runnable() {
public void run() {
//...๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ์์
์ํ
}
});
ํน์ ์กํฐ๋นํฐ๋ฅผ ์ํ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ ์ ํ๊ธฐ ์ํด, ์์ ๋์๋ฅผ ๋ง๊ธฐ ์ํด ์กํฐ๋นํฐ์ ์๋ช ์ฃผ๊ธฐ์ ๊ฐ๊น๊ฒ HandlerThread์ ์๋ช ์ฃผ๊ธฐ์๋ ๋ฌถ๊ธฐ
HandlerThread.quit์ ํธ์ถํ๋ ๊ฒ. HandlerThread๋ฅผ ์ข ๋ฃ์์ผ์ ํ์ ์์ ์ ๋ ์ด์ ์ฒ๋ฆฌํ์ง ๋ชปํ๊ฒ ํจ.
API ๋ ๋ฒจ 18์์ ์ถ๊ฐ๋ quitSafety()๋ ์ข ๋ฃํ๊ธฐ ์ ์ HandlerThread๊ฐ ๋จ์์๋ ๋ชจ๋ ์์ ์ ์ฒ๋ฆฌํ๋๋ก ํ๋ค. ์ข ๋ฃ๋ ๋ค์์๋ ์ด๋ค ์์ ๋ ๋ฐ์ง ์๋๋ค.
private HandlerThread thread;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstatnceState);
thread = new HandlerThread(...);
}
protected void onPause() {
super.onPause();
if ((thread != null) && (isFinishing())) {
thread.quite();
}
}
Handler๋ฅผ ์ฌ์ฉํ๋ฉด์ ๋์ค๋ ์ด์
์์์ ์ฐธ์กฐ ๋์
๋ฐ์ ์
//์กํฐ๋นํฐ ๋ด๋ถ์์ ํธ๋ค๋ฌ ์ฌ์ฉ
final Runnable runnable = new Runnable() {
public void run() {
// ...์์
์ํ
}
};
handler.postDelayed(runnable, TimeUnit.SECONDS.toMillis(10));
์กํฐ๋นํฐ๊ฐ 10์ด ์์ ๋๋๋ฉด Runnable์ ๊ฐ๋น์ง๋ฅผ ์์งํ ์ ์๋ค. Runnable์ ์์์ ์ฐธ์กฐ๋ ๋ผ์ด๋ธ ๊ฐ์ฒดlive object๊ฐ ์ฌ์ ํ ์กํฐ๋นํฐ์ ์ ๊ทผํจ์ ์๋ฏธํ๊ธฐ ๋๋ฌธ์ด๋ค.
ํด๊ฒฐ์ฑ
- ๋ฐฉ๋ฒ 1: ๋น์ ์ ๋ด๋ถ ํด๋์ค ์ฌ์ฉ์ ํผํ๋ ๊ฒ์ด๋ค. Runnable์ ์ต์์ ํด๋์ค๋ก ์ ์ธํ๊ฑฐ๋, ์กํฐ๋นํฐ ์๋ธํด๋์ค์์ ์ ์ ํด๋์ค(static)๋ก ์ ์ธํ๋ ๊ฒ. ์ฐธ์กฐ๋ฅผ ๋ช ์์ ์ผ๋ก ๋ง๋๋ ๊ฒ์ด๋ค.
- ๋ฐฉ๋ฒ 2: onPause()์ ๊ฐ์ ์กํฐ๋นํฐ ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ์์ ๊ธฐ๋ค๋ฆฌ๋ ์์ ์ ์ทจ์ํ๊ธฐ
- ๋ฐฉ๋ฒ 3 : HandlerThread ์ธ์คํด์ค์ ๊ฒฝ์ฐ์๋ ์กํฐ๋นํฐ๋ฅผ ์ข ๋ฃํ ๋ ์ฐจํ ์คํ์ ๋ง๊ณ ๊ฐ๋น์ง ์ปฌ๋ ์ ์ ์ํด Runnable ๊ฐ์ฒด์ ๋ฉ์์ง ๊ฐ์ฒด๋ฅผ ํ์ด์ฃผ๋ quit์ ๋ฐ๋์ ํ์ธํ๊ธฐ
๋ช ์์ ์ฐธ์กฐ ๋์
์ฌ์ฉ์ ์ธํฐํ์ด์ค์ ์ํธ์์ฉํ๋ ์ค์, ๋ทฐ ๊ณ์ธต์ ๊ฐ์ฒด๋ก ์ธํ ์ฐธ์กฐ๋ก ์ฐธ์กฐ ๋์๊ฐ ์ผ์ด๋ ์ ์๋ค.
๋ฐ์ ์
static class MyRunnable implements Runnable {
private View view;
public MyRunnable(View view) {
this.view = view;
}
public void run() {
// ... ๋ทฐ์ ํจ๊ป ์์
์ํ
}
}
๋ทฐ์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ ์งํ๊ธฐ ๋๋ฌธ์ Runnable์ด ๋ทฐ๋ณด๋ค ๋ ์ค๋ ์์กดํ๋ค๋ฉด ์ ์ฌ์ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋์์ด ๋๋ค.
ํด๊ฒฐ์ฑ
- ๋ฐฉ๋ฒ 1 : ์ฝํ ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํ๋, ์ฐธ์กฐ๋ ๋ทฐ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ์ ๋์ ํ์ธํ๋ค.
static class MyRunnable implements Runnable {
private WeakReference<View> view;
public MyRunnable(View view) {
this.view = new WeakReference<View>(view);
}
public void run() {
View v = view.get(); // ๋์ ๋ฐํํ ์๋ ์์
if(v != null) {
// ... ๋ทฐ์ ํจ๊ป ์์
์ํ
}
}
}
- ๋ฐฉ๋ฒ 2 : ๋ฉ์์ง ์ ์ก์ ์ด์ฉํ๋ค๋ฉด, onResume๊ณผ onPause์์ ํธ๋ค๋ฌ๋ฅผ ๋ถ์ด๊ณ ๋ผ๋ด๊ธฐ
private static class MyHandler extends Handler {
private TextView view;
public void attach(TextView view) {
this.view = view;
}
publci void detach() {
view = null;
}
@Override
public void handleMessage(Message msg) {
//...
}
}
'๋น ๊ตฌ๋ฉ ์ฑ์ฐ๊ธฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] RecyclerView ํ๋จ ํจ๋ฉ์ "android:clipToPadding" (0) | 2023.07.20 |
---|---|
[GIT] ๊น ํ๋ก์ฐ: git-flow & github-flow & gitlab-flow (0) | 2023.06.26 |
[Git] ํ์ ํ๋ก์ ํธ ๊ด๋ฆฌ ์ ์ธ์์ผ ํ GIT ๊ท์น (0) | 2023.06.23 |
[Android][๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ] Android ํ๋ก๊ทธ๋๋ฐ์์ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ํ์์ฑ (0) | 2023.06.22 |
[GitLab] SSH ํค ๋ฑ๋กํ๊ธฐ (0) | 2023.06.19 |