'분류 전체보기'에 해당되는 글 45건

  1. 2012.09.24 TweenAnimation
  2. 2012.09.22 callback 이란
  3. 2012.09.21 AsyncTask
  4. 2012.09.21 Context
  5. 2012.09.21 UI쓰레드와 Handler
  6. 2011.01.28 Microsoft Wireless Mobile Mouse 3500 신묘년 스페셜 에디션 5
  7. 2010.12.11 종료
  8. 2010.12.11 생산자 / 소비자
  9. 2010.12.10 Producer / Customer
  10. 2010.12.10 자체 뮤텍스락 이용
posted by Kyleslab 2012. 9. 24. 16:03

1. 트윈 애니메이션의 종류 및 설명
 
    트윈 애니메이션은 주어진 정보를 이용하여 뷰의 출력 영역을 연산하는 방법을 사용하며 이러한
    방법으로 이동, 회전, 투명화, 크기 변경을 구현할 수 있습니다. 하지만 뷰의 출력에 대한 부분만
    연산하여 출력시키기 때문에 뷰가 화면에 보이지 않더라도 지속적으로 터치 이벤트를 받는 등
    뷰 자체가 애니메이션에 영향을 받지 않으므로 추가적인 처리를 해야할 수도 있습니다.
  
 
    1.1 투명화 적용
 
        트윈 애니메이션으로 투명화를 적용하면 화면에서 뷰가 점점 사라지도록 표현할 수 있고, 반대로
        보이지 않는 뷰가 점점 나타나도록 표현할 수도 있습니다. XML 에서 투명화를 표현할 때 사용하는
        요소 및 속성은 아래와 같습니다.
 
        요소명 - alpha
        속성
            - android:fromAlpha : 애니메이션 시작시 적용될 투명도입니다. 0.0 으로 설정하면 완전한
                                        투명화가 적용되며 1.0 으로 하면 투명도가 적용되지 않습니다.
            - android:toAlpha : 애니메이션 종료시 적용될 투명도입니다. 0.0 으로 설정하면 완전한
                                        투명화가 적용되며 1.0 으로 하면 투명도가 적용되지 않습니다.
 
        투명화 애니메이션은 애니메이션 설정한 시간동안 fromAlpha 속성에 지정한 투명도에서 toAlpha
        속성에 지정한 투명도로 뷰를 변형시킵니다.
 
 
    1.2 크기 변경 ( scale )
  
        크기 변경은 뷰의 크기를 변경하는 것입니다. 시작시 뷰의 크기와 애니메이션이 종료될 때의
        크기를 지정할 수 있으며 크기 변경시 뷰의 어느 위치를 기준으로 변경이 되는지도 설정할 수
        있습니다.
 
        요소명 - scale
        속성
            - android:fromXScale : 애니메이션 시작시 뷰의 너비입니다. 1.0 이 온전한 뷰의 크기를
                        의미하며 1.0 보다 작으면 뷰의 너비가 작아지고, 1.0 보다 크면 뷰의 너비가 커집니다.
            - android:toXScale : 애니메이션 종료시 뷰의 너비입니다. 1.0 이 온전한 뷰의 크기를
                        의미하며 1.0 보다 작으면 뷰의 너비가 작아지고, 1.0 보다 크면 뷰의 너비가 커집니다.
 
            - android:fromYScale : 애니메이션 시작시 뷰의 높이입니다. 1.0 이 온전한 뷰의 크기를
                        의미하며 1.0 보다 작으면 뷰의 너비가 작아지고, 1.0 보다 크면 뷰의 너비가 커집니다.
            - android:toYScale : 애니메이션 종료시 뷰의 높이입니다. 1.0 이 온전한 뷰의 크기를
                        의미하며 1.0 보다 작으면 뷰의 너비가 작아지고, 1.0 보다 크면 뷰의 너비가 커집니다. 
 
            - android:pivotX : 뷰의 크기가 변경될 때 뷰의 위치를 의미합니다. 0% 이면 뷰의 좌측이
                        고정된 상태로 크기가 변경되며 50% 이면 뷰의 중점을 중심으로 뷰의 크기가 변경
                        됩니다. 즉, 뷰가 커질 땐 좌우로 함께 커지고, 작아질때에도 좌우로 줄어듭니다.
            - android:pivotY : 뷰의 크기가 변경될 때 뷰의 위치를 의미합니다. 0% 이면 뷰의 상단이
                        고정된 상태로 크기가 변경되며 50% 이면 뷰의 중점을 중심으로 뷰의 크기가 변경
                        됩니다. 즉, 뷰가 커질 땐 상하로 함께 커지고, 작아질때에도 상하로 줄어듭니다.
            
 
    1.3 이동 ( translate )
 
        두개의 위치 값을 지정하여 시작 위치에서 이동 종료 위치로 뷰를 이동시킵니다.
 
        요소명 - translate
        속성
            - android:fromXDelta : 뷰의 X 좌표상 시작 위치입니다. 수치값을 직접 입력하면 픽셀단위의
                        위치로 판단하여 해당 위치에서 시작하며 퍼센트값을 입력하면 뷰의 너비에서 해당
                        퍼센트를 연산한 위치에서 시작합니다. 또, 퍼센트 뒤에 p 를 붙이면 뷰를 소유한
                        부모 객체의 너비에서 퍼센트를 연산한 위치에서 시작하게 됩니다.
            - android:toXDelta : 뷰의 X 좌표상 이동 종료 위치입니다. 해당 속성이 가질 수 있는 값은
                        fromXDelta 와 같습니다.
            - android:fromYDelta : 뷰의 Y 좌표상 시작 위치입니다. 해당 속성이 가질 수 있는 값은
                        fromXDelta 와 같습니다.
            - android:toYDelta : 뷰의 Y 좌표상 이동 종료 위치입니다. 해당 속성이 가질 수 있는 값은
                        fromXDelta 와 같습니다.
 
 
    1.4 회전 ( rotate )
 
        애니메이션의 시작 시점과 종료 시점의 뷰의 각도, 위치를 지정하여 뷰를 회전 시킵니다.
 
        요소명 - rotate
        속성
            - android:fromDegrees : 애니메이션이 시작될 때 뷰에 적용되는 각도입니다.
            - android:toDegrees : 애니메이션이 종료될 때 뷰에 적용되는 각도입니다.
            - android:pivotX : 회전 애니메이션이 이루어질 X 좌표상의 기준 위치입니다. 이 요소에
                        수치값을 직접 입력하면 픽셀단위의 위치로 판단하여 해당 위치에서 시작하며
                        퍼센트값을 입력하면 뷰의 너비에서 해당 퍼센트를 연산한 위치에서 시작합니다.
                        또, 퍼센트 뒤에 p 를 붙이면 뷰를 소유한 부모 객체의 너비에서 퍼센트를 연산한
                        위치에서 시작하게 됩니다.
            - android:pivoxY : 회전 애니메이션이 이루어질 Y 좌표상의 기준 위치입니다. pivotX 속성과
                        동일하게 값을 설정할 수 있습니다.
   
     
