Handler사용 방법 1

Code April·2022년 6월 25일
0

안드로이드

목록 보기
4/5

하나의 작업스레드(Worker Thread)을 생성하여 메인 스레드와의
메세지 처리 및 작업스레드 내에서 메시지 처리 방법을 예제를 통해
확인해 보겠습니다.

쓰레드간의 의사 전달은 메시지를 통해서 이루어 지며 메세지는 타입(what)과
내용(object)로 구성 된다. 이렇게 전달된 메세지는 각 스레드의 메시지큐
에 저장되며, 스레드의 루퍼는 메시지큐의 내용을 꺼내서 핸들러에게 수행을
위임 또는 POST형태로 전달된 runnalbe메세지는 바로 수행을 시킨다.
바로 이 내용이 쓰레드,루퍼,핸들러,메시지,메시지큐에 대한 설명이며
이 내용들이 코드로 구현 된다.

위 화면은 세게의 버튼을 가지고 있고 다음과 같은 것을 해보려고 한다.

  1. MAIN TO WORKER
    메인 스레드에서 작업 스레드로 메시지를 전달 하고, 작업 스레드에서는
    해당 메시지를 처리한 후, 다시 메인 스레드의 "전달된 메세지 내용"
    TextView를 업데이트 하기 위한 메세지를 다시 전송 한다.

  2. WORKER TO MAIN
    메인 스레드에서 작업 스레드로 메시지를 전달 하고, 작업 스레드에서는
    해당 메시지를 처리한 후, 다시 메인 스레드의 "전달된 메세지 내용"
    TextView를 업데이트 하기 위해 post형태로 runnalbe을 실행

  3. WORKER TO WORKER
    메인 스레드에서 작업 스레드로 메시지를 전달 하고, 작업 스레드에서는
    해당 메시지를 처리한 후,다시 작업 스레드내의 메시지큐에 메시지를
    전달하여 한번 더 작업스레드에서 메시지를 처리하고, TextView를
    업데이트 하기 위한 메세지를 다시 전송 한다.

이제 소스 형태로 추가 설명을 할텐데, 먼제 스레드에서 루퍼 핸들러를
통한 코드 방식은 다음과 같다

아래 예제의 루프는 무한 루프인가??
그건 해당 스레드에 메시지큐에 있는 메시지 갯수에 종속적이라고
보면 되겠지^^. 그리고 여러번 말했지만,구글이 이렇게 하라고
가이드를 주었기 때문에, 그런 부분은 "ㅇㅋ"하고 따라가면 될듯..

class LooperThread extends Thread {
  public Handler mHandler;

  public void run()
  {
    Looper.prepare();

    mHandler = new Handler() {
      public void handleMessage(Message msg){
        //process incoming message here
      }
    };
    Looper.loop();
  }	
}
public class MainActivity extends AppCompatActivity {
 //메인 스레드에서 직업스레드 및 핸들러 선언
 //메인 스레드는 루프를 디폴트로 내장하고 있으므로 루퍼생성 불필요
 private SimpleWorker mSimpleThread;
 public Handler mainHandler;
 
 //Widget 데이터 멤버
 TextView request_view;      //결과표시
 Button btn_main_to_worker;  //MAIN TO WORKER버튼
 Button btn_worker_to_main;  //WORKER TO MAIN버튼
 Button btn_worker_to_worker; //WORKER TO WORKER버튼
    
 @Override
  protected void onCreate(Bundle savedInstanceState) {
  
  btn_main_to_worker = findViewById(R.id.btn_mtow);
  btn_worker_to_main = findViewById(R.id.btn_wtom);
  btn_worker_to_worker = findViewById(R.id.btn_wtow);
  request_view = findViewById(R.id.requestText);
  
  //main스레드용 핸들러 생성
  //핸들러는 자신을 생성한 스레드에 자동 바인딩 된다.
  //handleMessage을 항상 override해야 한다.
  mainHandler = new Handler(Looper.myLooper()){
  @Override
   public void handleMessage(@NonNull Message msg) {
     Log.i(TAG,"Main Handler - Handle Message");
     String worker_message = (String)msg.obj;
     doUpdateTextView(worker_message);
   }
  public void doUpdateTextView(String update_string){
      request_view.setText(update_string);
      }
   };
   
  //작업 스레드 생성 및 메인 스레드 핸들 연결  
  mSimpleThread = new SimpleWorker();
  mSimpleThread.setMainHandler(mainHandler);
  mSimpleThread.start();  
  
  //"main to worker"버튼을 선택 했을 때, 이벤트 핸들러
  btn_main_to_worker.setOnClickListener(
  new View.OnClickListener() {
      @Override
      public void onClick(View view) {
      Message msg = 
      mSimpleThread.mhandler.obtainMessage();
      msg.what=0;
      msg.obj=
    "(main)I'm main can you complete your work?";
      msg.sendToTarget();
      }
      });
 }
 
 //"worket to main"버튼을 선택 했을 때.
 //이경우 main은 별도의 데이터 내용을 포함하는
 //obj은 null로 하고, 타입에 해당하는 what만 보낸다.
 btn_worker_to_main.setOnClickListener(new       
    View.OnClickListener() {
    @Override
     public void onClick(View v) {
       Message msg = 
       mSimpleThread.mhandler.obtainMessage();
       msg.what=1;
       msg.obj=null;
       msg.sendToTarget();
       
       }
     });
 
