ANDROID2011. 3. 17. 09:27

이벤트 핸들러

먼저, 안드로이드 이벤트 처리에는 크게 다음과 같은 여섯가지 방법이 있습니다.



  1. callback method 재정의

    1. callback 메서드는  View클래스에서 오버라이딩해야 하므로, View를 상속받는 서브 클래스를 생성한다.
    2. super class에서 제공되는 callback 메소드를 재구성한다. (onTouchEvent, onKeyDown, onKeyUp, onTrackballEvent 등)

       

  2. Listener interface 구현 (Listener -특정 이벤트를 처리하는 인터페이스)

    1. View의 inner interface를 상속받는 listner 구현 서브 클래스를 생성한다. ( class TouchListenerclass implements View.OnTouchListener { ~~~ }  )
    2. 리스너 객체를 생성(instance)한다. ( TouchListenerClass TouchListener = new TouchListenerClass(); )
    3. 준비된 리스너 객체를 뷰의 이벤트과 연결한다 ( vw.setOnTouchListener(TouchListener); )
      * 리스너 객체를 등록한 뷰는 모두 같은 리스너를 쓸 수 있는 것임.


  3. Activity가 Listener 구현

    1. Activity 생성 시 View의 inner interface를 상속받음. ( public class HandleEvent extends Activity implements View.OnTouchListner { ~~~ } )
    2. Activity 내부에 멤버 메소드로 핸들러 메서드 재정의. ( public boolean onTouch( View v, MotionEvent event) )
    3. 뷰의 이벤트에 this 연결. ( vw.setOnTouchListener(this) )


  4. View가 Listener 구현

    1. View 생성 시 View의 inner interface를 상속받음. ( protected class MyView extends View implements View.OnTouchListner { ~~~ } )
    2. VIew 내부에 멤버 메소드로 핸들러 메서드 재정의. ( public boolean onTouch( View v, MotionEvent event) )
    3. 뷰의 이벤트에 this 연결. ( vw.setOnTouchListener(this) )


  5. 익명 이너 클래스의  사용 ( 2번 방법에서 응용 -> listener 인터페이스를 상속받는 서브 클래스의 생성과, 메소드 재정의, 인스턴스화를 한번에 한다. )

    1. 익명 이너 클래스 생성
      private View.OnTouchListener TouchListener = new  View.OnTouchListener() {
         public bolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
               Toast.makeTet(HandleEvent.this, "Touch Event Received", Toast.LENGTH_SHORT).show() ;
               return true;
            }
            return false;
         }
      };
    2. 뷰의 이벤트에 리스너 객체 연결. ( vw.setOnTouchListener(TouchListener) )
      * class의 생성없이 인스턴스화 하므로 이 타입의 객체는 딱 하나만 생성할 수 있다.


  6. 익명 이너 클래스의 임시 객체 사용

    1. 별도의 인스턴스화 없이 setOnTouchListener에 바로 인수로 생성
      vw.setOnTouchListener( new View.OnTouchListener() {
            public bolean onTouch(View v, MotionEvent event) {
               if (event.getAction() == MotionEvent.ACTION_DOWN) {
                  Toast.makeTet(HandleEvent.this, "Touch Event Received", Toast.LENGTH_SHORT).show() ;
                  return true;
               }
               return false;
            }
         });
      * 가비지 컬렉터가 적당한 때 수집해주는 것을 이용하여 임시 객체로 만들어 인자로 등록할 때만 쓰는 것.

 

 

 

성능에 대한 고찰 - Multi Tread - Async Task

 UI Thread 만으로 이벤트를 처리하는 것은 성능상 무리가 있다고 합니다. 때문에 멀티 threading을 할 수 밖에 없는데요,