2. set 요소와 부가적인 속성들
   
    XML 형식으로 애니메이션을 정의할 때에는 반드시 set 요소 안에 애니메이션 요소들을 정의해야
    합니다. set 요소는 여러개의 애니메이션을 하나로 그룹짓는 역할을 하며 애니메이션의 효과를
    정의할 수 있는 interpolator 속성을 포함하고 있습니다. 아래의 코드는 이동 애니메이션을 구성하는
    간단한 XML 코드입니다.
  
    <?xml version="1.0" encoding="utf-8"?>
    <set
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@android:anim/bounce_interpolator">

        <translate 
            android:fromYDelta="0%" 
            android:toYDelta="50%"
            android:duration="2000"
            />
    </set>
 
    루트의 set 요소에는 어플리케이션에서 레이아웃을 구성할 때처럼 요소와 속성명들을 해석할 수
    있도록 네임스페이스를 정의해줍니다. set 요소의 interpolator 속성은 뷰의 이동시 어떤 효과를
    부가적으로 표현할지를 정의하는데 set 요소에서 모든 애니메이션에 동일하게 효과를 적용시킬
    수도 있습니다. interpolator 속성으로 정의 가능한 값과 효과는 다음과 같습니다.
  
    @android:anim/accelerate_decelerate_interpolator
 
        애니메이션이 점점 빠르게 동작하다가 점점 천천히 동작합니다.
 
 
    @android:anim/accelerate_interpolator
 
        애니메이션이 점점 빠르게 동작합니다.
 
 
    @android:anim/anticipate_interpolator
 
        애니메이션이 시작할 때 반대 방향으로 동작을 한 후에 본래의 방향으로 동작합니다.
 
 
    @android:anim/anticipate_overshoot_interpolator
 
        애니메이션이 시작할 때 반대 방향으로 동작을 한 후에 종료 지점을 지나갔다가 종료 지점으로
        돌아옵니다. 예를 들어 왼쪽에서 오른쪽으로 이동하는 애니메이션이 있다면 시작할 때 뷰가
        왼쪽으로 조금 움직인 후 오른쪽으로 이동하고, 원래 도착해야할 위치를 지났다가 다시 왼쪽으로
        이동합니다.
 
 
    @android:anim/bounce_interpolator
 
        애니메이션이 종료될 때 튕기면서 스프링 효과를 표현합니다.
 
 
    @android:anim/cycle_interpolator
  
        애니메이션을 동작한 후에 반대 방향으로 한번 더 동작합니다. 만약 오른쪽으로 50 위치까지
        이동하는 애니메이션에 이 효과가 적용되면 오른쪽으로 50 만큼 이동한 후에 왼쪽으로 -50
        위치까지 이동하게됩니다.
       
       
    @android:anim/decelerate_interpolator
 
        애니메이션의 움직임이 점점 느려집니다.
 
 
    @android:anim/linear_interpolator
 
        특별한 효과 없이 일정하게 애니메이션이 동작합니다.
 
 
    @android:anim/overshoot_interpolator
 
        애니메이션이 동작할 때 멈춰야할 위치를 지난 후에 다시 돌아옵니다.
  
 
    set 요소에서는 interpolator 속성과 함께 shareInterpolator 속성을 설정해주어야 합니다.
    shareInterpolator 속성는 interpolator 속성에 설정한 효과를 내부의 다른 애니메이션들이 공유하여
    사용할 것인지 여부를 설정하는 것입니다. 이 속성값은 기본이 true 이며 true 로 설정하면 set 요소
    내의 애니메이션이 interpolator 속성에 설정한 효과로 일괄 적용되며 false 로 설정하면 내부의
    애니메이션에서 설정한 interpolator 속성을 따로따로 적용하게 됩니다.
 
    그래서 애니메이션마다 각기 다른 interpolator 속성을 적용하더라도 set 요소에서 shareInterpolator
    속성에 true 를 지정하면 각자의 애니메이션에서 설정한 효과가 적용되지 않습니다.
 
    또 애니메이션에 startOffset 속성과 duration 속성을 설정할 수 있는데 startOffset 속성은 지정한
    밀리초 시간 후에 애니메이션을 동작하겠다는 것이고, duration 속성은 지정한 밀리초 시간만큼
    애니메이션을 동작하겠다는 것입니다. 기본적으로 set 요소 내부에 정의된 애니메이션은 동시에
    애니메이션이 수행되기 때문에 애니메이션간에 시간 차를 두고싶을 때 startOffset 속성을 사용하며
    duration 속성은 지정하지 않으면 애니메이션이 동작할 수 있는 시간이 설정되지 않는 것이므로
    반드시 지정해주어야합니다.
 
 
3. 간단한 예제 
 
    아래의 코드는 네 종류의 애니메이션을 볼 수 있도록 네개의 버튼을 구성하여 각 버튼을 누를때마다
    하단의 뷰가 동작하도록 구성한 것입니다.
 
    // rotation.xml - 회전
    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@android:anim/cycle_interpolator">
        <rotate
            android:fromDegrees="0"           
            android:toDegrees="-45"           
            android:toYScale="0.0"           
            android:pivotX="0%"           
            android:pivotY="0%"           
            android:duration="1000" 
            />
    </set>
 
    // size.xml - 크기 변경
    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@android:anim/cycle_interpolator">
        <scale
            android:fromXScale="1.0"
            android:toXScale="1.4"
            android:fromYScale="1.0"
            android:toYScale="0.6"
            android:pivotX="50%"        
            android:pivotY="50%"   
            android:duration="1000" 
            />   
    </set>
 
    // transparent.xml - 투명화
    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <alpha
            android:fromAlpha="1.0"
            android:toAlpha="0.5"
            android:duration="1000"
            />
    </set>
 
    // move.xml - 뷰 이동
    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:shareInterpolator="false">
        <translate 
            android:interpolator="@android:anim/bounce_interpolator"
            android:fromYDelta="0%" 
            android:toYDelta="50%"
            android:duration="2000"
            />
    </set>
 
    // mix.xml - 복합 애니메이션
    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:shareInterpolator="false">
        <scale
            android:interpolator="@android:anim/accelerate_decelerate_interpolator"
            android:fromXScale="1.0"
            android:toXScale="0.5"
            android:fromYScale="1.0"
            android:toYScale="0.5"
            android:pivotX="50%"
            android:pivotY="50%"
            android:duration="2000" 
            />
        <alpha
            android:fromAlpha="1.0"
            android:toAlpha="0.5"
            android:duration="2000"
            />
        <set android:interpolator="@android:anim/accelerate_interpolator">
            <scale
                android:fromXScale="0.5"
                android:toXScale="0.0"
                android:fromYScale="0.5"
                android:toYScale="0.0"
                android:pivotX="50%"
                android:pivotY="50%"
                android:startOffset="2000"
                android:duration="2000" 
                />
            <rotate
                android:fromDegrees="0"
                android:toDegrees="-45"
                android:toYScale="0.0"
                android:pivotX="50%"
                android:pivotY="50%"
                android:startOffset="2000"
                android:duration="2000" 
                />
        </set>
    </set>
  
    // 소스 코드 - 버튼을 클릭하여 OnClickListener 인터페이스의 onClick 메소드가 호출되었을 때
    public void onClick(View view)
    {
        int id = view.getId();
        
        if(id == R.id.rotation_btn) {
            // rotation.xml 에 정의된 애니메이션을 불러온다.
            Animation ani = AnimationUtils.loadAnimation(this, R.anim.rotation);
            // 애니메이션을 동작시킨다.
            m_tween_view.startAnimation(ani);         
  
        } else if(id == R.id.size_btn){
            // size.xml 에 정의된 애니메이션을 불러온다.
            Animation ani = AnimationUtils.loadAnimation(this, R.anim.size);
            // 애니메이션을 동작시킨다.
            m_tween_view.startAnimation(ani); 
          
        } else if(id == R.id.transparent_btn) {
            // transparent.xml 에 정의된 애니메이션을 불러온다.
            Animation ani = AnimationUtils.loadAnimation(this, R.anim.transparent);
            // 애니메이션을 동작시킨다.
            m_tween_view.startAnimation(ani); 
          
        } else if(id == R.id.move_btn) {
            // move.xml 에 정의된 애니메이션을 불러온다.
            Animation ani = AnimationUtils.loadAnimation(this, R.anim.move);
            // 애니메이션을 동작시킨다.
            m_tween_view.startAnimation(ani);          
 
        } else if(id == R.id.mix_btn) {
            // mix.xml 에 정의된 애니메이션을 불러온다.
            Animation ani = AnimationUtils.loadAnimation(this, R.anim.mix);
            // 애니메이션을 동작시킨다.
            m_tween_view.startAnimation(ani);           
        }
    }
 