 //worker to worker버튼을 선택 했을 때.
 //작업스레드의 두개의 핸들러가 같이 동작하게 한다.
 //스레드별 루퍼와 메시지큐는 하나지만 핸들러는 여러 개 생성 가능
 btn_worker_to_worker.setOnClickListener(
 new View.OnClickListener() {
      @Override
       public void onClick(View view) {
      Message msg = 
      mSimpleThread.mhandler.obtainMessage();
      msg.what=2;
      msg.obj="(main)Hey!m Handler 
      can you work with another Handler?";
      msg.sendToTarget();
     }
 });
 
 //작업 스레드 클레스 명세
 private static class SimpleWorker extends Thread{
     public Handler mhandler; //작업 스레드용 핸들러
     public Handler nhandler;
     public Handler requestHandler; 
     public TextView mainTextView;
     
//메인스레드에서 작업스레드 생성 후, 메인스레드의 핸들러를
//인자로 전달해서, 작업스레드에서 메인스레드로 메시지 전달 가능
    public void setMainHandler(Handler targetHandler)
    {
     requestHandler = targetHandler;
    }
  
 //메인 스레드의 TextView를 저장시키는 멤버 펑션.
 //작업 스레드에서 메인 스레드의 UI를 직접 제어하면 안되는
 //상화을 보여주기 위해 임의로 만든 멤버 펑션 임
 public void setMainTextView(TextView view)
 {
   mainTextView=view;
   String hint = (String)mainTextView.getHint();
   Log.i(TAG,"Main Worker's TextView Hint:"+hint);
 }

  //나와라 run!
  @Override
  public void run() {
    Looper.prepare();
    
    //바로 이 핸들러 부분이 로직이 들어간다.
    //그리고 HanldeMessage를 당연히 오버라이딩
    mhandler = new Handler(Looper.myLooper()){
      @Override
      public void handleMessage(@NonNull Message msg) {
      if(msg.what==0){
        String main_message = (String)msg.obj;
        handOverToMain(main_message);
      }else if(msg.what==1){
        if(msg.obj==null)
         {
          String main_message = "(main)...I 
          just send msg what!";
          WorkToMain(main_message);
        }
      }else if(msg.what==2){
        //Message을 다른 핸들러에게 전달 한다.(HandOver)
        String main_message = (String)msg.obj;
        HandlerCoWorker(main_message);
       }
     }
     
 //아래 함수에서는 main핸들러에서 전달된 메세지에
 //작업스레드 핸들러 내용을 추가해서, 다시 main스레드에
 //메시지를 전달하고 있다.
 //case1의 경우는 메인스레드의 handleMessage에서 전달된
 //메세지를 처리하여 TextView를 갱신.
 
 //case2와 같은 post방식은 실행 코드를 Runnable형식으로
 //작성해서 main스레드에 전달.main스레드는 해당 메세지를
 //루퍼가 message queue에서 추출하여 핸들러를 통하지 않고
 //바로 실행한다고 보면 될듯 하다.
 //코드는 작업스레드에서 구성되었지만 결국 실행은 main
 //스레드에서 되므로, UI업데이트 작업이 가능 하다.
 //하지만 case2의 경우는 다른 스레드에서 실행될 코드를 미리
 //작성하는 경우이므로, 실행될 스레드의 코드를 알아야될 
 //경우도 발생할 수 있다.
 
 public void handOverToMain(String curr_string){
    String worker_message = "(worker)I'm worker. 
    I'm done your request";
   String return_message = curr_string + "\n" +   
                            worker_message;

//case1 : sendMessaeg Style
//Main의 message handler에 sendMessage형태로 메세지 전송
//                    Message msg = requestHandler.obtainMessage();
//                    msg.what=0;
//                    msg.obj=return_message;
//                    msg.sendToTarget();

  //case2 : post방식 통신
  //post함수를 사용 runnabe객체를 메시지 큐에 전송
  requestHandler.post(new Runnable() {
   @Override
   public void run() {
     mainTextView.setText(return_message);
   }
 });
} ///End of handOverToMain
     
//Start WorkerToMain
public void WorkToMain(String curr_string)
{
  //TO DO Your Work
 String worker_message = "(worker)I'm worker. 
 I'm done work myself";
 
 String return_message = curr_string +
                         "\n" + worker_message;

//case1 : sendMessaeg Style
  Message msg = requestHandler.obtainMessage();
  msg.what=0;
  msg.obj=return_message;
  msg.sendToTarget();
}
//End WorkerToMain
 
//Star HandlerCoWorker
public void HandlerCoWorker(String curr_string)
{
  String worker_message = "(m worker)I'm m Handler. 
  I'll co-work with n Handler";
  
  String return_message = curr_string +
                         "\n" + worker_message;

  //send message to n Handler
 Message msg = nhandler.obtainMessage();
 msg.what = 0;
 msg.obj = return_message;
 msg.sendToTarget();
                }
//End HandlerCoWorker
 
     
}; ///End of m Handler

//Start of n Handler
nhandler = new Handler(Looper.myLooper()){
      @Override
      public void handleMessage(@NonNull Message msg) 
      {
       if(msg.what==0){
         String received_message = (String)msg.obj;
         CoWork_Confirm(received_message);
       }else if(msg.what==1){
        }else if(msg.what==2){
        }
     }

void CoWork_Confirm(String curr_string)
  {
      String worker_message = "(n worker)
      I'm n Handler. I'll co-work with m Handler";
      
      String return_message = curr_string +
                            "\n" + worker_message;

     //send message to main
     Message msg = requestHandler.obtainMessage();
     msg.what = 0;
     msg.obj = return_message;
     msg.sendToTarget();
   }

            }; 
 
//End of n Handler

  
  Looper.loop();
 
 } //end of run
        
 }
 
}
profile
Next Level

0개의 댓글