Case When 문의 의미
Case When문이나 Decode함수를 사용하여 Select절의 결과물을 원하는 대로 변경하는 SQL은 여러 번 본 적이 있다.
하지만 주변 사람들과 이야기해보면 의외로 Select절 뿐만 아니라 Where절, Order By절, Group By절 등에 다 사용할 수 있다는 사실을 모르는 경우가 있었는데 오늘은 이런 사실에 대해 이야기해보고자 한다.

Case When문의 활용범위는 무궁무진하며, 이 것을 사용하지 않고 Static한 SQL을 사용한다는 것이 필자로서는 거의 불가능하게 느껴진다. 앞으로의 포스트에서도 줄기차게 사용할 것이므로 관심이 있으신 분들은 여러 활용방법을 제대로 알아두면 좋을 것 같다.


Case When문
간단하게는 Case When문은 Decode함수의 확장이다.
Decode함수는 Oracle에서 제공되는 SQL 내의 if/else의 조건문 역할을 하는 함수이나 = 연산만 가능하다. Case When문은 이를 확장한 것이며, 논리연산, 산술연산, 관계 연산을 다 지원하며 Oracle 뿐만 아니라 MS-SQL에서도 지원한다.
(예전에 듣기로는 My-SQL에서도 지원한다고 들은 적이 있다. Test해보지는 않았지만.)
복잡한 조건일 수록 Case When문은 Decode문에 비해 가독성이 높고, 수행속도도 빠르다고 할 수 있겠다.

사용하는 문법은 다음과 같다.
  • case when [조건1] then [결과1] when [조건2] then?[결과2] ...else [결과3] end
  • case [컬럼1] when [값1] then [결과1] when [값2] then [결과2] …  else [결과 3] end


Select 절에서의 활용
실제로 가장 많이 사용되는 활용은 Select절에서일 것이다. 실제 우리가 가지고 있는 Data로부터 우리가 원한 Data를 뽑아내는데 자주 사용된다.

Select (Case When name = ‘서울’ Then ‘수도권’  When name = ‘경기도’ Then ‘수도권’ Else ‘비수도권’ End) city_group, name From City

이 SQL은 당연히 다음과 같이도 사용가능하다.

Select (Case name When ‘서울’ Then ‘수도권’ When ‘경기도’ Then ‘수도권’ Else ‘비수도권’ End) city_group, name From City


지역이라는 Column이 없기 때문에 수도권인지 아닌지를 가져오기 위해 위와 같은 SQL을 사용하였다.
(왜 수도권에 '인천'은 없는지에 대해서 궁금해하는 사람은 없기 바란다. PT하다보면 가끔 그런 사람을 만날 수 있는데 정말 한숨만 나온다-_- 집중해야 할 곳에 집중하자.)


Group By 절에서의 활용
사례를 살펴보기 위해서 Cartesian Product(카테시안 곱) 항목에서 예로 들었던 SQL을 다시 가져와 보자.

Select (Case tbl2.no1 When 1 Then tbl1.품목 Else ‘합계’ End), Sum(수량)
From Table1 tbl1, (Select 1 no1 From dual Union All Select 2 From dual) tbl2
Group By (Case tbl2.no1 When 1 Then tbl1.품목 Else ‘합계’ End), tbl2.no1

복제된 Data에서 no1이 1인 것은 품목으로 Group by를 하였고, 2인 것은 "합계" 즉 상수로 Group by를 하였다. 따라서, 1인 경우는 품목별로 합계를 구하게 되고, 나머지 항목들은 전체 Group by가 되게 될 것이다.


Order By 절에서의 활용
회사 직원을 이름 순으로 뽑아오는 쿼리가 있다고 하자. 어느 날 직원 시스템을 한 번도 써보지 않았던 사장님이 시스템에 들어가보니 본인 이름이 중간에 있는 것을 발견하고 명색이 사장님인데 직원들 순서 중에서 내가 제일 앞에 나와야 하지 않는가 하는 의문을 제기하였다.

그런-_- 이유로 고쳐야할 시스템의 요구사항은 다음과 같다.
사장님의 이름만 맨 앞으로 뽑아내고 나머지는 가나다 순으로 정렬하면 되는 것이다. 물론 일단 Data를 Client로 다 가져온 다음 프로그램 상에서 처리할 수도 있겠으나, 나중에 이 사실을 알게 된 이사님도 이의를 제기한다면? 다른 관리자들은? 이런 요구사항들을 다 Client에서 처리한다면 프로그램은 갈수록 복잡해지게 될 것이다.

따라서, 그런 요구사항의 변경은 SQL에서 적용하는 것이 가장 간단할 것이며, 이 것이 우리가 추구하고자 하는 Static SQL의 본질이다. (Static SQL이란 원하는 집합을 하나의 SQL로 뽑아내는 것을 말하며 이 문제영역에서는 사장님이 맨 앞에 나오고 다른 직원들은 이름순으로 정렬된 집합이 우리가 원하는 집합이다. 따라서, 그것을 하나의 SQL로 얻어와야 한다.)

Order by절에서는 오름차순이건, 내림차순이건 사장님의 이름만 맨 앞으로 뽑아올 수 없으나, Case When문을 사용하면 간단하다.

Select 이름
From Emp
Order By (Case When 직함 = '사장' Then 1 Else 2 End), 이름


Where절에서의 활용
외부에서 들어온 조건(:v1)에 따라 서로 다른 동작을 정의하고자 할 때 사용할 수 있다.
Select 이름
From Emp
Where (Case When Length(:v1) = 0 Then 1 Else Instr(이름, :v1) End) > 0

검색할 이름에 대한 입력이 :v1에 들어오면 이름을 검색하여 결과를 보여주고 입력이 없으면 전체 직원의 이름을 Return하는 SQL이다. Where절을 동적으로 조합하지 않고 SQL 내부에서도 이러한 방법을 통해 서로 다른 결과값을 Return할 수 있다.


결론
SQL을 작성하는 여러가지 방법을 배우는 것은 문제에 따라 다양한 Idea를 내기 위한 필요조건이다. Client에서 혹은 PL/SQL에서의 if/else를 상당부분 SQL로 옮겨올 수 있는 Case When문을 익혀두는 것은 그 중에서도 첫 걸음이 될 수 있을 것이다

출처 : http://rtti.tistory.com/ 

'SQL' 카테고리의 다른 글

Bulk Insert 옵션에 따른 성능 비교  (0) 2011.05.03
Alias(별칭) "AS"  (0) 2011.05.02

우선순위 큐와 을 비교해 봄으로써 추상적 데이타 타입과 자료구조의 차이를 명확하게 이해할 수 있다.

우선순위 큐는 추상적 데이타 타입(ADT)의 하나로서, ADT가 정의하는 것은 어떤 블랙 박스 같은 것으로,
그 안에 무엇이 어떻게 들어 있는지에는 관심이 없고, '어떤 일을 할 수 있다'에 초점을 맞춘다.
심지어 그 어떤 일을 할 때 얼마의 시간이 걸리는지에 대해서도 별로 관심이 없다.

우선순위 큐는 다음과 같은 일을 할 수 있다.