'Mobile > Android' 카테고리의 다른 글

BroadcastReceiver  (0) 2012.11.02
Push에 대한 것!  (0) 2012.10.30
AsyncTask  (0) 2012.09.21
Context  (0) 2012.09.21
UI쓰레드와 Handler  (0) 2012.09.21
posted by Kyleslab 2012. 9. 22. 16:10

호출하는 측과 호출받는 측의 개념으로 이해하세요

보통 call이라함은 사용자 프로세스가 어떤 시스템(주로 운영체제겠지요?)에게 임의의 서비스를 호출하는 것을 의미합니다. 즉, 처리루틴은 시스템에 있고, 사용자 프로세스는 그것을 호출한다는 의미지요.

callback : call에서는 사용자==>시스템 이었는데 이제는 거꾸로 호출해주는 것입니다. 시스템이 사용자의 루틴을 호출해주는것이지요. 호출의 시점이 시스템에서 사용자로 가는것에 유의해주시길. 즉, 사용자 프로그램루틴을 사용자가 호출하는것이 아니라, 이벤트처리루틴등에 의해 시스템이 사용자 프로그램을 호출해 주는 것을 의미합니다.

upcall : call은 보통 상위에서 하위(기반)시스템에게 호출이 일어나는데, 마이크로커널같은 구조에서는 커널에 서비스 루틴이 있지 않고, 사용자 레벨에 이런 서비스루틴이 존재하는 경우가 있습니다. 심지어 메모리 관리자도 사용자 수준에 있지요. 따라서, 다음과 같은 시나리오를 그려볼 수 있습니다.

사용자 =(call)==> 시스템 =(upcall)=>라이브러리커널=(return)=>시스템=(return)=>사용자

posted by Kyleslab 2012. 9. 21. 19:06

전 포스트에서 설명했던 여러 스레드 구현방법들은 비록 아무 문제가 없지만 구현방법이 복잡해서 코드를 읽기 힘들게 만드는 경향이 있었다. Background작업에 관한 모든 사항(스레드 객체 생성, 사용, UI스레드와 통신 등)이 Activity 코드에 포함 되고 특히 background 스레드가 UI위젯과 빈번한 통신을 할수록 Activity 코드의 복잡함은 점점 배가 된다. 

 

안드로이드에서는 이런 문제를 해결하기 위해 API level 3 (1.5 version) 부터 AsyncTask라는 클래스를 제공하고 있다. 


AsyncTask클래스는 background작업을 위한 모든 일(스레드생성, 작업실행, UI와 통신 등)을 추상화 함으로 각각의 background작업을 객체 단위로 구현/관리 할 수 있게 하는것이 목적이다. 그림으로 표현하면 다음과 같다.




참고로 1.0과 1.1 version의 API를 사용하는 디바이스에서는 구글 code에 공개되어 있는 UserTask 라는 클래스를 어플리케이션 프로젝트에 복사해 넣어 사용할 수 있다. 기능과 사용법은 AsyncTask와 완전히 동일하다.

 

그럼 AsyncTask에 관해 자세히 살펴보자.


 

 

1. AsyncTask 클래스 소개

 

AsyncTask라는 클래스 이름은 Asynchronous Task의 줄임이며, UI스레드의 입장에서 볼 때 비동기적으로 작업이 수행되기 때문에 붙여진 이름이다. (Ajax: Asynchronous javascript and XML 에서 사용된 의미와 같다)

 

AsyncTask의 상속관계는 다음과 같다.

 

Object로부터 상속하는 AsyncTask는 Generic Class이기 때문에 사용하고자 하는 type을 지정해야 한다.


AsyncTask클래스의 사용시 지정해야 하는 generic type은 각각 다음의 용도로 사용된다.

  • Params: background작업 시 필요한 data의 type 지정
  • Progress: background 작업 중 진행상황을 표현하는데 사용되는 data를 위한 type 지정
  • Result: background 작업 완료 후 리턴 할 data 의 type 지정

 

 

그림으로 각 generic type이 결정하는 것들을 표현하면 다음과 같다.

(각 메소드의 자세한 정보는 다음 단락의 예제 코드와 설명 참조)

 

 

만약 type을 정할 필요가 없는 generic이 있다면 void를 전달하면 된다.

예. …AsyncTask<void, void, void> {…}

 

 

 

 

2. AsyncTask의 사용

 

우선 AsyncTask가 어떻게 사용되는지 예제 소스를 보자.

AsyncTask 클래스의 사용 예

접기

001package com.holim.test;
002 
003import android.app.Activity;
004import android.os.AsyncTask;
005import android.os.Bundle;
006import android.os.SystemClock;
007import android.view.View;
008import android.widget.Button;
009import android.widget.ProgressBar;
010import android.widget.TextView;
011 
012public class AsyncTaskDemo extends Activity
013                implements View.OnClickListener {
014     
015    ProgressBar progressBar;
016    TextView textResult;
017    Button btnExecuteTask; 
018     
019    /** Called when the activity is first created. */
020    @Override
021    public void onCreate(Bundle savedInstanceState) {
022        super.onCreate(savedInstanceState);
023        setContentView(R.layout.main);
024     
025        progressBar = (ProgressBar)findViewById(R.id.progressBar);
026        textResult = (TextView)findViewById(R.id.textResult);
027        btnExecuteTask = (Button)findViewById(R.id.btnExecuteTask);
028         
029        btnExecuteTask.setOnClickListener(this);
030    }
031     
032    public void onClick(View v) {
033         
034        // AsynchTask를 상속하는 DoComplecatedJob 클래스를 생성하고
035        // execute(...) 명령으로 background작업을 시작함.
036        // (예제에 구현된 AsynchTask는 String 형의 인자를 받음)
037        new DoComplecatedJob().execute("987",
038                                        "1589",
039                                        "687",
040                                        "399",
041                                        "1722",
042                                        "50");     
043    }
044     
045     
046    // AsyncTask클래스는 항상 Subclassing 해서 사용 해야 함.
047    // 사용 자료형은
048    // background 작업에 사용할 data의 자료형: String 형
049    // background 작업 진행 표시를 위해 사용할 인자: Integer형
050    // background 작업의 결과를 표현할 자료형: Long
051    private class DoComplecatedJob extends AsyncTask<String, Integer, Long> {      
052    
053     
054        // 이곳에 포함된 code는 AsyncTask가 execute 되자 마자 UI 스레드에서 실행됨.
055        // 작업 시작을 UI에 표현하거나
056        // background 작업을 위한 ProgressBar를 보여 주는 등의 코드를 작성.
057        @Override
058        protected void onPreExecute() {
059            textResult.setText("Background 작업 시작 ");           
060            super.onPreExecute();
061        }
062 
063        // UI 스레드에서 AsynchTask객체.execute(...) 명령으로 실행되는 callback
064        @Override
065        protected Long doInBackground(String... strData) {
066            long totalTimeSpent = 0;
067             
068            // 가변인자의 갯수 파악 (이 예제에서는 5개)
069            int numberOfParams = strData.length;
070             
071            // 인자들을 이용한 어떤 작업을 처리를 함
072            for(int i=0; i<numberOfParams; i++) {              
073                 
074                // 각 인자를 이용한 복잡한 Task 실행함.
075                // 예제에서는 인자로 전달된 시간만큼 sleep
076                SystemClock.sleep(new Integer(strData[i]));
077                 
078                // background 작업에 걸린시간을 누산해 리턴함
079                totalTimeSpent += new Long(strData[i]);
080                 
081                // onProgressUpdate callback을 호출 해
082                // background작업의 실행경과를 UI에 표현함
083                publishProgress((int)(((i+1)/(float)numberOfParams)*100));
084            }          
085            return totalTimeSpent;
086        }
087         
088        // onInBackground(...)에서 publishProgress(...)를 사용하면
089        // 자동 호출되는 callback으로
090        // 이곳에서 ProgressBar를 증가 시키고, text 정보를 update하는 등의
091        // background 작업 진행 상황을 UI에 표현함.
092        // (예제에서는 UI스레드의 ProgressBar를 update 함)
093        @Override
094        protected void onProgressUpdate(Integer... progress) {
095            progressBar.setProgress(progress[0]);
096        }
097         
098        // onInBackground(...)가 완료되면 자동으로 실행되는 callback
099        // 이곳에서 onInBackground가 리턴한 정보를 UI위젯에 표시 하는 등의 작업을 수행함.
100        // (예제에서는 작업에 걸린 총 시간을 UI위젯 중 TextView에 표시함)
101        @Override
102        protected void onPostExecute(Long result) {
103            textResult.setText("Background 작업에 걸린 총 시간: "
104                            new Long(result).toString()
105                            "m초");   
106        }
107         
108        // AsyncTask.cancel(boolean) 메소드가 true 인자로
109        // 실행되면 호출되는 콜백.
110        // background 작업이 취소될때 꼭 해야될 작업은  여기에 구현.
111        @Override
112        protected void onCancelled() {
113            // TODO Auto-generated method stub
114            super.onCancelled();
115        }      
116    }
117}