다음은 Painless threading이라는 주제로, 멀티 스레딩시 긴 background 작업에 적합한 AsyncTask를 소개합니다. 




  •  안드로이드 어플리케이션 시작 시에 최초 main thread가 자동으로 생성됩니다. 
    이 main thread는 UI thread라고 부르기도 하며, 각 widget과 그것을 포함한 그리기 이벤트에 적합한 이벤트를 처리하고 상호작용하는 중요한 일을 합니다. 
     그 예로, 당신이 스크린위 버튼을 터치하면 UI thread는 touch event를 widget으로 보내고, widget측은 상태와 요청을 , 이벤트 큐로 보냅니다.  UI thread는  request를 dequeue하고, 위젯에게 그 자신을 다시 그리도록 통지합니다.
     이러한 단일 스레드 모델은 implications을 고려하지 못한 안드로이드 어플리케이션으로, 저조한 performance를 산출합니다. 
     즉, 다른 일들이 일어나기도 전에, 단일 스레드가 긴 작업을 동작하고 있는 것 입니다...( db 쿼리에 대한 네트워크 access와 같은.) 이 때 이 스레드는 block 되고, 전체 User Interface도 그렇습니다. 긴 operation을 진행하는 동안, drawing events등을 포함한 어떠한 이벤트도 먹지 않습니다. ㅠㅠ
     결국 유저 입장에서 거시기 하다는 말임. 더욱이 나쁜것은 UI thread 블록현상이 몇 초 이상 된다는 것입니다.
    (about 5 seconds currently)
     the user is presented with the infamous "application not responding" (ANR) dialog.

 

  • thread를 잘못 사용한 예 ) 
      OnClickListner 속에, 버튼을 누르면 Thread.sleep(2000)이 호출되도록 심플한 application을 작성합니다.
     버튼은 원상복귀 되기 전에 2초동안 눌러진 상태가 되어 있을 것입니다. 이때, 유저는 application이 느리다고 매우 쉽게 자각합니다.
     
     이제 UI thread의 긴 작업을 피해야 된다는 것을 알고 있으니, 이 작업을 위하여 background or worker threads와 같은 특별한 thread를 써야 합니다. 
     다음 예제는 listner를 클릭하면 네트워크에 있는 image를 다운로딩하여 imageView에 출력하는 예입니다.

    1.  public void onClick(View v) {
        new Thread(new Runnable() {
          public void run() {
            Bitmap b = loadImageFromNetwork();
            mImageView.setImageBitmap(b);
          }
        }).start();
      }  

     먼저, 이 코드는 UI thread의 block 문제를 잘 풀어낸 것 처럼 보입니다. 
    허나 불행하게도, 이것은 single thread model을 위반합니다:  the Android UI toolkit is not thread-safe and must always be manipulated on the UI thread 
     이 코드를 보면 ImageView는 worker thread에서 수행하게 되고, 이것은 무시무시한 문제를 가지게 됩니다.
    추적하기 힘들고 고치기 어려운 버그로 시간을 소비하게 됩니다.
     

  • 개선한 예) 
    Android offers several ways to access the UI thread from other threads. You may already be familiar with some of them but here is a comprehensive list:

    Any of these classes and methods could be used to correct our previous code example:

    1.  public void onClick(View v) {
        new Thread(new Runnable() {
          public void run() {
            final Bitmap b = loadImageFromNetwork();
            mImageView.post(new Runnable() {
              public void run() {
                mImageView.setImageBitmap(b);
              }
            });
          }
        }).start();
      }     

     불행히도, 이 클래스와 메소드는 너무 복잡하고 가독성이 떨어집니다. 
    또한 당신의 implemet가 복잡한 수행을 할때 UI update를 너무 자주 요구 할 수 있습니다.
    이러한 문제를 해결하기 위하여 Android 1.5부터 새로운 utility class를 제공하는데 AsyncTask라고 부르며, 매우 간단하고 긴 태스크에 필요한 user interface를 가지고 있습니다.

    ( AsyncTask is also available for Android 1.0 and 1.1 under the name UserTask. It offers the exact same API and all you have to do is copy its source code in your application. )

 


  • AsyncTask의 사용)
    The goal of AsyncTask is to take care of thread management for you. Our previous example can easily be rewritten with AsyncTask:

    1. public void onClick(View v) {
        new DownloadImageTask().execute(http://example.com/image.png);
      }
      private class DownloadImageTask extends AsyncTask {
           protected Bitmap doInBackground(String... urls) {
               return loadImageFromNetwork(urls[0]);
           }
           protected void onPostExecute(Bitmap result) {
               mImageView.setImageBitmap(result);
           }
      }   



    As you can see, AsyncTask must be used by subclassing it. It is also very important to remember that an AsyncTask instance has to be created on the UI thread and can be executed only once. You can read the AsyncTask documentation for a full understanding on how to use this class, but here is a quick overview of how it works:

    In addition to the official documentation, you can read several complex examples in the source code of Shelves (ShelvesActivity.java and AddBookActivity.java) and Photostream (LoginActivity.java, PhotostreamActivity.java and ViewPhotoActivity.java). I highly recommend reading the source code of Shelves to see how to persist tasks across configuration changes and how to cancel them properly when the activity is destroyed.

    Regardless of whether or not you use AsyncTask, always remember these two rules about the single thread model: do not block the UI thread and make sure the Android UI toolkit is only accessed on the UI thread. AsyncTask just makes it easier to do both of these things.

    If you want to learn more cool techniques, come join us at Google I/O. Members of the Android team will be there to give a series of in-depth technical sessions and answer all your questions.

 

 

 

'ANDROID' 카테고리의 다른 글

Android View System  (0) 2011.09.07
[Service Framework] Service Interface & Service Class & Service Proxy  (0) 2011.09.07
proxy 설정  (0) 2011.09.07
성능 향상을 위한 TIP ..  (0) 2011.03.17
몇가지 질의에 대한 답변  (0) 2011.03.17
Posted by BLUE-NOTE