1. 큐에 '무엇인가'를 넣을 수 있다. 여기에 숫자로 된 우선순위를 꼬리표로 해서 함께 넣는다.
2. 큐에서 가장 높은 우선순위의 꼬리표가 붙은 '무엇인가'를 꺼낼 수 있다.
3. 2번의 변형으로, 가장 높은 우선순위에 무엇이 있는지 볼 수 있다. (꺼내지 않는다)

  • add an element to the queue with an associated priority
  • remove the element from the queue that has the highest priority, and return it
  • (optionally) peek at the element with highest priority without removing it  

  • 왜 가장 높은 우선순위의 것만 꺼낼 수 있고, 다른 것을 임의로 꺼낼 수도 없는,
    그렇게 제약이 많은 것을 생각하느냐고 할 수도 있겠지만,
    ADT에서 중요한 개념은 이것이다. 
    시간이나 공간 같은 제약을 고려하지 않고, 한정된 수의 요구사항을 명확히 하는 것.
    요구사항이 한정되어야 그 다음에, 어떤 방법으로 구현하는 것이 효율적인지 생각할 수 있기 때문이다.

    '배열로 구현하지'라고 먼저 생각해 버리면, 배열에서 효율적으로 동작하는 새로운 기능을 추가할 수도 있겠지만,
    일의 순서가 그런 것이 아니기 때문이다.

    이렇게 우선순위 큐의 기능을 정의하고 나서, 우선순위 큐를 효율적으로 구현하기 위한 자료구조를 생각해 보자.
    메모리에 저장된 형태에 대해서 다음과 같은 것을 생각해 볼 수 있다.

    1. 배열 - 여기에는 다시 임의 배열과, 정렬된 배열
    2. 리스트 - 역시 임의 리스트와, 정렬된 리스트
    3. 그리고 다음에 설명할 힙(Heap)

    배열과 리스트가 별도의 자료구조인 것은 명확하지만, 임의로 넣은 것과, 정렬된 상태인 것이 다른 것인 이유는,
    배열과 리스트에 대한 연산이 다르듯이, 정렬되었는지 여부에 따라 연산의 구현이 확연하게 달라지기 때문이다.

    임의 배열을 사용할 경우 우선순위 큐의 구현은 다음과 같이 이루어진다.
    1. 넣을 때 - 현재 들어 있는 것들 뒤에 넣는다. O(1)
    2. 꺼낼 때 - 우선순위 값을 비교하여 가장 큰 것을 꺼낸다. O(n)

    정렬되어 있는 배열을 사용할 경우 우선순위 큐의 구현은 다음과 같이 이루어진다.
    1. 넣을 때 - 정렬된 순서에서의 위치를 찾아서 넣는다. O(n)
    2. 꺼낼 때 - 맨 앞의 것을 꺼내면 된다. O(1)

    n개를 넣고 꺼낼 때 걸리는 시간은, 둘 다 O(n * n)이 된다.

    n개를 다 넣고 나서 - 정렬하고 - n개를 순서대로 꺼내는 것은, 우선순위 큐를 쓰고자 하는 목적에도 맞지 않으며,
    우선순위 큐의 정의에 정렬 같은 것은 없다. 
    다만 이렇게 했을 때 걸리는 시간은 정렬에 걸리는 시간인 O (n log n)으로 낮출 수 있다.

    이제 에 대해 얘기해 보자.
    힙이라는 자료구조를 가지고 얻고자 하는 것은 임의로 넣고 꺼내는 것을 지원하면서, O(n * n)보다 빠른 방법이다.

    이를 위해서 힙이라는 자료구조는 개념상으로 바이너리 트리에서, '부모 노드는 자식 노드보다 크다'라는 가정만
    유지한다. 당연히 최상위 루트 노드의 값이 가장 크다. 
    이를 배열에 저장했을 때 유지되는 내부 형태는, n번째 항목의 자식 노드는 n * 2와, n * 2 + 1 번째에 위치한다. 
    그리고 배열에 들어있는 값들은 정렬된 형태도 아니고, 임의 형태도 아니지만, 
    맨 앞에는 가장 우선순위가 큰 값을 갖고 있게 된다.

    1. 넣을 때 - 맨 뒤에 넣는다. 그리고 형제 노드, 부모 노드와 비교해서 셋 중에 큰 값을 부모 노드로 만든다.
    그리고 위로 전파한다. O(log n)에 수행 가능하다.

    2. 꺼낼 때 - 맨 앞의 것을 꺼낸다. 그리고 이 자리에는 자식 노드 둘 중에 큰 값을 갖고 올라온다.
    그리고 아래로 전파한다. 역시 O(log n)에 수행 가능하다.

    따라서 n개를 넣고 꺼낼 때 걸리는 시간은, O(n log n)이 된다.
    전체적으로, 정렬에 걸리는 시간과 같은 복잡도를 갖게 되었다. 

    여기서 얘기하는 힙은 정확하게는 Binary Heap의 array implementation 이다.

    이와 같이 추상적 데이타 타입이 문제에 초점을 둔다면, 자료구조는 해결에 초점을 두고  있다.
    인터페이스와 임플리멘테이션도 이와 비슷하게 생각할 수 있을 것이다.

    좀 더 자세한 것은 위키피디아에서 Priority Queue와 Heap을 검색해 보기 바란다.

    출처 : http://core.egloos.com/

     

    'Java' 카테고리의 다른 글

    ArrayIndexOutOfBoundsException  (0) 2011.07.13
    Collection  (0) 2011.05.02

    ■ Collection : 오브젝트 집합을 나타내는 가장 기본적인 인터페이스
       □ Set : 중복 요소 없는 오브젝트 집합
          ○ SortedSet : 요소가 자동 올림순으로 정렬된다.
                                삽입되는 요소는 Comparable 인터페이스 속성을 갖고 있지만
                                지정된 Comparator에 의해 받아들여진다.
       □ List : 순서있는 오브젝트 집합, 중복허가, 리스트내의 특정위치에 요소를 넣을 수 있다.
                   유저는 위치(인덱스)를 지정하여 각요소에 접근할 수 있다.

    ■ Map : 키-값으로 나타내는 오브젝트 집합. 키는 중복될 수 없다.
       □ SortedMap : 요소가 키로서 자동 올림순 정렬되는 Map.
                              삽입된 키는 Comparable 인터페이스 속성을 갖고 있지만
                              지정된 Comparator에 의해 받아들여진다.

    구분 해쉬테이블 가변배열 밸런스트리 링크리스트 해쉬테이블&링크리스트
    Set HashSet TreeSet LinkedHashSet
    List ArrayList LinkedList
    Map HashMap TreeMap LinkedHashMap

    HashSet   : HashMap 인터페이스를 기본으로 Set인터페이스를 구현한 것. 순서를 보장할 수 없다.

    TreeSet    : TreeMap 인터페이스를 기본으로 Set인터페이스를 구현한 것. 올림순으로 소트된다.

    LinkedHashSet : 삽입된 순서를 기억한다.
                              같은 요소를 다시 삽입하면 이전 순서를 그대로 기억한다.

    ArrayList   : List 인터페이스 속성을 갖고 배열크기를 변경할 수 있다.
                      배열크기를 조작하는 메소드를 제공한다.

    LinkedList : 리스트의 처음과 마지막 요소를 삽입,삭제할 수 있다. 
                      그 관련 메소드 제공.
                      동기화되어 있지 않다는 것을 제외하면 Vector와 같다.

    HashMap : 동기화되어 있지않다는 것과 Null을 허용한다는 것 이외에는 HashTable과 같다.

    TreeMap  : Red-Black Tree. 키의 올림순으로 소트된다.
                      LinkedHashMap : 키가 맵에 삽입된 순서를 기억한다. 
                      같은 키로 삽입해도 이전의 순서를 기억한다.


    ■HashSet,TreeSet,LinkedHashSet 비교
    중복요소없는 오브젝트 집합을 관리하는 클래스에는 HashSet,TreeSet,LinkedHashSet가 있다.
    Set기능만 필요한 경우에는 HashSet.
    요소를 올림차순으로 정렬하는 경우에는 TreeSet.
    요소의 삽입순서를 기억할 필요가 있을 때에는 LinkedHashSet.

    ■ArrayList와 LinkedList
    순서있는 오브젝트 집합을 관리하는 클래스로는 ArrayList와 LinkedList가 있다.

    구분 인덱스 엑세스 Iterator 엑세스 추가 삽입 삭제
    ArrayList 느림 느림
    LinkedList 느림

    ArrayList는 인덱스에 의한 랜덤엑세스 성능이 좋지만
    요소의 삽입, 삭제에는 좋지않다.
    LinkedList는 거꾸로 요소의 삽입, 삭제에는 성능이 좋지만
    인덱스에 의한 랜덤엑세스는 좋지 않다.

    ■HashMap, TreeMap, LinkedHashMap 
    키-값 관계로 맵핑하는 경우에 사용하는 클래스에는 HashMap, TreeMap, LinkedHashMap이 있다.
    Map 기능만 필요한 경우는 HashMap
    키를 올림차순으로 소트할 필요가 있을 때는 TreeMap
    키의 삽입순서를 기억할 필요가 있을 때에는 LinkedHashMap

    ■Set Class Test

    import java.util.*;

    public class SetClassTest {
      public static void main(String[] args) {
        try {
          // HashSet
          Set hashSet = new HashSet();
          addData(hashSet);
          System.out.println("HashSet : " + hashSet);

          // TreeSet
          Set treeSet = new TreeSet();
          addData(treeSet);
          System.out.println("TreeSet : " + treeSet);

          // LinkedHashSet
          Set linkedHashSet = new LinkedHashSet();
          addData(linkedHashSet);
          System.out.println("LinkedHashSet : " + linkedHashSet);

        } catch (Exception e) {
          e.printStackTrace();
        }
      }

      static void addData(Set set) {
        for (int i = 10;i >= 1;i--) {
          set.add(new Integer(i));
        }
      }
    }

    결과 :

       HashSet : [2,4,8,9,6,1,3,7,10,5]

       TreeSet : [1,2,3,4,5,6,7,8,9,10]

       LinkedHashSet : [10,9,8,7,6,5,4,3,2,1]

     

    ■List Class Test

    import java.util.*;

    public class ListClassTest {
      public static void main(String[] args) {
        try {
          long start, end;
          /** 추가 **/
          // ArrayList
          List arrayList = new ArrayList();
          start = System.currentTimeMillis();
          addData(arrayList);
          end = System.currentTimeMillis();
          System.out.println("ArrayList 추가 : " + (end - start));

          // LinkedList
          List linkedList = new LinkedList();
          start = System.currentTimeMillis();
          addData(linkedList);
          end = System.currentTimeMillis();
          System.out.println("LinkedList 추가 : " + (end - start));

          /** 삭제 **/
          // ArrayList
          start = System.currentTimeMillis();
          removeData(arrayList);
          end = System.currentTimeMillis();
          System.out.println("ArrayList 삭제 : " + (end - start));

          // LinkedList
          start = System.currentTimeMillis();
          removeData(linkedList);
          end = System.currentTimeMillis();
          System.out.println("LinkedList 삭제 : " + (end - start));

          /** 삽입 **/
          // ArrayList
          start = System.currentTimeMillis();
          insertData(arrayList);
          end = System.currentTimeMillis();
          System.out.println("ArrayList 삽입 : " + (end - start));

          // LinkedList
          start = System.currentTimeMillis();
          insertData(linkedList);
          end = System.currentTimeMillis();
          System.out.println("LinkedList 삽입 : " + (end - start));


          /**  인덱스 접근 **/
          // ArrayList
          start = System.currentTimeMillis();
          indexAccess(arrayList);
          end = System.currentTimeMillis();
          System.out.println("ArrayList 인덱스 접근 : " + (end - start));

          // LinkedList
          start = System.currentTimeMillis();
          indexAccess(linkedList);
          end = System.currentTimeMillis();
          System.out.println("LinkedList 인덱스 접근 : " + (end - start));

          /** Iterator 로 접근 **/
          // ArrayList
          start = System.currentTimeMillis();
          iteratorAccess(arrayList);
          end = System.currentTimeMillis();
          System.out.println("ArrayList Iterator로 접근 : " + (end - start));

          // LinkedList
          start = System.currentTimeMillis();
          iteratorAccess(linkedList);
          end = System.currentTimeMillis();
          System.out.println("LinkedList Iterator로 접근 : " + (end - start));

        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      static void addData(List list) {
        for (int i=1;i<100000;i++) {
          list.add(new Integer(i));
        }
      }
      static void removeData(List list) {
        while(!list.isEmpty()) {
          list.remove(0);
        }
      }
      static void insertData(List list) {
        for (int i=1;i<100000;i++) {
          list.add(0 , new Integer(i));
        }
      }

      static void indexAccess(List list) {
        int size = list.size();
        for (int i=0;i<size;i++) {
          Integer integer = (Integer)list.get(i);
        }
      }
      static void iteratorAccess(List list) {
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          Integer integer = (Integer)iterator.next();
        }
      }
    }

    결과 :

      ArrayList 추가 : 451

      LinkedList 추가 : 230

      ArrayList 삭제 : 45315

      LinkedList 삭제 : 20

      ArrayList 삽입 : 45416

      LinkedList 삽입 : 170

      ArrayList 인덱스로 접근 : 0

      LinkedList 인덱스로 접근 : 231473

      ArrayList Iterator로 접근 : 60

      LinkedList Iterator로 접근 : 50

     

    ■Map Class Test

    import java.util.*;

    public class MapClassTest {
      public static void main(String[] args) {
        try {
          // HashMap
          Map hashMap = new HashMap();
          putData(hashMap);
          System.out.println("HashMap : " + hashMap);

          // TreeMap
          Map treeMap = new TreeMap();
          putData(treeMap);
          System.out.println("TreeMap : " + treeMap);

          // LinkedHashMap
          Map linkedHashMap = new LinkedHashMap();
          putData(linkedHashMap);
          System.out.println("LinkedHashMap : " + linkedHashMap);

        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      static void putData(Map map) {
        map.put("5" , "Five");
        map.put("4" , "Four");
        map.put("3" , "Three");
        map.put("2" , "Two");
        map.put("1" , "One");
      }
    }

    결과 :

    HashMap : {3=Three, 5=Five, 2=Two, 4=Four, 1=One}

    TreeMap : {1=One, 2=Two, 3=Three, 4=Four, 5=Five}

    LinkedHashMap : {5=Five, 4=Four, 3=Three, 2=Two, 1=One}

    출처 : 
    http://dojeun.egloos.com

     

    'Java' 카테고리의 다른 글

    ArrayIndexOutOfBoundsException  (0) 2011.07.13
    우선순위 큐(Priority Queue)와 힙(Heap)  (0) 2011.05.02
    rivate int dipToInt(float number) {

        int num = (int) TypedValue.applyDimension(

                    TypedValue.COMPLEX_UNIT_DIP, number, 

                    context.getResources().getDisplayMetrics());

        return num;

    '안드로이드' 카테고리의 다른 글

    Thread 우선순위 설정.  (0) 2011.05.03
    Looper  (0) 2011.05.03
    [Service] onStartCommand  (0) 2011.04.06
    Android에서 alarm List의 Item들이 무한으로 재생되는 경우  (0) 2011.04.04
    ForeGround Service  (0) 2011.04.04

    출처 : http://blog.naver.com/PostView.nhn?blogId=huewu&logNo=110081442947&redirect=Dlog&widgetTypeCall=true
     

    원래 하나의 포스트로 끝마치려고 했지만... 글이 조금 길어지는 경향이 있어서 두 개로 나누어 포스트 합니다. 첫번째 글에서 바로 뒤이어 Service 의 라이프 사이클 변화에 관한 내용을 번역합니다.


    Service lifecycle changes

     Service 들이 필요하지 않은 경우에도 불구하고, 자신을 포그라운드 프로세스로 설정하는 문제를 제외하고도,  백그라운드에서 너무 많은 수의 Service 들이 돌아감에 따라, 각각의 Service 들이 한정된 메모리 자원을 차지하게 위해 서로 경쟁하게 되면 또 다른 문제가 발생할 수 있다. 안드로이드 플랫폼과 Service 간의 상호 작용 과정에서, 특정 어플리케이션이 코드상으로는 별다른 문제 없이 작성된 경우에도, Service 가 정상 종료되지 않고 백그라운드에 남아있을 수 있다. 예를 들어 다음과 같은 상황이다.
    1. Application 이 StartService 를 호출 한다.

    2. Service 의 onCreate, onStart() 함수가 호출 되고, 특정한 작업을 수행하기 위해 백그라운드 스레드가 생성된다.

    3. 시스템이 메모리 부족으로 인해 현재 작동중인 서비스를 강제로 종료 한다.

    4. 잠시 후에, 메모리 상황이 호전되어 Service 가 재시작한다. 이 때, Service 의 onCreate 함수는 호출 되지만, onStart 는 호출 되지 않는다. (startService 는 호출 되지 않았음으로..)

     
     결국, Service 는 생성된 채로 남아있게 된다. Service 는 어떠한 일을 수행 하지도 않으며 (무슨 일을 수행해야할 지 알 수 없음으로...), 어떤 시점에 종료되어야 하는지도 알 수 없다.

     이러한 문제를 해결하기 위해 안드로이드 2.0 에서 Service 의 onStart() 는 더이상 사용되지 않는다. (이전 버전과의 호환성 문제때문에 작동 자체는 하지만...) 대신 Service.onStartCommand() 콜백이 사용된다. 이 콜백 함수는 시스템이 Service 를 좀 더 잘 관리 할 수 있도록 해주는데, 기존 API 와는 달리  onStartCommand() 는 결과값을 반환한다. 안드로이드 플랫폼은 이 결과값을 기반으로 만일 작동중인 서비스가 강제로 종료될 경우, 어떠한 일을 수행해야 하는지 판단한다.
    • START_STICKY  는 기본적으로 이전과 동일하게 작동한다. 이전과의 차이점은 다음과 같다.  기존에 프로세스가 강제 종료된 후 Service 가 재 시작 될 때, onStart() 콜백이 호출 되지 않았지만, START_STICKY 형태로 호출된 Service 는 null Intent 가 담긴 onStartCommand() 콜백 함수가 호출된다. 이 모드를 사용하는 Service 는 null Intent 로 onStartCommand() 콜백 함수가 호출되는 경우를 주의깊게 처리해야 한다. 
    • START_NOT_STICKY 모드로 시작된 Service는  안드로이드 플랫폼에 의해 프로세스가 강제로 종료되는 경우, 다시 시작되지 않고 종료된 상태로 남게된다. 이러한 방식은 특정 Intent 로 주어진 명령을 수행하는 동안에만 Service 가 실행되면 되는 경우에 적당하다 예를 들어, 매15분마다 네트워크 상태를 체크하기 위해 실행되는 Service 를 생각해 보면, 만일 이 Service 가 작업도중 강제로 종료될 경우, 이 Service 는 재시작하기 보다는 정지된 상태로 남겨두고, 15분 후에 새롭게 시작되도록 하는 것이 최선이다. 
    • START_REDELIVER_INTENT  는 START_NOT_STICKY 와 유사하다. 단, 프로세스가 강제로 종료되는 경우 (stopSelf() 가 호출 되기 전에 종료되는 경우), Intent 가 다시 전달 되어 Service 가 재시작 한다.  (단, 여러차례 시도한 후에도, 작업이 종료되지 않으면, Service 는 재시작되지 않는다.) 이 모드는 전달받은 명령을 반드시 수행해야 하는 Service 에 유용하다. 
     기존에 존재하는 어플리케이션과의 호환성을 위해서, 이전 버전을 기준으로 제작된 어플리케이션은  START_STICKY_COMPATIBILITY 모드로 작동한다.  (null intent 를 보내지 않고 기존과 동일하게 작동). 단 API 버전 5  이상을 기준으로 작성된 어플리케이션들은 기본적으로 START_STICKY 모드로 작동 하며, 개발자는 반드시 onStart() 나 onStartCommand() 가 null intent 를 가지고 호출되는 경우를 염두해 두어야 한다. 

     개발자들은 아래에 제시된 코드를 활용하여, 손쉽게 이전 버전과 새로운 버전 양쪽 모두에서 작동하는 Service 를 구현할 수 있다. 

        // This is the old onStart method that will be called on the pre-2.0
        // platform.  On 2.0 or later we override onStartCommand() so this
        // method will not be called.
        @Override
        public void onStart(Intent intent, int startId) {
            handleStart(intent, startId);
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            handleStart(intent, startId);
            return START_NOT_STICKY;
        }
    
        void handleStart(Intent intent, int startId) {
            // do work


    New "running services" user interface

     * 이 후 이어지는 내용은 새롭게 추가된 Running Service 를 확인 할 수 있는 UI 어플리케이션에 관한 내용이라 대충 대충 건너 띄도록 하겠습니다.

     안드로이드 2.0 부터는 새롭게 "Running Services" Activity 가 어플리케이션 세팅에 추가 되었다. 사용자는 목록에 표시된 Service 를 터치해서 종료할 수 있다.

     화면 하단부에는 현재 시스템의 메모리 상황에 대한 정보가 표시된다. 

    • Avail: 38MB+114MB in 25 은 현재 38MB 의 공간이 바로 사용가능하며, 114MB 의 메모리 공간은 언제든지 종료시킬 수 있는 25개의 백그라운드 프로세스에 의해 점유되고 있음을 알려준다. 
    • Other: 32MB in 3 은 현재 3개의 임의로 종료되서는 않되는 포그라운드 프로세스에 의해 32MB 의 공간이 사용되고 있음을 알려준다. 
     대부분의 사용자들에게 이 새로운 UI 는 기존의 Task Killer 어플리케이션에 비해 사용하기 편리하며, 강제로 Service 를 종료시키는 것이 아니기 때문에 문제를 일으킬 소지가 훨씬 적다. 

     개발자들에게는 자신이 작성한 어플리케이션이 잘 작동하는지 (특히 Service 관리라는 측면에서) 확인 할 수 있게 해주는 유용한 툴이 될 수있다. 또한 어플리케이션이 구동한 Service 가 사용자에 의해 어느시점에서든지 종료 할 수 있다는 점을 염두해 두어야 한다. 안드로이드의 Service 는 매우 강력한 도구이지만, 어플리케이션 개발자들이 전체 폰의 성능에 큰 악영향을 끼칠 수 있는 문제를 가장 많이 일으키는 도구이기도 한다. 

    '안드로이드' 카테고리의 다른 글

    Looper  (0) 2011.05.03
    DIP를 px로 변환하기  (0) 2011.04.21
    Android에서 alarm List의 Item들이 무한으로 재생되는 경우  (0) 2011.04.04
    ForeGround Service  (0) 2011.04.04
    Thread  (0) 2011.03.24

    일단 Alarm 재생파일인 Ogg파일을 울트라 에디터로 보시면 다음과 같은 TAG 부분을 확인할 수 있습니다.

     

    위와 같이 파일에서 보시면 ANDROID_LOOP=true TAG부분이 존재하는데 이부분을 파싱을 하여, 무한으로 재생이 되는 것입니다.

     

    이 부분에 해당하는 Source부분은 Vorbisplayer.cpp란 부분에 보시면, setdatasource란 함수가 존재할 것입니다. 이 함수에서 아랫부분을 보시면 다음과 같은 소스부분이 존재하게 됩니다.

     

    Vorbisplayer.cpp

    // look for the android loop tag  (for ringtones)

        char **ptr = ov_comment(&mVorbisFile,-1)->user_comments;

        while(*ptr) {

            // does the comment start with ANDROID_LOOP_TAG

            if(strncmp(*ptr, ANDROID_LOOP_TAG, strlen(ANDROID_LOOP_TAG)) == 0) {

                // read the value of the tag

                char *val = *ptr + strlen(ANDROID_LOOP_TAG) + 1;

                mAndroidLoop = (strncmp(val, "true", 4) == 0);

            }

            // we keep parsing even after finding one occurence of ANDROID_LOOP_TAG,

            // as we could find another one  (the tag might have been appended more than once).

            ++ptr;

        }

        LOGV_IF(mAndroidLoop, "looped sound");

     

        mState = STATE_OPEN

    Vorbisplayer.h

       #define ANDROID_LOOP_TAG "ANDROID_LOOP"

    위의 부분에서 빨간색 부분을 보시면 ANDROID_LOOP_TAG 부분이 있는데, TAG를 인식하여 LOOP를 돌리도록 처리함으로써 알람 무한 루프가 발생하게 되는 것입니다.

    만약 무한 ROOP를 돌리고 싶지 않다면, OGG파일에서 ANDROID_LOOP=truefalse로 돌리시면 됩니다.

    '안드로이드' 카테고리의 다른 글

    DIP를 px로 변환하기  (0) 2011.04.21
    [Service] onStartCommand  (0) 2011.04.06
    ForeGround Service  (0) 2011.04.04
    Thread  (0) 2011.03.24
    윈도우 환경에서 CTS 돌리기  (0) 2011.03.11
    2.1에서 2.2로 변경되면서
    언어가 바뀔시에는  restartPackage(String packageName)함수를 사용하지 않고
    killBackgroundProcesses(String packageName)로 바뀌게 되었습니다.

    이러한 이유로 예전에는 BackGround Service로 돌렸었던
    Music 등과 같은 부분에대해서는
     언어를 바꿔줄때 Service가 죽으면서 ANR이 발생하게 됩니다.
    이러한 문제를 해결하기 위해서 BackGround Service로 시작하는 부분과 정지시켜주는 부분에 대해서는
     
    startForeground(int,Notification) 와 
    stopForeground(boolean removeNotification)
    사용하여 Foreground로 돌리도록 처리하였습니다.

    '안드로이드' 카테고리의 다른 글

    [Service] onStartCommand  (0) 2011.04.06
    Android에서 alarm List의 Item들이 무한으로 재생되는 경우  (0) 2011.04.04
    Thread  (0) 2011.03.24
    윈도우 환경에서 CTS 돌리기  (0) 2011.03.11
    CTS 정리  (0) 2011.03.10
    va.lang.Object
    java.lang.Thread
    public class Thread extends Object implements Runnable

    제 13 장 쓰레드(Thread)


    Objective

    ▶Thread란 무엇인가?

    ▶Java에서 Thread를 정의하고 Create하는 방법 (implements Runnable)

    ▶Thread를 시작하는 방법

    ▶Thread의 기본적인 Control

    ▶Thread를 만드는 또 하나의 방법(extends Thread)

    ▶Synchronized

    ▶Thread Interaction

    ▶Thread control in JDK1.2

     

    Thread

    ▷ 쓰레드란 프로그램 내에서의 순차적인 제어의 흐름(flow of control)이다. 즉 동일한 프로그램 내에서 다른 제어의 흐름과 독립적으로 실행될 수 있는 코드를 뜻한다.

    ▷ 쓰레드와 프로세스는 서로 다르다. 프로세스가 서로 완전히 독립적으로 수행되는 것이라면, 한 프로그램내의 쓰레드들은 서로 완전히 독립적이 아니어서 어떤 변수나 파일을 쓰레드들이 서로 공유하도록 하기에 편리하다. 즉, 다중 쓰레드는 다중 프로세스보다 다루기가 편리하다고 할 수 있다.

    ▷ 즉, 자바는 운영체제의 도움 없이도 언어 자체에서 일종의 멀티프로세싱인 멀티 쓰레드를 지원하는데 이것을 이용하면 unix 같은 운영체제에서 지원하는 멀티프로세싱보다 훨씬 프로그래밍작성 하기에 편리하다.

    Thread 종류

    ▷ 데몬쓰레드는 입출력처리, garbage collection, 사용자 쓰레드의 구동 등을 지원해주는 JVM이 제공하는 쓰레드이다.

    ▷ 사용자쓰레드는 일반 프로그램에서 만들어지는 쓰레드이다.

    ▶ 예를들어 main() 메소드에서 만든 사용자쓰레드는 main()이 수행되는 동안 살아있고(alive) main()이 종료될 때 같이 종료된다. 특별한 언급이 없으면 사용자쓰레드에 관한 사항만 다룬다.

    쓰레드 사용 예제)

    1. public class OurClass{
              public void run(){
                      for(int i=1;i<100;i++){
                              System.out.println("Hello");
                      }
              }
      }
    2. import java.applet.Applet;
    3. public class OurApplet extends Applet{
              public void init(){
                      OurClass oc=new OurClass();
                      oc.run();
              }
      }
    4. import java.applet.Applet;
    5. public class OurApplet extends Applet{
              public void init(){
                      OurClass oc=new OurClass();
                      oc.start();
              }
      }

    start()가 run()를 호출하는 것은 틀림없는 사실이지만 이것이 다른 쓰레드에서 이뤄진다는 것은 큰 차이점이다. 이것은 start()가 다른 쓰레드를 생성한다는 것을 의미한다. 이새로운 쓰레드는 어느정도 초기화 작업을 수행한후 run()를 호출하게 되고 run()가 수행 완료되었을 쓰레드의 소멸작업을 맏게 된다.start()의 호출은 신속히 리턴된다. 그러므로 start()가 최초의 쓰레드로 리턴됨과 동시에 run()는 새롭게 생성된 쓰레드에서 실행된다.

    1. import java.util.*;
    2. public class ThreeThread{
    3. public static void main(String args[]){
    4.                   TestThread t1 = new TestThread("Thread1",(int)(Math.random()*2000));
    5.                   TestThread t2 = new TestThread("Thread2",(int)(Math.random()*2000));
    6.                   TestThread t3 = new TestThread("Thread3",(int)(Math.random()*2000));
    7.                   t1.start();
    8.                   t2.start();
    9.                   t3.start();
    10.                   try{
    11.                           t1.join();
    12.                           t2.join();
    13.                           t3.join();
    14.                             //join() // 다른 쓰레드가 종료하면 바로 시작하도록 대기함
    15.                    }catch(InterruptedException e){
    16.                                    System.out.println("Interrupted Exception ::"+ e);
    17.                    }
    18.            }// end of main
    19. }//end of ThreeThreads
    20. class TestThread extends Thread{
    21.              private String whoami;
    22.              private int delay;
    23.              public TestThread(String s, int d){
    24.                              whoami =s;
    25.                              delay = d;
    26.              }
    27.              public void run(){
    28.                              try{
    29.                                       sleep(delay);
    30.                              }catch(InterruptedException e){}
    31.                              System.out.println("Hello World!"+whoami+""+new Date());
    32.                }//end of run()
    33. }// end of class TestThread   

     

    Thread 생성

    ▷ Thread를 상속하고 run() 메소드의 내용을 중복하여 정의하는 방법

    ▷ Runnable 인터페이스를 구현하는 방법

    ▷ 첫 번째 방법에서 쓰레드를 생성하려면 Thread 타입의 객체를 만들고 이 객체의 start() 메소드를 호출하면 쓰레드가 생성되어 구동된다.

    ▷ Thread 객체에서 start()를 호출하면 이 쓰레드를 thread scheduler 라고 하는 프로그램에 등록하는데 thread scheduler는 모든 쓰레드의 실행을 관리하는 시스템 프로그램이다.

    ▷ start()를 호출한다고 그 객체가 즉시 실행되는 것이 아니며 그 쓰레드가 실행될 차례가 되면 run() 메소드에 구현해둔 내용을 실행하는 것이다.

    ▷ 즉, 이 쓰레드 객체가 실행된때 동작할 내용을 미리 run() 메소드에 구현해 두어야 한다.

    ▷ 쓰레드에서 일반 프로그램(즉, 단일 쓰레드)의 main()과 같은 기능을 하는 것이 run() 메소드이다.

    ▷ main() 메소드를 JVM이 자동으로 호출해주는 것과 달리 쓰레드의 run() 메소드는 start()를 호출한 이후에 실행될 수 있는 것이다(차례가 되면).

    ▷ run() 메소드는 static 메소드가 아니므로 여러 가지 오버로딩된 메소드를 만들어 사용할 수 있다.
        기본적으로 대부분이 void run()이다. 

    ▷ 쓰레드를 만드는 방법은 위에 언급한 방법을 포함하여 두가지가 있다.

    (1) Thread를 상속하는 방식

    1. public class Test {
    2.            public static void main(String[] args) {
    3.                       new A().start();
    4.           }
    5. }
    6. class A extends Thread {
    7.           public void run() {
    8.                         System.out.println("Thread of Class A is running now...");
    9.          }
    10. }

    ▷ Test에서 만일 new A().run()과 같이 run()을 직접 호출하여도 화면에 "Thread ..."이 나타나지만 이것은 쓰레드가 생성되어 나타난 결과가 아니라 단순히 클래스 A의 run() 이라는 메소드가 호출된 것이다.

    ▷ 반드시 쓰레드 타입 객체 A의 start()를 호출하여야만 클래스 A가 자신을 쓰레드로서 thread schedulrer에 등록하게 되며 이 쓰레드가 실행될 차례가 오면 run()이 병행하여(in parallel) 실행되는 것이다.

    ▷ run()외에 클래스 Thread가 지원하는 주요 메소드는 다음과 같다.

    start()/* 쓰레드를 시작한다 */

    destroy()/* 쓰레드를 소멸시킨다 */

    getName()/* 현재 실행중인 쓰레드의 이름을 얻는다 */

    interrupt()/* 쓰레드에게 인터럽트를 알린다 */

    ▷ 이 외에도 쓰레드를 일시 중지시키기 위해서는(예를 들면 브라우져에서 다른 웹 페이지로 가는 경우) suspend()메소드가 사용되며, 일시 중지된 쓰레드를 다시 동작 시키기 위해서는 resume()메소드가 사용된다.

    ▷ Applet이 완전히 종료될 때 destroy() 메소드가 호출되는데 destroy() 메소드에서 쓰레드의 동작을 완전히 정지시키는 stop()을 호출한다.

    ▷ 위에서 예제를 inner class로 만든 것은 아래와 같다.

    1. public class Test {
    2.            public static void main(String[] args) {
    3.                          new Test().new A().start();
    4.             }
    5.      class A extends Thread {
    6.                public void run() {
    7.                            System.out.println("Thread of Class A is runnign now...");
    8.                }
    9.        }
    10. }

    ▷ 위에서 외부 클래스(Test)의 객체를 먼저 만든 후 내부클래스를 아래와 같이 new로 만들고 최종적으로 start()를 부르고 있다.

    new Test().new A().start();

    ▷ 이 프로그램에서 다음과 같이 내부클래스를 static으로 선언하면(즉, top-level의 클래스로 하여) 외부클래스의 객체를 만들 필요 없이 바로 내부클래스를 객체화하여 부를 수 있다.

    1. public class Test {
    2.              public static void main(String[] args) {
    3.                         new A().start();
    4.              }
    5.          static class A extends Thread {
    6.                       public void run() {
    7.                               System.out.println("Thread of Class A is runnign now...");
    8.                       }
    9.            }
    10. }

    ▷ 위의 프로그램을 더욱 간단히 하기 위하여 쓰레드 구동 부분을 다음과 같이 익명클래스로 만들 수도 있다.

    1. public class Test {
    2.               public static void main(String[] args) {
    3.                            Thread t = new Thread() {           // 익명클래스 시작
    4.                                             public void run() {
    5.                                                    System.out.println("Thread is running now..");
    6.                                             }
    7.                            };// 익명클래스 실행문 마감을 ; 로 해야 한다
    8.                            t.start();
    9.                }
    10. }

     

    (2) Runnable 인터페이스를 구현하는 방법

    ▷ Runnable이라는 인터페이스에 Thread와 관련된 클래스와 메소드들이 정의되어 있어 Runnable 인터페이스를 불러 쓰면 Thread와 관련된 각종 기능들을 수행할 수 있다.

    ▷ 쓰레드를 만드는 두 번째 방법으로 Runnable 인터페이스를 구현한다고 선언하고 이 클래스의 인스턴스를 Thread() 생성자의 인자로 주는 방법이다.

    ▷ 이 방법에서는 start()를 호출하기 위하여 Thread 타입의 객체를 하나 만들고 그 객체의 start()를 실행하여야 한다. Thread를 상속 받지 않았기 때문이다.

    1. public class Test {
    2.            public static void main(String[] args) {
    3.                        Thread t = new Thread(new A());
    4.                        t.start();
    5.             }
    6. }
    7. class A implements Runnable {
    8.             public void run() {
    9.                        System.out.println("Thread of Class A is runnign now...");
    10.              }
    11. }

    ▷ 위에서 Thread 객체 생성문 Thread t = new Thread(new A()) 는 다음의 표현과 같다.

    1. Runnable r = new A();// 타겟 객체
    2. Thread t = new Thread(r);

    ▷ 위와 같은 경우에 객체 r을 쓰레드 t의 target 이라고 한다.

    ▷ 한편 애플릿에서 init() 메소드 내에서 start()를 호출하는 경우에는 다음과 같이 target으로 this를 사용할 수 있다.

    1. public void init() {
    2.         Thread t = new Thread(this);
    3.         t.start();
    4. }

    ▷ 참고로 Runnable 인터페이스는 구현해야 할 메소드로 하나의 run() 만을 포함하고 있다.

    ▷ 위에 소개한 Runnable 구현 방식도 내부클래스를 사용하여 다시 작성할 수 있다.

    1. public class Test {
    2.            public static void main(String[] args) {
    3.                       Thread t = new Thread(new A());
    4.                       t.start();
    5.           }
    6.          static class A implements Runnable {
    7.                        public void run() {
    8.                                System.out.println("Thread of Class A is runnign now...");
    9.                        }
    10.           }
    11. }

    ▷ 한편 (1)에서는 new A().start()와 같이 start()를 직접 호출하였는데 여기서는 A 객체를 target으로 하여 Thread 객체 t를 먼저 만든 후 t.start() 로 호출했다.

    ▷ 이렇게 해야 하는 이유는 Runnable은 start() 메소드를 제공하지 않기 때문이다. //Thread 에서만 제공

    ▷ 위의 내부클래스를 사용하는 방식은 다시 익명클래스를 사용하는 방법으로 고쳐 쓸 수가 있다.

    1. public class Test {
    2.              public static void main(String[] args) {
    3.                         Runnable r = new Runnable() {
    4.                                  public void run() {
    5.                                          System.out.println("Thread is runnign now");
    6.                                  }
    7.                            };// 익명클래스 실행문 마감
    8.                            Thread t = new Thread(r);
    9.                            t.start();
    10.                }
    11. }

    ▷ 지금까지 run() 이 정의되어 있는 객체의 쓰레드를 구동하는 방법만을 설명하였다. 그러나 다른 객체에 있는 run() 메소드를 구동하려면 target r 자리에 다른 객체의 참조를 쓰면 된다.

    ▷ (1)과 (2)에서 소개한 쓰레드 생성법 두가지를 정리하면 다음과 같다.

    1) Thread의 하위클래스를 사용하고 run()의 내용을 중복구현

    2) Runnable을 구현하는 클래스를 선언하고 이 객체의 인스턴스를 Thread() 생성자의 인자(target)으로 전달

    ▷ 자바는 다중 상속, 즉 둘 이상의 상위 클래스로부터 동시에 계승받는 것을 허용하지 않는다.

    ▷ 1)의 방법은 Test가 이미 Thread라는 클래스로부터 한번 계승을 받았기 때문에 TestThread가 또 다른 클래스로부터 계승을 받을 필요가 있을 때 다중 계승이 되어 오류가 발생한다.

    ▷ 2)의 방법은 Test가 쓰레드를 사용할 수 있으면서 필요하다면 동시에 다른 클래스로부터 계승을 받을 수 있다는 장점이 있다.

    ▷ 쓰레드는 다음과 같은 메소드들을 제공한다.

    1. String getName()// 쓰레드의 이름을 리턴
    2. void setName(String s)// 쓰레드의 이름을 지정

    ▷ 아래는 쓰레드에 이름을 주어 초기화 한 후에 그 이름을 화면에 출력하도록 위의 예제에서 main() 부분을 고친 것이다.

    1. public static void main(String[] args) {
    2.            Runnable r = new A();
    3.           Thread t = new Thread(r, "FirstThread");
    4.            t.start();
    5.            System.out.println("Thread name = " + t.getName());
    6. }

    출력:

    Thread name = FirstThread

    Thread of Class A is running now...

     

    (3)쓰레드의 종료

    ▷ run() 메소드가 리턴되면 쓰레드도 종료되는데 이때 쓰레드가 dead 되었다고 한다.

    ▷ 이러한 dead 쓰레드를 다시 실행시키려면 start()를 다시 호출하여 run() 메소드가 실행되도록 하여야만 한다.

    ▷ 한편 어떤 쓰레드가 stop()을 호출하면 이 쓰레드는 바로 dead가 된다.

    t.stop();// 외부에서 쓰레드 t를 죽일 때

    Thread.currentThread().stop();// 쓰레드가 스스로 죽을 때

     

    Basic Control of Threads

    sleep()

    ▷ 쓰레드가 지정된 시간 동안 동작을 멈추게 하는데 다음과 같이 ms 단위 또는 ns 단위로 멈추는 시간을 조정할 수 있다. (이것은 static 메소드이다.) 지정된 시간이 지나면 이 쓰레드는 바로 실행되는 것이 아니라 ready 상태로 간다.

    1. sleep(long millisec);// InterruptedException을 throw
    2. sleep(long milliSec, int nanoSec);// InterruptedException을 throw

    ▷ sleep()에서는 InterruptedException 예외를 처리할수 있다.

    1. try {
    2.       t.sleep(10)// sleep 기본 단위는 ms
    3. } catch (InterrruptedException e) { }
    4. block()

    ▷ 네트웍으로부터 데이터 수신을 기다리는 경우에 데이터가 도착할 때까지 오래 기다릴 수도 있다. 이와같이 I/O에 오랜시간을 기다리게 되면 자바는 자동으로 이 쓰레드를 block 상태로 보낸다. 나중에 데이터를 읽을 수 있어 해당 I/O가 이루어지면 쓰레드는 ready 상태로 가게 된다.

    ▷ 한편 쓰레드는 synchronized로 선언된 코드부분의 monitor를 lock 하지 못하는 경우도 block이 될 수 있다. (뒤에 설명함)

    ▷ 기타 쓰레드가 제공하는 일반적인 메소드는 다음과 같다.

    1. isAlive()// 쓰레드가 살아있는지 확인
    2. join() // 다른 쓰레드가 종료하면 바로 시작하도록 대기함

    ▷ 어떤 쓰레드 ss가 현재 실행중인 쓰레드이면 ss.join()을 호출한 곳에서 프로그램이 기다리다가 쓰레드 ss가 종료하면 프로그램이 그곳에서부터 계속된다.

    1. ss.join();

    한편 join() 될 때까지 기다리는 최대시간을 지정할 수도 있다.

    1. nowt.join(long milliSec, int nanoSec);

     

    Thread의 상태

    ▷ 쓰레드 객체에 start()를 호출하면 쓰레드가 바로 실행되는 것이 아니라 "new thread" 상태에서 "alive" 상태로 간다.

    ▷ alive 상태 내부에서도 이 쓰레드가 현재 실행중인가 아닌가에 따라 다시 runnable 또는 not runnable 상태로 나뉘어지게 된다.

    ▷ 쓰레드가 종료되면 dead 상태로 간다. 즉 크게 보면 쓰레드는 아래와 같은 상태 변화를 격게 된다.

    new thread --> alive --> dead

    ▷ alive 상태에 있는 동안은 (runnable 상태이든 not runnable 상태이든) isAlive() 메소드 호출에 대해 true를 리턴한다.

    ▷ alive 상태 내에서도 runnable 이 아닌 상태를 다음과 같이 여러 세부적인 중간상태와 ready 상태로 다시 나눌 수 있다.

    ▷ 이 중간상태와 ready 상태들은 모두 어떤 변화를 기다리는 상태이며 항상 ready를 거쳐서만 runnable 상태 즉, CPU의 서비스를 받는 상태로 갈 수 있다.

    not runnable --> ready --> runnable

    ( waiting, asleep, blocked )

    ▷ 상태의 이동 원칙은 다음과 같다.

    ▶ ready 상태에서는 thread scheduler에 의해서 runnable 상태로만 갈 수 있고,

    ▶ runnable 상태에서는 ready 상태 또는 not runnable (즉, 모든 중간상태)로 갈 수 있다.

    ▶ 중간 상태에서는 runnable 상태로 직접 갈 수는 없고 반드시 ready 상태를 거쳐서만 갈 수 있다.

    ▷runnable 상태에서 ready 상태 또는 not runnable (즉, 모든 중간상태)로 가는 경우를 다음과 같이 나눌 수 있다.

    1) thread scheduling에 의한 이동

    2) 특정 메소드 호출에 의한 이동

     

    1) thread scheduling에 의한 이동

    ▷ 먼저 쓰레드의 우선순위에 대하여 설명하겠다.

    ▷ 모든 쓰레드는 우선순위를 갖는데 스케줄러가 ready 상태에 있는 쓰레드들을 runnable 상태로 바꿀 때 즉, 실행시킬 때 이 우선순위를 참조한다.

    ▷ 높은 우선순위의 쓰레드를 먼저 서비스 하며 동일한 우선순위의 쓰레드들은 "랜덤"하게 선택한다. (즉, 오래 기다렸다고 먼저 runnable이 된다는 보장은 없다.)

    ▷ 우선순위는 1부터 10까지의 값을 가지는데 (큰값이 높은 우선순위임) 초기의 디폴트 우선순위 값은 5이다.

    ▷ 우선순위는 쓰레드를 처음 만들 때(즉 생성자에서) 임의의 값으로 지정할 수는 없으며 일단 쓰레드를 디폴트 우선순위로 생성한 후에 setPriority() 메소드를 이용하여 변경하여야 한다.

    ▷ 우선순위의 확인은 getPriority()로 한다.

    ▷ 쓰레드 우선순위를 하나 올리는 코드 부분은 다음과 같다.

    int oldP = MyThread.getPriority();

    int newP = Math.min(oldP+1, Thread.MAX_PRIORITY);

    MyThread.setPriority(newP);

    ▷ 쓰레드 클래스가 제공하는 상수로 MAX_PRIORITY는 10, MIN_PRIORITY는 1, NORM_PRIORITY는 5의 값을 갖는다.

    ▷ 보통 사용자의 입력을 처리하는 쓰레드에 높은 우선순위를 주며 배치작업을 하는 쓰레드는 낮은 우선순위를 주는 것이 일반적이다.
      -로그인 할 때 사용자의 아이디와 패스워드 입력창이 우선순위를 갖게 된다.

    ▷ 주의: 높은 우선순위를 갖는 쓰레드와 낮은 우선순위를 갖는 쓰레드가 공존하고 있을 때 낮은 우선순위를 가진 쓰레드가 계속 실행의 기회가 없는 것은 아니다. 단 처리시간이 전체적으로 적게 배정될 뿐이다. 쓰레드에 실제로 우선순위가 적용되는 구체적인 현상은 플랫폼마다 다를 수 있다.

     

    2) 특정 메소드 호출에 의한 이동

    ▷ 다음과 같은 메소드를 사용하면 프로그램에서 쓰레드의 상태를 바꾸게 할 수 있다.

    yield()

    ▷ 쓰레드를 스스로 ready 상태로 이동시키는 메소드이다.

    ▷ 어떤 쓰레드가 yield()를 호출한 때 다른 쓰레드들이 ready 상태에서 기다리고 있었으면 그 쓰레드가 먼저 실행될 수 있으며 기다리는 쓰레드가 없었으면 이 쓰레드가 바로 다시 runnable 상태로 갈 수 있다.

    ▷ yield()는 어떤 쓰레드가 CPU 시간을 독점할 우려가 있을 때 사용되는데 OS에 따라서 yield()를 반드시 필요로 하는 경우도 있고  yield()를 호출하지 않아도 OS가 자동으로 CPU 독점을 제한하는 경우도 있다. yield()는 static 메소드이므로 직접 호출할 수 있다.

    1. try {
    2.         t.yield()
    3. } catch (InterrruptedException e) { }

     

    스케쥴러의 동작 모드

    ▷ 자바에서는 스케줄러가 다음의 두가지중 하나로 구현된다.

    ▶ pre-emptive(선점형) 모드

    ▶ time-sliced 형

    ▷ pre-emptive(선점형) 모드에서는 어떤 쓰레드가 자신이 하던일을 마치거나 중간 대기상태로 되면 ready 상태에 있던(즉, 기다리던) 다른 쓰레드들 중 높은 우선순위의 것부터 runnable이 되는 방식이다.

    ▷ 어떤 경우에는 쓰레드가 실행도중에도 (즉, runnable상태에서) ready로 강제로 내려올 수도 있는데 이 경우는 다음과 같다.

       - block 될 I/O를 요구한 경우

       - 더 높은 우선순위의 쓰레드가 실행되려고 하는 경우

    ▷ time-sliced 형은 round-robin 방식이라고도 하는데 일정한 시간단위로 쓰레드를 돌아가면서 동작시키는 방식이다.

    쓰레드 사용 예제

    ▷ 다음은 쓰레드를 두 개 만드는 예로 클래스 A의 생성자에서 쓰레드를 만든다. 각 쓰레드는 평균 500ms 간격으로 화면에 자신의 쓰레드이름을 출력한다.

    1. public class Test {
    2.           public static void main(String[] args) {
    3.                      new A("FirstThread");
    4.                     new A("SecondThread");
    5.           }
    6. }
    7. class A implements Runnable {
    8.             Thread t;
    9.             String threadName;
    10.             public A(String s) {
    11.                          t = new Thread(this);
    12.                          this.threadName = s;
    13.                          t.start();
    14.             }
    15.            public void run() {
    16.                          while(true) {
    17.                                    System.out.println(" I am " + threadName);
    18.                                    try {
    19.                                            t.sleep((int) (Math.random()*1000.0));
    20.                                     } catch (InterruptedException e) { }
    21.                           }
    22.             }
    23. }

    결과)

    I am FirstThread

    I am FirstThread

    I am FirstThread

    I am FirstThread

    I am SecondThread

    I am SecondThread

    I am SecondThread

    I am SecondThread

    I am FirstThread

    . . .

     

    동기화(Synchronized)

    ▷ 어떤 변수를 여러 쓰레드가 공동으로 엑세스하는 경우에 한 쓰레드가 이 변수를 사용하여 중요한 처리를 하는 도중에 다른 메소드에 의하여 이 변수의 값이 변하는 경우가 발생할 수 있다.

    ▷ 이와 같이 여러 쓰레드들이 어떤 데이터를 공유하는 경우에 발생하는 문제를 피하기 위하여 동기화와 기능을 제공한다.

    ▷ 멀티쓰레드 환경에서 여러 쓰레드간의 동기화를 제공하는 기법으로 다음과 같은 두가지가 있다.

    1) 모니터의 사용

    2) wait()와 notify()의 사용

     

    1) 모니터의 사용

    ▷ 자바에서 모든 객체는 flag를 가지고 있는데 이것을 lock flag로 사용할 수 있다.

    ▷ 어느 코드블록을 (또는 메소드전체를) synchronized로 선언한 경우에 이 객체의 lock flag를 점유한 쓰레드만 이 동기화 부분을 실행하도록 하여 어느 순간에 한 쓰레드만 이 코드블록을 엑세스하게 한다.

    ▷ 모든 객체는 monitor를 가지고 있다.

    ▷ 어떤 쓰레드가 모니터를 잡는 때는 sychronized 로 선언된 메소드 또는 코드블록 부분을 실행하게 되는 것이다.

    ▷ 즉, 이 모니터의 소유권을 한 쓰레드만 잡을 수 있으며 이때 다른 쓰레드들이 이 객체의 sychronized 메소드들을 접근하는 것을 block 시키는 것이다.

    ▷ 다시말하면 모든 객체는 고유의 lock(즉 모니터)를 가지고 있으며 어느 한 순간에 이 lock을 엑세스하는 쓰레드는 하나뿐이다.

    ▷ 만일 이 lock을 다른 쓰레드가 잡고 있으면 이 쓰레드는 block 상태로 가고 그 lock이 사용가능해졌을 때 ready 상태로 가게 된다.

    ▷ 한편 lock을 잡고 있던 쓰레드는 synchronized 코드블록을 다 처리하고 나면 자동적으로 lock을 놓도록 되어있다.

    ▷ 메소드 또는 코드블록을 synchronized로 세트하는 방법은 다음과 같다. 먼저 메소드 전체를 synchronized로 세트하려면 메소드 선언문에서 다음과 같이 한다.

    1. public synchronized void myMethod() {
    2. // 보호할 코드
    3. }

    ▷ 메소드 전체가 아니라 메소드내의 코드블록을 synchronized로 세트하는 방법은 다음과 같다.

    1. synchronized(this) {
    2. // 보호할 코드
    3. }

    ▷ 이때는 synchronized()의 인자에 어떤 객체의 모니터를 잡을지를 명시해야 하는데 자신의 객체의 모니터를 잡으려면 this를 쓰고 다른 객체의 모니터를 잡으려면 그 객체의 참조를 쓴다.

    1. synchronized(otherObj) {
    2. // 보호할 코드
    3. }

     

    2) wait(), notify()의 사용

    ▷ 앞에 설명한 동기화는 둘 이상의 쓰레드가 동시에 어떤 코드블록을 접근함으로써 발생하는 문제는 해결해 준다.

    ▷ 쓰레드간의 어떤 통신을 제공하지는 못한다. 예를들어 한 쓰레드가 어떤 동기화 부분의 사용을 완료한 경우 이 사실을 다를 쓰레드에게 알려주기 위하여 wait()와 notify()를 사용한다. 어떤 쓰레드가 wait()를 호출하면 waiting 상태로 가게 되는데 이 상태에서 ready로 나오려면 notify()나 notifyAll()을 받아야만 한다.

    ▷ 모든 객체는 두 개의 큐를 가지고 있는데 하나는 앞의 1)에서 설명한 synchronized 처리를 위한 lock 큐이고 하나는 쓰레드간 통신을 위한 waiting 큐이다. wait() 는 객체가 쓰레드의 실행을 중지하고 waiting 큐에 들어가서 어떤 상태변화(notify()의 발생)를 기다리게 하는 것이며, notify()는 이 객체를 사용하려고 기다리고 있는 쓰레드들 중 한 쓰레드에게 이 객체에 어떤 변화가 발생했음을 (즉, 모니터를 잡을 수 있음을) 알려 그 쓰레드가 waiting 큐에서 나와 계속 실행하도록 하는 것이다. 만일 모든 기다리는 쓰레드에게 이 사실을 동시에 알리려면 notifyAll()을 호출해야 한다.

    ▷ 여기서 주의할 것은 notify는 횟수를 축적하지 않는다는 것이다. 즉, notify는 한번 알리면 그만이고 과거의 notify 발생 횟수는 기록되지 않는다.

    ▷ 임의의 synchronized 코드를 가지고 있는 객체는 모두 monitor 객체가 될 수 있으며 wait()와 noify()를 사용할 수 있는 것이다.

    ▷ 쓰레드의 상태 변화의 관점에서 설명하면 임의의 모니터 객체는 wait() 호출로 waiting 상태로 가며 notify()의 호출로 ready 상태로 가는 것이다.

    ▷ 중요한 것은 wait()와 notify()는 모두 synchronized 코드부분 내에서만 호출할 수 있다는 것이다.

    ▷ 만일 synchronized 블록 외부에서 wait()를 호출하면 (즉, 모니터를 잡지 않고 호출하면) 자바는 IllegalMonitorStateException을 발생한다.

    ▷ 보통 wait()는 while 문 내에서 호출하게 되며 notify()는 블록내에서 아무곳에서나 해도 된다. wait()는 interrupt를 받으면 InterruptedException을 발생하므로 이를 처리해주어야 한다.

    1. try {
    2.        wait();
    3. } catch(InterrruptedException e) { }

    이상의 내용을 정리하면 다음과 같다.

     

    wait()를 호출하는 순간 호출한 쓰레드에서 이루어지는 것은

    - CPU의 사용을 중단 (waiting 상태로 간다.)

    - 객체 lock을 놓는다.

    - 객체의 waiting 큐로 들어간다.

     

    notify()를 호출하는 순간 호출한 쓰레드에서 이루어지는 것은

    - 어떤 쓰레드 하나가 waiting 큐에서 나와 ready 상태로 간다.

    - notify()를 받은 쓰레드는 반드시 monitor lock을 얻어야만 계속 진행될 수 있다. (즉, notify 되었다고 바로 쓰레드가 실행되는 것은 아니다.

    윈도우 환경에서 CTS 테스트 하기

    1.     CTS 소스를 적당한 폴더에 넣고 압축을 푼다.
    ex> D:\cts\android-cts

    2.     환경변수를 등록.
    ADB
    파일이 위치한 platform-tools Java SDK를 추가.
    ex> D:\UTIL\Android\android-sdk-windows\platform-tools
        C:\Program Files\Java\jdk1.6.0_24\bin
     

    3.     Text 파일을 열어서 아래와 같이 입력 후 확장자를 층로 변경.
    ex> cts.cmd

    입력내용
    cd d:\cts\android-cts\tools
    java -Xmx512m -cp "cts.jar;D:/UTIL/Android/android-sdk-windows/tools/lib/ddmlib.jar;junit.jar;hosttestlib.jar" com.android.cts.TestHost "D:\cts\android-cts\repository\host_config.xml"

    4.     Cts.cmd 파일을 황경변수에 등록.

    5.     윈도우에서 Command 창을 뛰운 후 생성해 준 cmd파일 이름을 쳐서 실행
    ex> cts [
    엔터]

    6.     CTS 명령어를 치면 실행된다.

      

    Android Full Source에서 CTS 파일 생성하기

    1.     리눅스의 Android 폴더로 이동 후 아래와 같은 명령어 실행
    make –j8 cts

    2.     Out/host/linux-x86/cts/android-cts.zip가 생성됨.

     

    '안드로이드' 카테고리의 다른 글

    ForeGround Service  (0) 2011.04.04
    Thread  (0) 2011.03.24
    CTS 정리  (0) 2011.03.10
    전체 컴파일 시 Dalvic Heap Memory Size 늘리기  (0) 2011.03.09
    참고 사이트  (0) 2011.03.09

     정의

    CTS(Compatiblity Test Suite)는 안드로이드 디바이스의 호환성을 테스트 하는 도구입니다.
    안드로이드 표준 SDK에서 요구하는 API들이 존재하는지, 그리고 정상적으로 동작하는지 확인하는 것입니다.

    CTS를 하는 이유는 개발자가 표준 SDK의 개발 가이드에 따라서 만든 응용 프로그램들이, CTS 인증을 받은 안드로이드 호환 디바이스에서 정상적으로 동작함을 보장하기 위함입니다.
    따라서 CTS의 통과는 안드로이드 마켓에 접속하기 위한 가장 기본적인 요건입니다..
    그리고 구글의 정책상 마켓 프로그램 뿐 아니라 GMS(Google Mobile Service)라고 불리우는 다양한 어플들(Gmail, Maps,Search, YouTube, etc) 역시 호환성 테스트 통과를 해야 라이선스를 받을 수 있게 되어 있습니다.

    즉 다시 말하자면, CTS 테스트의 통과와 기타 CDD(Compatibility Definition Document)에 기재되어 있는 하드웨어 요구치를 충족해야만 구글의 인증을 통과하고 마켓 및 구글의 서비스를 탑재할 수 있는 것입니다.

     

    구성

    리눅스에서 실행되는 CTS엔진과 타겟디바이스에 설치 및 실행되는 개별 테스트 케이스들로 구성되어 있습니다.

    Android 2.1(Éclair)버전은 총 23,107개의 시험 테스트 케이스로 구성되어 있으며, 테스트를 완료하는데도 상당한 시간이 걸립니다.

     

    원리

    Android 가 설치된 장치 와 Test 수행 PC 간에 연결이 되어 있으면 자동으로 Test App 등을 장치에서 실행하고, 그 결과를 Pass, Fail, Timeout 등으로 표시하게 됩니다.

     

    테스트 과정

    1.     CTS 다운로드(http://source.android.com/compatibility/downloads.html)
    android-cts\tools
    폴더에서 startcts 란 스크립트 파일을 열고, SDK_ROOT를 검색.
    그리고 adb 파일이 위치한 곳에 지정이 되어 있는지를 확인한 후,
    그렇지 않다면 adb파일 위치로 지정.
    ex) PATH=${SDK_ROOT}/platform-tools:${SDK_ROOT}/tools:${PATH}

    2.     테스트할 기기를 PC에 연결.

    3.     기기 설정.
    몇가지 준비해야 할 사항들이 있는데, Finger Print, Client ID등을 설정해주어야 함.
    - SD
    카드, USIM 카드 삽입, IMEI 등록
    - WIFI
    3G 망 접속 가능하도록 설정.
    - USB
    디버깅 ON
    -
    테스트 바이너리는 eng, user 와는 무관
    - Fingerprint(
    폰이 마켓에 접속할 때 인증이 되는 값) 설정
    -
    타겟 Data 파티션 영역이 25MB 이상인지 확인
    - Data
    통신이 가능한 상태인지 확인(3G,WIFI)
    -
    언어 영어로 변경
    - Screen Timeout
    30분으로 변경
    - TTS
    음성 데이터 설치

     

    4.     CTS 실행
    전체 테스트
    csjung@android-buildserver2:~$sudo ./startcts
    [sudo] password for p12522 :
    Android CTS version 2.3_r2
    Device(EF10S01110118000291) connected
    cts_host > start –plan CTS


    부분 테스트
    cts_host > start –-plan CTS –p <java_package_name>
              ex> start –plan CTS –p android.location

    Fail
    항목 수정후 테스트
    cts_host > start –-plan CTS –t <test pull name>
              ex> start –plan CTS –t android.app.cts.ActivityManagerMemoryInfoTest
                                                                       #testDescribeContents

    Package
    이름은 ls –p 로 확인 가능

    간혹 adb devices 명령을 통해 기기 확인했을 때, 퍼미션이 없다고 나오는 경우에는 root 권한으로 adb server를 시작해주면 됩니다.

     

    테스트 이후의 작업

    1.     테스트 결과 확인
    테스트의 결과는 xml 파일의 형태로 생성됨.
    android-cts/repository/results/<
    테스트시작 시간>/testResult.xml
    result
    폴더에 보면 <테스트시작 시간>.zip 파일을 볼수 있는데, 이 파일은 CTS 결과 리포트 계정을 통해 구글에 전달할 때 사용됨.
    * testResult.xml
    이 안열릴 경우 : 에디터로 파일을 연 후 &#(); 스트링을 찾아서 제거 후 저장.

    2.     시험 결과 분석 및 디버깅
    xml
    파일에는 Error 종류 및 기대값, 시험값, 오류가 발생한 함수, 파일 위치가 표시
    또한 xml을 에디터로 열게 되면 오류가 발생한 부분의 stack trace를 볼수 있음.

    3.     결과 등록 및 구글 CTS 인증
    CTS결과 서버 등록한다.
    - http://sites.google.com/a/android.com/compatibility/Home

    GMS launch checklist
    를 작성한다.
    - http://sites.google.com/a/android.com/compatibility/Home
    checklist
    서버 업데이트.

    GMS build approval submission
    를 작성한다.
    - https://sites.google.com/a/google.com/gms_distribution/
    -
    구글에 인증을 요청한다.  - 인증 승인까지는 약 14일이 소요된다.

     

     


     

    CTS 테스트 종류

    Software 
    - Managed API Compatibility :
    달빅 실행 환경에 대한 보장 
    - Soft API Compatibility  :
    런타임 API에 대한 보장
    -- Permissions :
    모든 퍼미션 상수에 대한 지원 및 강제구조 보장
    -- Build Parameters : android.os.Build
    글래스 보장
    -- Intent Compatibility :
    인텐트 구조 보장
    --- Core Application Intents :
    디폴트 탑재된 애플리케이션 교체시 기존 인텐트 구조 보장 
    --- Intent Overrides :
    인텐트 변경을 통한 애플리케이션 차별화 금지
    --- Intent Namespaces : android
    패키지내의 인텐트 네임스페이스 보장
    --- Broadcast Intents :
    시스템에 의해 발송되는 브로드캐스트 인텐트 발송 보장
    - Native API Compatibility : NDK
    에서 런타임 환경 보장
    - Web API Compatibility : WebView
    구현물 실행 환경 보장
    - API Behavioral Compatibility : Managed, Soft, Native, Web API
    의 일관성 보장
    - API Namespaces :
    안드로이드 Java 패키지와 클래스 네임스페이스 보장
    - Virtual Machine Compatibility : Dex
    바이트코드 실행 보장 및 달빅 VM 보장
    - User Interface Compatibility :
    표준 UI API 보장
    -- Widgets : "AppWidget"
    의 런처내에서의 실행 보장
    -- Notifications :
    사운드,진동,발광,상태바 노티피케이션 지원 보장.
    -- Search :
    시스템 전반에 대한 검색 기능 보장.
    -- Toasts :
    토스트 기능 보장.
    Reference Software Compatibility : 참조구현된 테스트 애플리케이션 실행 보장.
    Application Packaging Compatibility : aapt로 생성한 apk 파일 설치 및 실행 보장.
    Multimedia Compatibility : 안드로이드 코어 미디어 포맷에 대한 코덱 지원 보장.
    Developer Tool Compatibility : adb,ddms,monkey등과 같은 개발 도구 지원 보장.
    Hardware Compatibility 
    - Display 
    -- Standard Display Configurations :
    표준 디스플레이 설정 지원 보장.
    -- Non-Standard Display Configurations :
    비표준 디스플레이 지원시 CTS팀에 컨택.
    -- Display Metrics : android.util.DisplayMetrics
    에 정확한 값 리포트.
    - Keyboard : IMF
    지원 및 소프트키보드 탑재, 
                  android.content.res.Configuration
    에 존재하지 않는 하드웨어 키보드 사용 금지 보장.
    - Non-touch Navigation :
    터치이외의 네비게이션 제거시, 
                              android.content.res.Configuration
    에 리포트 보장.
    - Screen Orientation :
    동적 오리엔테이션 지원 및 기타.
    - Touchscreen input :
    터치스크린 포함 보장(정전용량 또는 저항막 방식 사용가능).
    - USB :
    표준 USB-A, 클라이언트, 호스트, USB기반 ADB, USB 대용량 스토리지, 마이크로 USB 형태 지원 등.
    - Navigation keys :
    ,메뉴,백 기능을 하드웨어 키 및 소프트웨어 등으로 명백히 보장.
    - WiFi : 802.11b
    802.11g 지원 보장 및 802.11a 지원 가능.
    - Camera :
    최소 2 megapixels 이상의 해상도 보장 및 기타.
    -- Non-Autofocus Cameras :
    오토 포커스 기능 미지원시 ro.workaround.noautofocus 값 지정 및 기타.
    - Accelerometer : 3
    축 가속도 센서 포함 및 최소 50Hz 이벤트 보장 등.
    - Compass : 3
    축 나침판 포함 및 최소 10Hz 이벤트 보장 등.
    - GPS : GPS
    assisted GPS 포함 보장 등.
    - Telephony : 
    텔레포니 기능을 포함 및 포함하지 않을 수 있음.
    - Bluetooth :
    블루투스 포함 및 최소로 OPP, PBAP, RFCOMM, A2DP, AVCRP 프로파일 지원 보장.
    - Volume controls :
    볼륨 업 다운 기능 포함 및 하드웨어 또는 다양한 형태로 지원할 수 있음.
    - Memory :
    최소 256MB RAM 512MB 플래시 탑재 권장.
    Performance Compatibility : 일정시간내의 애플리케이션 런칭타임 보장 및 동시적 애플리케이션 보장.
    Security Model Compatibility : 
    - Permissions :
    안드로이드 퍼미션 모델 지원 보장.
    - User and Process Isolation : UID
    기반의 프로세스 분리 및 동일 UID기반의 복수개의 어플 샐행 보장.
    - Filesystem Permissions  :
    보안 및 퍼미션 기반의 파일 접근 퍼미션 지원 보장.

     문서정리


    '안드로이드' 카테고리의 다른 글

    Thread  (0) 2011.03.24
    윈도우 환경에서 CTS 돌리기  (0) 2011.03.11
    전체 컴파일 시 Dalvic Heap Memory Size 늘리기  (0) 2011.03.09
    참고 사이트  (0) 2011.03.09
    Activity Rotation 시 처리 프로세스  (0) 2011.03.08

    + Recent posts