접기

 

 

AsyncTask 클래스는 다음과 같이 중요한 callback들을 제공 함으로 상황에 맞게 오버라이딩 해야 한다.

  • protected void onPreExecute(): Background 작업이 시작되자마자 UI스레드에서 실행될 코드를 구현해야 함. (예. background 작업의 시작을 알리는 text표현, background 작업을 위한 ProgressBar popup등)
  • protected abstract Result doInBackground(Params… params): Background에서 수행할 작업을 구현해야 함. execute(…) 메소드에 입력된 인자들을 전달 받음.
  • void onProgressUpdate(Progress... values): publishProgress(…) 메소드 호출의 callback으로 UI스레드에서 보여지는 background 작업 진행 상황을 update하도록 구현함. (예. ProgressBar 증가 등)
  • void onPostExecute(Result result): doInBackground(…)가 리턴하는 값을 바탕으로 UI스레드에 background 작업 결과를 표현하도록 구현 함. (예. background작업을 계산한 복잡한 산술식에 대한 답을 UI 위젯에 표현함 등)
  • void onCancelled(): AsyncTask:cancel(Boolean) 메소드를 사용해 AsyncTask인스턴스의 background작업을 정지 또는 실행금지 시켰을 때 실행되는 callback. background작업의 실행정지에 따른 리소스복구/정리 등이 구현될 수 있다.

 

 

또, AsyncTask 클래스는 background 작업의 시작과 background 작업 중 진행정보의 UI스레드 표현을 위해 다음과 같은 메소드를 제공한다.

  • final AsyncTask<…> execute(Params… params): Background 작업을 시작한다. 꼭 UI스레드에서 호출하여야 함. 가변인자를 받아들임으로 임의의 개수의 인자를 전달할 수 있으며, 인자들은 doInBackground(…) 메소드로 전달된다.
  • final void publishProgress(Progress... values): Background 작업 수행 중 작업의 진행도를 UI 스레드에 전달 함. doInBackground(…)메소드 내부에서만 호출.

 

 

위의 메소드들은 AsyncTask 클래스를 이용해 구현된 background 작업 시 다음과 같은 형태로 사용된다.

 

 

위 의 그림에서 처럼AsyncTask인스턴스는 자기 자신을 pending, running, finished 이렇게 세 가지 상태(status)로 구분하는데 각각 AsyncTask:Status 클래스에 상수 PENDING, RUNNING, FINISHED로 표현 될 수 있다.

현재 AsyncTask인스턴스의 상태는 다음 메소드를 호출해서 얻을 수 있다.


public final AsyncTask.Status getStatus ()

return

AsyncTask인스턴스의 상태정보를 AsyncTask.Status 객체의 상수 값 PENDING, RUNNING, FINISHED 중에서 리턴.

 

 

또, AsyncTask클래스는 background 작업을 정지, 또는 시작금지 시키기 위해 다음 메소드를 제공한다. 이 메소드가 성공적으로 호출되면 onCacelled() callback이 호출되니 onCacelled()에 적절한 뒤처리를 해주어야 한다.


final boolean cancel (boolean mayInterruptIfRunning)

parameter

mayInterruptIfRunning: true값을 제공했을 때 background작업이 실행 중일 경우(running 상태) 작업을 중단 시키고, 준비 중(pending 상태) 일 경우 작업을 실행 금지 시킴. (execute() 명령 사용 불가. 사용하면 exception 발생)

return

true: background작업을 성공적으로 중지하거나 실행 금지 시킴

false: 벌써 작업이 완료된 상태(finished 상태) 일 경우 리턴

 

 

마지막으로 AsyncTask 사용해 background작업을 구현 시 꼭 지켜야 하는 사항들이다.

  • AsyncTask클래스는 항상 subclassing 하여 사용하여야 한다.
  • AsyncTask 인스턴스는 항상 UI 스레드에서 생성한다.
  • AsyncTask:execute(…) 메소드는 항상 UI 스레드에서 호출한다.
  • AsyncTask:execute(…) 메소드는 생성된 AsyncTask 인스턴스 별로 꼭 한번만 사용 가능하다. 같은 인스턴스가 또 execute(…)를 실행하면 exception이 발생하며, 이는 AsyncTask:cancel(…) 메소드에 의해 작업완료 되기 전 취소된 AsyncTask 인스턴스라도 마찬가지이다. 그럼으로 background 작업이 필요할 때마다 new 연산자를 이용해 해당 작업에 대한 AsyncTask 인스턴스를 새로 생성해야 한다.
  • AsyncTask의 callback 함수 onPreExecute(), doInBackground(…), onProgressUpdate(…), onPostExecute(…)는 직접 호출 하면 안 된다. (꼭 callback으로만 사용)

'Mobile > Android' 카테고리의 다른 글

BroadcastReceiver  (0) 2012.11.02
Push에 대한 것!  (0) 2012.10.30
TweenAnimation  (0) 2012.09.24
Context  (0) 2012.09.21
UI쓰레드와 Handler  (0) 2012.09.21
posted by Kyleslab 2012. 9. 21. 16:22


안드로이드에서도 프로세스는 당연히 OS 커널 (리눅스)에서 관리됩니다. 어플리케이션과 프로세스가 별도로 관리되고 있다면, 어플리케이션 정보는 어디에서 관리하고 있을까요? 안드로이드의 시스템 서비스 중 하나인 ActivityManagerService 에서 그 책임을 집니다. 그렇다면 ActivityManagerService 는 어떤식으로 어플리케이션을 관리하고 있을까요? 이외로 단순 합니다. 특정 토큰을 키값으로 'Key-Value' 쌍으로 이루어진 배열을 이용해 현재 작동중인 어플리케이션 정보를 관리합니다.


 거의 결론에 다다른거 같습니다. Context 는 어플리케이션과 관련된 정보에 접근하고자 하거나 어플리케이션과 연관된 시스템 레벨의 함수를 호출하고자 할 때 사용됩니다. 그런데 안드로이드 시스템에서 어플리케이션 정보를 관리하고 있는 것은 시스템이 아닌, ActivityManagerService 라는 일종의 또 다른 어플리케이션입니다. 

