ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SwingWorker 클래스
    개발 2008. 11. 20. 18:19

    파일전송 루틴을 구성한다고 하자.

    파일전송이 되는 것 뿐 아니라 그 상태를 다이얼로그로 보여주어야 한다.

    그래서 진행상태를 ProgressMonitor를 이용한다.

    대략적인 코드는 다음과 같을 것이다.

     

    public void call()

    {

      ProgressMonitor monitor = new ProgressMonitor(this, "Task", "", 0, 100);

      progressMonitor.setProgress(0);

      int i=0;

     

      while(i<=100)

      {

        progressMonitor.setNote("complete "+i+"%");

        progressMonitor.setProgress(i);

        i++;

      }

      monitor.close();

    }

     

    정확히 구동은 되지 않겠지만 대략 위와같이 나올 것이다.

    하지만 실행해보면 화면에 빈 화면만 나올뿐이다.

     

     

    왜 그럴까?

     

    상태바를 띄우긴 했지만 내용이 다이얼로그에 표시되지 않는다.

    대부분의 Swing Component 와 마찬가지로 ProgressMonitor도 쓰레드에 친절하지 않다.

    내용 변경은 다른 쓰레드를 통해 변경시켜주어야 한다.

    이때 편리한 클래스가 SwingWorker 이다.

     

     

    우선, 파일을 다운로드하는 메소드를 만든다.

     

    public void filedownload()

    {

      // 파일전송 다이얼로그를 띄운다. 맥스값은 100이다.

      monitor = new ProgressMonitor(frame, fname, "", 0, 100);
      monitor.setProgress(0);
               

      // 실제로 파일다운로드 루틴을 담당할 Task 객체를 생성하고 리스너를 추가한다.
      task = new Task();
      task.addPropertyChangeListener(this);
      task.execute(); 

    }

     

    위에서 Task 클래스는 실제로 파일다운로드 부분이 구현되며 Task 클래스가 SwingWorker 클래스를 상속받는다.

     

        class Task extends SwingWorker
        {
            @Override

            // 백그라운드 상에서 동작할 작업을 정의힌다. 여기에서는 파일다운로드이다.
            protected Object doInBackground() throws Exception
            {
                try {

                    // 서버 또는 다른 로컬로부터 스트림을 받는다.
                    InputStream in = ...getInputStream()


                    byte[] bytes = new byte[4096];
                    FileOutputStream fos = new FileOutputStream(lpath);  // lpath : 파일을 저장할 위치

                    while(true)
                    {
                        int readlen = in                     if(readlen < 0)
                            break;

                        fos.write(bytes, 0, readlen);
                        fos.flush();

                        downloaded_size += readlen;
                        int per = (int)(downloaded_size*100/total_size);


                        setProgress(per);


                        if(monitor.isCanceled())
                            break;
                    }
                    if(fos != null) fos.close();
                    if(in != null) in.close();

                }catch(FileNotFoundException e){

                    e.printStackTrace();
                }catch (IOException e){
                    e.printStackTrace();
                }
                return null;
            }
           
            @Override
            public void done() {
                // monitor.close() 는 setProgress() 가 최대값을 갱신하면 자동으로 호출된다.
                // monitor.close();
            }
        }

     

    setProgress() 메소드는 SwingWorker 클래스에 변경된 값을 전송한다.

    SwingWoker 클래스는 내부적으로 progress 속성을 가지고 있으며, 이는  setProgress() 메소드로 값을 변경할 수 있다.

    progress 속성의 값이 변경되면 PropertyChangeListener 가 구현된 propertyChange() 메소드가 호출된다.

     

    여기서 잠깐,

    SwingWorker 클래스는 기본적으로 progress, state 두개의 속성을 가지고 있다.

    progress 속성은 1부터 100까지의 범위를 가지며 그 이상의 값에서는 오류를 던진다.

    또, 퍼포먼스 향상을 위해 progress() 가 연속적으로 호출되더라도 마지막으로 호출된 한번의 메소드만이 유효하다.

    무슨말인가하면, 예를 들어

     

    while(i<100){

      setProgress(i);

      i++;

    }

     

    위의 루틴을 호출하면 마지막의 setProgress(99)만 한번 호출이 된다. 연속적인 값들의 변화이기 때문에 이전 값들은 적용하지 않는것이다.

    원하는 값을 얻으려면 다음과 같이 한다.

     

    while(i<100){

      try{

        Thread.sleep(1);

      }catch(InterruptedException e){}

      setProgress(i);

      i++;

    }

     

    루프사이에 텀을 주면 된다. 네트워크상으로 파일전송을 받을 때는 연속적으로 파일을 받는 것이 아니기 때문에

    위처럼 별도로 쓰레드를 두어 텀을 만들 필요는 없다.

     

    이제 propertyChange() 메소드를 살펴보자.

    Task 클래스에서 progress 값이 변경되면 여기에서는 전송상태 다이얼로그의 상태를 변경해준다.

     

     public void propertyChange(PropertyChangeEvent evt)
        {
            if(evt.getPropertyName().equals("progress"))
            {
                Integer per = (Integer)evt.getNewValue();
                monitor.setProgress(per);
                monitor.setNote(String.format("Complete %d%% (%d/%d)Byte", per, downloaded_size, total_size));
               
                if (monitor.isCanceled())
                {
                    task.cancel(true);
                }
            }
        }

     

    위와 같이 코딩이 되면 파일전송 다이얼로그가 빈화면이 아닌 변경된 상태값을 보여줄 수 있다.

    취소버튼을 누르면 task.cancel() 을 호출하여 쓰레드가 종료되어야 함을 알리고,

    Task 클래스의 doInBackground() 에서도 동작을 멈추게 해야한다.

     

     

    이리하면 위와 같은 상태변경이 가능한 다이얼로그를 볼 수 있다.

    반응형

    '개발' 카테고리의 다른 글

    Trac 설치  (0) 2009.02.05
    Google Calendar API 삽질 일기  (1) 2008.12.05
    넷빈 6.1에서 struts2 설치하기  (0) 2008.11.04
    사이트 글자 크기 제어하기  (0) 2008.10.31
    바이트 배열을 16진수 문자열로 변환하기  (0) 2008.10.04

    댓글

Designed by Tistory.