안드로이드에서는 어플리케이션과 관련된 정보에 접근하고자 할때는 ActivityManagerService 를 통해야만 합니다. 당연히 정보를 얻고자 하는 어플리케이션이 어떤 어플리케이션인지에 관한 키 값도 필요해집니다.


 즉, 안드로이드 플랫폼상에서의 관점으로 샆펴보면, Context 는 다음과 같은두 가지 역할을 수행하기 때문에 꼭 필요한 존재입니다.

  • 자신이 어떤 어플리케이션을 나타내고 있는지 알려주는 ID 역할 

  • ActivityManagerService 에 접근할 수 있도록 하는 통로 역할 

 정리하자면 이렇습니다.


Activity 와 Service 가 생성될 때 만들어지는 Context 와 BroadcastReceiver 가 호촐될 때( onReceive() ) 전해지는 Context 는 모두 서로다른 인스턴스입니다. 즉, Context 는 어플리케이션이 시작될 때는 물론이요, 어플리케이션 컴포넌트들이 생성될때마다 태어나는 셈입니다. 물론, 새롭게 생성되는 Context 들이 부모와 완전히 독립되어 있는 존재는 아니고 '거의' 비슷한 내용을 담고 있습니다.

< 파생된 Context 인스턴스들은 언제든지 부모 Context 에 접근할 수 있다.>

 어째서 동일한 Context 인스턴스를 어플리케이션 컴포넌트들이 공유해서 사용하지 않고, 모두 서로 다른 (그러나 알고보면 알맹이는 거의 같은) 인스턴스를 만들어 사용하고 있을까요? 음... 어려운 문제입니다. 잘 모르겠네요. 일단 한 가지 분명한 원인이 있습니다.

 Context 의 기능 중, 시스템 API 를 호출하는 기능과 관련되어 한 가지 문제점이 있습니다. 어떤 어플리케이션 컴포넌트가 시스템 API를 호출하느냐에 따라서 서로 다른 결과가 나타나야 한다는 점입니다. 
예를들어, Service 에서 Activity 실행하기 포스트에서 언급한 것 처럼, 동일한 형태로 startActivity 메서드 호출하더라도, 일반적인 Activity 에서는 정상적으로 새로운 Activity 를 시작하게 되지만, Service 에서 호출할 경우에는 예외가 발생합니다. 만일 어플리케이션을 구성하는 Service 와 Activity 가 서로 동일한 Context 인스턴스를 공유하고 있다면 동일한 메서드 호출에 대하여 서로 다른 결과를 나타내도록 구현하지 못했을겁니다.


 따라서, 현재 안드로이드 시스템은 어플리케이션 Context 를 기반으로 컴포넌트를 위한 Context 를 생성할 때 해당 Context 가 어떤 종류의 컴포넌트인지 알 수 있도록 약간의 표시를 해두곤 합니다. 

'Mobile > Android' 카테고리의 다른 글

BroadcastReceiver  (0) 2012.11.02
Push에 대한 것!  (0) 2012.10.30
TweenAnimation  (0) 2012.09.24
AsyncTask  (0) 2012.09.21
UI쓰레드와 Handler  (0) 2012.09.21
posted by Kyleslab 2012. 9. 21. 10:18

안드로이드는 스윙과 마찬가지로 싱글 쓰레드 GUI 모델이 적용되어 있다. 
즉 UI를 그리거나 갱신하는 쓰레드는 하나뿐이라는 것이다. 그 쓰레드는 바로 안드로이드의 주요 컴포넌트들이 실행되는 "main" 쓰레드이다. 모든 UI 관련 코드는 main 쓰레드에서 실행된다.

스윙에서 응답없음(unresponsive) 현상을 막기 위해 백그라운드에서 돌아가는 worker 쓰레드를 만든 것처럼, 안드로이드에서도 오래 걸리는 작업은 UI 쓰레드(= main 쓰레드)에서 처리하지 말고 별도의 쓰레드를 만들어 처리해야 한다. 그렇지 않으면 화면을 갱신하고자 하는 모든 코드는 block 당하여 ANR이 발생할 것이다.

오래 걸리는 작업에는 무엇이 있나?
- 파일 처리
- 네트워크 조회
- 다량의 DB 트랜잭션
- 복잡한 계산

그럼 백그라운드 쓰레드(Worker Thread)를 만드는 방법은?
- UI 쓰레드와 상호작용 없으면 그냥 Thread.start()에서 처리하면 된다.
- 그러나 작업후 결과를 UI에 반영(즉 UI 쓰레드와 통신)해야 한다면 Handler 등을 이용해야 한다.

다른 쓰레드에서 UI 쓰레드에 액세스하는 방법

  • Handler : 일반적인 쓰레드간 상호작용
  • Activity.runOnUiThread(Runnable) : UI 쓰레드의 메시지큐에 작업을 전달(post). 
       activity 인스턴스에 액세스할 수 있는 경우 사용
  • View.post(Runnable) : UI 쓰레드의 메시지큐에 작업을 추가. 해당 view 객체에 접근가능한 경우 사용
  • View.postDelayed(Runnable, long) : 일정시간 후에 작업 처리
  • AsyncTask : 쓰레드나 Handler에 대한 지식없이 백그라운드 작업 후 UI 쓰레드에서 화면갱신 작업을 
       처리할 수 있게 코드를 단순화해주는 유틸리티 클래스
  • AsyncQueryHandler : 비동기적으로 쿼리를 수행할 수 있게 해주는 도우미 클래스
  • NotifyingAsyncQueryHandler
  • WeakAsyncTask

  • Handler는 무엇인가?
    - 쓰레드간 상호작용을 위한 일반적인 목적의 클래스
    - 작업 쓰레드(=자식 쓰레드)에서 부모 쓰레드(=Handler 객체를 생성한 쓰레드)에 Message 및 
      Runnable(부모 쓰레드에서 처리할 작업) 전달(send/post 메소드)
    - 자식 쓰레드에서 handler를 통해 전달되는 Message와 Runnable은 부모 쓰레드의 메시지큐에 들어감
    - 내부적으로 Runnable도 결국은 Message로 변환(Message.callback=runnable)되어 메시지큐에 들어감
    - Handler 객체를 생성한 쓰레드(부모 쓰레드)에서는 Looper를 통해 MessageQueue를 만들어 놓아야 함
    - UI쓰레드(= main쓰레드)는 ActivityThread.main()에 의해 생성되는데, 여기서 Looper를 통해 
      UI 쓰레드용 메시지 큐가 이미 만들어져 있으므로 우리가 UI 쓰레드용 메시지큐를 만들 필요는 없음
    - HandlerThread 클래스는 Looper를 가진 쓰레드를 쉽게 만들기 위한 용도


    * 안드로이드 쓰레드에 대한 'i티거'님의 글 참조

    http://tigerwoods.tistory.com/26 Thread 구현하기1

    http://tigerwoods.tistory.com/28 Thread 구현하기2



    안드로이드 에서는 UI Thread 외부에서 UI 관련 작업을 호출 하면 Exception이 발생한다.

    android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

    이와 같은 경우에는 Activity의 runOnUiThread 를 이용하여 해당 작업을 UI Thread 를 호출해 작업하면 문제를 회피할 수 있다.

    --------------------------------------------------------------
    new Thread(new Runnable() {
        @Override
        public void run() {    
            runOnUiThread(new Runnable(){
                @Override
                 public void run() {
                     // 해당 작업을 처리함
                 }
            });
        }
    }).start();
    --------------------------------------------------------------




    Android Thread 내에서 UI 핸들링

    별거 아니지만 모르고 있으면 큰 낭패를 당하는 내용입니다.

    아래의 Code는 Exception이 발생합니다.

    public class BuggingService extends Service {
    private Timer timer;
    private int counter;
    private TimerTask executeAgain = new TimerTask() {

    @Override
    public void run() {
    Toast.makeText(getApplicationContext(), "I poop on you", Toast.LENGTH_LONG).show();
    }
    };

    Android에서는 Thread내에서는 UI이 변경을 직접적으로 하지 못하게 되어 있습니다. 위의 Code에서 Toast는 UI 요소이기 때문에 Exception이 발생하게 되는 것입니다.

    해결 방법은 두 가지가 있습니다.
    1. Activity에서 Thread을 호출한다면? “runOnuiThread” Method를 이용합니다.
    2. 대부분은 “Handler.post” 방식을 이용합니다.

    runOnuiThread 방식 예
    activity.runOnUiThread(new Runnable() {
       public void run() {
           Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
       }
    });

    Handler.post 방식 예
    final Handler handler = new Handler();
    new Thread(new Runnable() {
    @Override
    public void run() {
    while (isInboxThreadRunning) {
    handler.post(new Runnable() {
             public void run() {
                 Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
             }
         });
    SystemClock.sleep(1 * 60 * 1000);
    }
    }
    }).start();

    출처 : http://daddycat.blogspot.kr/2011/05/android-thread-ui.html

    Handler 객체
      +Handler 인스터스를 생성하면 안드로이드 스레드 시스템에 자동 등록
      +사용자 인터페이스에서 일어나는 모든 작업을 담당
      +백그라운드 스레드와 통신
      +통신방법 : Message, Runnable
    -Message 객체
      +Handler 인스턴스에서 Message를 전달하려면 obtainMessage() 메소드를 호출해 메세지 풀에서 인스턴스를 확보해야 한다
      +Message를 Handler에게 전달할 때는 메시지큐에 쌓는 구조로 동작
      -sendMessage(), sendMessageAtFrontQueue(), sendMessageAtTime(), sendMessageDelayed()
      +handlerMessage() 메소드로 메시지 처리, 안드로이드가 큐에서 메시지를 하나씩 뽑아낸다
    start()와 run()에 대한 차이와 쓰레드가 실행되는 과정
    run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 속한 메서드 하나를 호출하는 것이다. 반면에 start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택(call stack)을 생성한 다음에 run()을 호출해서 생성된 호출스택에 run()이 첫번째로 저장되게 한다.

    1. main 메서드에서 쓰레드의 start메소드를 호출한다.
    2. start메서드는 쓰레드가 작없을 수행하는데 사용될 새로운 호출 스택을 생성한다.
    3. 생성된 호출 스택에 run메서드를 호출해서 쓰레드가 작업을 수행하도록 한다.
    4. 이제는 호출스택이 2개 이기 때문에 스케줄러가 정한 순서에 의해서 번갈아 가면서 실행된다.

    한쓰레드에서 예외가 발생해서 종료되어도 다른 쓰레드의 실행에는 영향을 미치지 않는다.


    Thread를 사용해서 프로그램 한다고 하면 일감 떠오르는 것은 "Thread에게는 Runnable객체가 필요하다" 는 사실입니다.Thread가 Thread로서 삶을 시작할 때 시작하는 일이 Runnable의 public void run() 메소드 를 수행하는 것이기 때문입니다. 즉,Runnable객체의 public void run() 메소드는 Thread로 하여금 원하는 일을 시킬수 있는 거의 유일한 방법인 셈입니다. Thread가 수행해야할 일을 Runnable의 public void run()에 써주는 겁니다.

    관심이 있는 분들은 혹 아실지 모르지만 Thread클래스를 살펴보면 Thread 클래스에도 public void run()가 있습니다. 그렇다면 Thread도 Runnable인가요?  예 그렇습니다.  모든 Thread는 Runnable 입니다.  이것이 의미하는 뜻은 Thread가 생성자에서 Runnable 객체를 인자로 받게 되면 , Thread는 Thread로서 삶을 시작할 때 생성자에서 받은 Runnable의 public void run()을 수행합니다.  하지만 만약 생성자에서 Runnable객체를 인자 로 받지 못하면, 스스로가 Runnable객체이므로, Thread가 가진 public void run()을 실행 하게 된다는 뜻입니다.  
    이와 같이 어쨌든 Thread가 해야할 일을 지정해주는 것은 r
    unnable 객체입니다. 따라서 Thread를 이용하는 프로그램을 해야 하는 경우 크게 두가지 방법이 있는데 그 첫째는 Thread가 수행해야할 일인 Runnable 객체를 별도로 만들어 public void run() 메소드를 원하는 대로 만드는 것이고, 둘째는 Thread 자체가 Runnable객체이므로 Thread를 상속한후  public void run() 메소드를 원하는 대로 고치는 겁니다. 즉, Runnable 객체를 만들지 않고 Thread를 상속해서 public void run()메소드를 오버라이딩 한다는 뜻입니다. 즉, Runnable객체의 public void run()에서 적어주여할 일들을 모두 적어 주게 되면 되는 것이지요. 

    물론 2가지 방법중 어떤 것을 사용해도 원하는 결과를 얻는데는 크게 다르지 않습니다. 하지만 필자는 수행해야 하는 일을 거의 Runnable객체를 정의하고 Thread는 Runnable 객체를 수행하는 도구로 사용하고만 있습니다. 여기에는 상당한 이유가 있는데, Runnable객체는 독립적으로 수행되야할 일의 개념화 및 모듈화를 의미합니다. Thread는 단순히 그 일을 독립적으로 수행하는 단순한 도구로써 사용하는 것입니다. 실제로 일을 하는 프로그램 코드(Runnable 객체에 담긴 내용)를코드와는 전혀 상관도 없고, 아무 연관관계도 없는 Thread 클래스에 합쳐서 관리할 이유가 없기 때문이고, 오히려 이런 관계없는 것을 철저히 분리시켜 놓는 것이 가독성이라던가 코드의 관리에 아주 도움이 되기 때문입니다.  하지만 이는 절대적인 것은 아니고, 가끔 편의성에 따라 필자도 Thread 클래스를 상속해서 사용하곤 합니다. 하지만 특별한 이유가 없는한 Runnable 객체를 이용하는 것을 먼저 고려하시라는 것입니다.


    Runnable객체는 할일이라고 생각하자!!

    obtainMessage 는 메세지 풀에서 메세지를 가져오는 거고 
    (이미 사용된 message 를 재 사용하거든요. 소켓 풀 생각하시면 됩니다.)
    sendToTarget 은 Lopper Queue 에 메세지를 보내는 메서드입니다.

    메시지 큐!

    메시지는 스레드간의 신호입니다. 메서드처럼 호출한다고 바로 실행되는 것이 아니라 순서대로 처리된다. 메시지를 쌓아두는 공간이 바로 메시지 큐이다.

    루퍼
    루퍼는 메시지 큐에서 메시지를 꺼내어 핸들러로 전달하는 작업을 수행한다.
    메인스레드의 경우 기본적으로 루퍼를 가지지만 일반 작업을 수행하는 스레드의 경우 기본적으로 루퍼를 가지지 않는다. 이런 스레드가 메시지를 받아야할경우 루퍼를 직접 생성시켜야한다.
    루퍼는 무한히 실행되는 메시지 루프를 통해 쿠에 메시지가 들어오는지 감시하며 들어온 메시지를 처리할 핸들러를 찾아 handle Message메소드를 호출한다.UI를 관리하는 메인스레드는 기본적으로 루퍼를 가지고 이미 동작하므로 별다른 신경을 쓸 필요가 없다.
    루퍼를 직접 프로그래밍 해야하는 경우는 작업 스레드가 메시지를 받아야할때이다. 

    내가 정의한 핸들러에서 쏜메시지가 어떻게 알고 메인쓰레드의 메시지 큐에 쌓이는 걸까?
    메인쓰레드 영역안에서 선언해서?
    그렇다면 자신이 내가 정의한 handler.sendmessage()를 하면 루퍼를 구현하지 않았다면 메인쓰레드의 메시지큐에 메시지가 쌓이고 메인쓰레드의 루퍼가 이메시지를 꺼내 핸들러(내가 정의한 핸들러)로 보내서 처리한다.

    'Mobile > Android' 카테고리의 다른 글

    BroadcastReceiver  (0) 2012.11.02
    Push에 대한 것!  (0) 2012.10.30
    TweenAnimation  (0) 2012.09.24
    AsyncTask  (0) 2012.09.21
    Context  (0) 2012.09.21
    posted by Kyleslab 2011. 1. 28. 10:47



    원래 노트북에서 사용하던 마우스의 휠이 고장나 바꾸기로 결심했고 이왕이면 무선 마우스로 바꿔보자 생각하여 검색하던중 아주 마음에 드는 마우스를 발견했다.

    마우스를 선정한 기준은
    1. 크기가 그리 크지 않을것.
       노트북에 사용할 것이기 때문에 너무 크면 오히려 번거로울수 있기때문에.
    2. 수신기는 초소형일것
       주위에 무선마우스를 사용하는 지인들중 몇몇이 이 초소형 수신기 인데 수신기를 사용할때마다 빼었다 끼었다 하는게 아니라 그냥 계속 노트북에 꽂아 놓은체 사용할 수 있어 번거롭지 않고 분실의 위험이 적다.
    3. 배터리가 오래 갈 것
       너무 자주 배터리를 갈아야 한다면 무선 마우스가 오히려 독이 되므로 가장 배터리타임이 오래가는 것을 위주로 골랐다.
    4. 어느 지면에서도 잘작동하고 인식범위가 5미터이상이어야한다.
    5. 가격은 2만원에서 3만원 사이
       물론 1만원대의 저렴한 제품들도 있지만 한번 살때 조금더 좋은 것을 사 오래 사용하는 주의이기도 하고 확실히 A/S문제도 걸려 일명 "묻지마 A/S"라는 마이크로소프트사의 제품을 골랐다.
    6. 무엇보다도 디자인이 이쁠것!
      매일 보는 녀석인데 이뻐야 한다. 거기다 신묘년 스페셜 에디션은 토끼띠인 나에게 좋은 선물이라고 생각했다.

      그래서 고른게 이녀석인 Microsoft Wireless Mobile Mouse 3500  신묘년 스페셜 에디션이다.
    이녀석 이름도 길다. 위에서 정한 모든 기준을 충족시키는 녀석으로써 아주 마음에 든다. 적당한 무게 와 크기, 이쁜 디자인, 경악할 인식 범위(졸프를 진행하는 Lab실의 전지역을 커버하는 이능력!), 건전지 AA사이즈하나로 8개월의 배터리타임 등등 너무 마음에 들었다.

      그러나 딱 한가지 마음에 안드는 점은 초소형 수신기이긴한다. 겉만 초소형처럼 보인다는 점이다. 아래사진 처럼 수신기 부분만 나와 있지 않고 보기 안좋게 USB단자부분도 나온다는 것이다. 혹시 내 노트북 USB단자의 깊이 얕은가 해서 데스크탑과 친구 노트북으로 확인해보았지만 똑같다.결국 이동할때는 빼고 다녀야 할 듯하다.


    posted by Kyleslab 2010. 12. 11. 01:39


    이서비스는 쓰레드로 만들었습니다.
    그리고 윈도우는 메인함수에서 메인쓰레드라는게 존재한다.
    그리고 각각 동작한다.

    그리고 아까이야기 했듯이 클래스단위로 쓰레드화 하는게 아니고
    하나의 컨숨?한 쓰레드가 됩니다.
    이 프로듀서를 어디서 생성을 할까요->메인에서 생성할겁니다.
    끄려면 강제종료를 시켜야하는데 그러면 메인이 종료됩니다.
    그러면서 자식쓰레드들도 종료가됩니다.

    그럼 소멸자가 어디서 불릴까? ->메인스레드
    서비스함수들은 어디서 돌아가고 있을까? -> 각각 자기스레드

    그럼 소멸자가 호출되는 순간 그 스레드들은 돌고있을까 안돌고있을까?
    -> 돌고있고 소멸자가 호출되도 돌리려고 시도 할 수 있다. -> 그러면 터진다

    해결책
    플래그변수를 불러요 -> Customer에 boolean변수 isEnd를 만든다
    volatile -> 최적화 안하겠다 -> 캐싱하지 않겠다 ->멀티코어에서 발생할수있는 버그를 없애겠다
    -> 메모리에 있는 변수 캐쉬에 올리지마. 느려도 걍 쓸테니
    ->컴파일러의 코드순서바꾸기도 방지

    캐시 아이오 속도차이 를 극복하기위해 각각 자신이 사용하는 변수를 캐싱한다. 메모리의 복사본을
    캐시메모리에 올린다. 하나의 메모리를 두개의 시피유에서 사용한다.

    그리고 isEnd를 false로 세팅하고 각각의 소멸자에 true로 바꿔준다.
    서비스는 isEnd가 Ture일때까지 돌도록 바꾼다

    isend가 한번더 돌다가 종료할때

    ->>>>>>>>>> 하지만 그래도 터진다.

    서로 스레드가 다르므로 생산자로 넘어갔을때 한번 실행하고 돌아간다.
    이 미묘한 타이밍때문에 똑같이 터진다.

    >>>>>>>>>>>>>>>>>> while루프를 빠져나와서 온순간까지 락걸어준다.

    소멸자에서 wait코드가 있는거고 while루프 바깥에서 풀어주고
    생성자는 리소스에 대해서 락걸고 while루프 바깥에서 풀어버리면 세이프한 코드가 된다.

    . 저번주에 한 생산자/소비자 실습에서 프로그램 종료시에 에러가 날 수 있는 조건과 해결책에 대한 필기 문제
    메인함수에서 프로듀서와 커스토머 쓰레드를 만들고
    메인쓰레드가 종료될때 아직 이 두개의 쓰레드는 각자 자기 쓰레드에서 돌고 있는데, 메인쓰레드를 종료되면 터진다.
    왜냐하면 메인쓰레드가 종료될때 소멸자가 호출되는데 이 호출되는 순간에도 각자 자신의 쓰레드가 돌고 있기 때문이다. 그러므로 메인쓰레드가 종료될때 소멸자가 호출되는것을 이용하여 소멸자가 호출되었을대
    모든 자식 스레드를 종료해주기 위해 자신의 스레드가 종료되도록하게 한다. 그리고 그다음 메인스레드가 종료되게하면 문제가 해결된다.
    ::CloseHandle( ( HANDLE )( _threadHandle ) );

    메인스레드가 종료될때 소멸자가 호출되는데
    Thread::~Thread()
    {
     ::WaitForSingleObject( ( HANDLE )( _threadHandle ), INFINITE); //반드시해주어야함 모든 자식 스레드를 종료해주기 위해 메인스레드가 종료하면 소멸자를 호출하게 되고 자신의 핸들스레드가 종료될때까지 기다리게 한다.!!!
     ::CloseHandle( ( HANDLE )( _threadHandle ) ); // 그다음에 메인 스레드를 종료한다.
    }
    내가 만든 스레드가 돌고 있는 상황에서 메인스레드가
    종료 되버리면 에러가 난다.

    메인스레드가 종료될때 클래스의 소멸자를 호출하게되면
    소멸자에 지금 돌고있는 스레드가 모두 종료될때 까지 기다리게 한다.

    스핀락을 사용하는 이유는 리소스를 사용? 대기? 하는 것이 컨텍스트스위칭이 일어나는것보다 빠르기 때문

    'Operations Dev > Producer' 카테고리의 다른 글

    Producer / Customer  (0) 2010.12.10
    posted by Kyleslab 2010. 12. 11. 01:37

    메인함수에서 소비자와 생산자의 쓰레드가 만들어지도록 호출하고
    처리후 메인쓰레드가 종료될때 소멸자가 호출되는데 이 호출되는 순간에도 각자 자신의 쓰레드가 종료되지않고 돌고 있기 때문이다. 해결책은 메인쓰레드가 종료될때 소멸자가 호출되는것을 이용하여 소멸자가 호출되었을대 모든 자식 스레드를 종료할때까지 기다린다. 그다음 메인스레드가 종료되게하면 문제가 해결된다.

    ::WaitForSingleObject( ( HANDLE )( _thread ), INFINITE);

    또한 이 생산자/소비자의 문제에서는 멀티코어에서 생길수 있는 동일 메모리공유 문제에 대해서는 volatile boolean변수로 플래그를 만들어 해결한다.

    그러나 이경우에도 생산자와 소비자 서로의 쓰레드가 다르므로 터질가능성이 있다. 이경우에는 소멸자에서 웨이트를 넣어두고 와일루프안에서 이 웨이트를 풀어주게만든다.

    ::ReleaseMutex(_thread);

    결국 플래그와 공유변수에 대한 락으로 해결하게된다.

    posted by Kyleslab 2010. 12. 10. 23:45


    Producer.h

    #pragma once

    #include <queue>
    #include <string>

    #include "Concurrency\Thread.h"
    #include "Customer.h"
    #include "Concurrency\Timer.h"
    #include "Concurrency\SpinLock.h"

    class Producer
    {
    public:
     Producer(float frequency);
     ~Producer();


    public:
     void
      beginService();
     bool
      tryPop( std::string * product);
     
    private:
     void service();

    private:
     Thread * _thread;
     std::queue< std::string > _products;//경쟁관계에 있는 녀석
     Timer _timer;
     float _frequency;
     unsigned int _count;
     volatile bool _isEnd;
     SpinLock mylock;
    };


    Produce.cpp

    #include "StdAfx.h"
    #include "Producer.h"

    #include <Windows.h>


    Producer::Producer(float frequency)//인자를 갖지 않는 생성자를 호출한다 기입하지 않으면, 파라미터를 받는 생성자를 만들었다면 기본생성자는 호출되지않는다.
     : _count( 0 ),
     _isEnd(false),
     _frequency(frequency)
    {
     
    }

    void Producer::beginService()
    {
     _thread = new Thread( std::bind(&Producer::service, this));
    }

    Producer::~Producer(void)
    {
     _isEnd = true;
     ::WaitForSingleObject( ( HANDLE )( _thread ), INFINITE); //반드시해주어야함 모든 자식 스레드를 종료해주기 위해 메인스레드가 종료하면 소멸자를
     delete _thread;

     // 메인스레드가 종료될때 소멸자 호출,. 그러나 지금 스레드가 작동중이라면???
     

    }

    bool Producer::tryPop( std::string * product)
    {
     mylock.lock();
     bool result = false;

     if( !_products.empty())
     {
      *product = _products.front();
      _products.pop();
      result = true;
     }
     mylock.unlock();
     return result;

    }

     

    void Producer::service()

     _timer.start();
     
     while( !_isEnd )
     {
       
      if(_timer.elapsedSeconds() >= _frequency)
      {
       
       char number[100];
       itoa( _count, number, 10 );
       
       std::string product = "product";
       product += number;
       ++_count;
       _timer.stop();
       _timer.start();
       mylock.lock();
       _products.push( product );
       mylock.unlock();
      }
      
      ::Sleep(0); //원래는 블록큐로 가나, 0이니까 준비큐로가고 1초가 아닐때 다른작업할거있음 하라고 양보하는 것이다.
     }
     
    }

    Customer.h
    #pragma once

    #include "Concurrency\Thread.h"
    #include "Concurrency\Timer.h"
    #include "Concurrency\MutexLock.h"

    class Producer;

    class Customer
    {
    public:
     Customer( Producer * producer, // 포인터 타입으로 갖고 있게 한다. 인스턴스를 선언하지 않고, 클래스실체를 선언하지 않고 사용하는법
            float frequency);
     ~Customer();

    public:
     void
      beginService();

    private:
     void service();//백그라운드에서
     
     private:
     Thread * _thread;
     Producer * _producer;
     Timer _timer;
     float _frequency;
     volatile bool _isEnd;


    };


    Customer.cpp
    #include "StdAfx.h"
    #include <concrt.h>
    #include <Windows.h>


    #include "Customer.h"

     

    #include "Producer.h"//헤더에서 그냥 선언만해놓고 여기서 추가해주면 사용할수 있다.


    Customer::Customer( Producer * producer,
     float frequency)
     : _producer( producer ),
     _frequency( frequency ),
     _isEnd(false)
    {

    }


    Customer::~Customer(void)
    {
     _isEnd = true;
     ::WaitForSingleObject( ( HANDLE )( _thread ), INFINITE);
     delete _thread;
    }


    void Customer::beginService()
    {
     _thread = new Thread( std::bind(&Customer::service, this));
    }


    void
     Customer::service()
    {
     _timer.start();

     while( !_isEnd)
     {
      if(_timer.elapsedSeconds() >= _frequency )
      {
       std::string product;

       if( _producer->tryPop(&product)) // 커스토머 쓰레드 호출되는 곳의 쓰레드이다.
       {
     
        printf( "%s\n", product.c_str() );

        _timer.stop();
        _timer.start();
       }

      }
      ::Sleep( _frequency * 250 );
     }
    }


    ServiceThread.cpp
    // ServiceThread.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
    //

    #include "stdafx.h"

    #include<Windows.h>

    #include "Customer.h"
    #include "Producer.h"
    int _tmain(int argc, _TCHAR* argv[])
    {
     Producer p(0.01f);
     Customer c1( &p, 0.008);
     Customer c2( &p, 0.005 );

     p.beginService();
     ::Sleep( 2000 );
     c1.beginService();
     c2.beginService();

     Timer timer;
     timer.start();
     while( timer.elapsedSeconds() < 20);

     return 0;
    }

     

    'Operations Dev > Producer' 카테고리의 다른 글

    종료  (0) 2010.12.11
    posted by Kyleslab 2010. 12. 10. 22:09


    // Test1.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
    //

    #include "stdafx.h"

    #include <string>
    #include <vector>
    #include <algorithm>
    #include <iostream>

    using namespace std;

    #include "Concurrency/Thread.h"
    #include "Concurrency/MutexLock.h"

    //이거 연결은 정적 라이브러리 연결 동적이 나올수도 있음

    string g_buffer;
    MutexLock g_lock; //1000번 찍는데 락을 걸어주기 위해서 우리가 만든 락 클래스를 이용한다.

    int _tmain(int argc, _TCHAR* argv[])
    {
     vector<Thread * >threads; //힙을 이용하면 new를 이용하면 생성하고 delete를 하면 우리가 생성 소멸 타이밍을 조절 할 수 있다.

     for(int i =0; i < 5; ++i)
     {
      char ch = 'a'+i;
      threads.push_back(new Thread( // 푸시백은 백터 맨뒤에 이것들을 추가하는것
      [ ch ]()->void //[]여기 안에는 클로져 특성을 가지는 변수, 캡쳐할 리스트들 ()인자값이 들어감
      {
       g_lock.lock();
       for(int i =0; i < 1000; ++i)
       {
        g_buffer +=ch;
       }
       g_lock.unlock(); //락을 걸어준다.

      }));
     }
     for_each(threads.begin(), //이것은 외우기
      threads.end(),
      [](Thread * thread)  //포위치로 쓰래드를 처음부터 끝까지 훌터서 지울려고 하는거임
     {
      delete thread; //벡터에는 포인터가 들어있기 때문에 벡터의 원소는 변화가 없다. 쓰레드가 종료 되면 지워버림
     });
       
     cout<<g_buffer<<endl;
     return 0;
    }


     

    'Operations Dev > Concurrency' 카테고리의 다른 글

    자체 스핀락 이용  (0) 2010.12.10
    Concurrency  (0) 2010.12.10