2014년 12월 10일 수요일

android - LocationClient issue

안드로이드에서 위치정보 관련 API는 Android API와 Google Play Service API가 있습니다.

1. Android API
LocationManager는 상황에 따라 개발자가 직접 프로그래밍 해주어야 할 것이 상대적으로 많습니다.

2. Google Play Service API
다소 수월하게 프로그래밍을 할 수 있으며 보다 강력하고 신규 API 적용이 수월합니다.
처음에는 LocationClient를 사용해서 위치정보 관련 기능을 처리 했으나, Google 서비스들이 많이 추가되면서 효유적인 사용을 위해 GoogleApiClient와 LocationServices로 구현되게 변경되었습니다.

Google Play Service API 업데이트 관련 이슈
LocationClient는 deprecated되었지만 동작에는 문제가 없었습니다.
그러나 이번 22버전으로 업데이트 되면서 해당 API가 사라졌습니다.
API Reference 문서에서도 사라졌습니다.

그래서 이제는 Google Play Service API로 위치정보를 처리하는 앱에서는 반드시 GoogleApiClient와 LocationServices로 구현을 해야 합니다.


참고) 구글의 문서가 아직 업데이트가 안되어서 인지 안드로이드 개발자 트레이닝 페이지에는 위치정보 처리를 위한 가이드가 아직도 LocationClient를 사용하는 문서로 설명되어 있네요.

https://developer.android.com/training/location/retrieve-current.html

2014년 10월 28일 화요일

android - nexus 6, nexus 9을 위한 준비

nexus 6 를 위한 준비

1. xxxhdpi 리소스
  • 730 x 410 dp에 493ppi를 위한 리소스 처리
  • 해당 리소스가 없다면 nexus 6에서는 xxhdpi 를 스케일 업해서 사용함

2. AndroidManifest.xml에서 <compatible-screens> 태그 사용 제거 권고
  • Nexus 6에서 Play Store의 App이 노출되지 않을 수 있음
  • 만일 사용을 계속 하고자 한다면 신규 스크린 정보를 추가 후 재 배포를 반복해야 함


nexus 9 을 위한 준비

1. 화면 처리
  • 1025 x 768 dp에 288ppi를 위한 리소스로 xhdpi를 사용하기에 추가적인 리소스는 필요치 않음
  • 기본 태블릿은 주로 16:10의 화면비를 갖지만 nexus 9는 4:3의 화면비를 갖기에 레이아웃에 고려가 필요함

2. 64bit 지원
  • 안드로이드 최초의 64bit 지원 단말로 64bit ARM 을 지원해야 함
  • NDK app인 경우 Application.mk 파일에 다음과 같이 세팅(nexus 9은 arm64-v8a)

 APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64

 또는 

APP_ABI := all



3. 하드웨어 키보드 지원
  •  단방향 화면(가로 또는 세로 고정)을 사용하지 않을 것을 권고
 --> 넥서스 9은 다른 태블릿과 다르게 기본 방향이 세로이며 하드웨어 키보드 연결시 가로로 변경됨
  •  app 에서 키보드의 조작만으로 기본적인 사용이 가능하게 하길 권고
 -->  nexus 9 부터 키보드를 판매시작, 이 후 키보드가 번들 형태로 제공되거나 하이브리드 노트북 형태에서 안드로이드 사용을 위한 준비




공통

1. 화면 크기에 유연하게 대응는 레이아웃 구성
  • 가로, 세로 또는 화면 크기에 대한 대한 레이아웃 구성보단 화면 dp 값에 따른 레이아웃 구성을 권장
  • 컨텐츠를 표시하기 위해 필요한 최소 크기와 최대 크기를 고려하여 레이아웃 구성
2. Android 5.0과 Material 의 이점을 사용
  • android:targetSdkVersion을 21로 세팅
  • 이외 추가된 신규 API를 AppCompat v7을 사용하여 적용

출처: http://android-developers.blogspot.kr/2014/10/getting-your-apps-ready-for-nexus-6-and.html

android App 64bit 호환 처리하기

Android App 64bit 호환 처리하기

- JNI를 사용하지 않는다면 호환에 문제가 없다.
- JNI를 사용한 어플이라면 다시 빌드하여 배포해야 한다.

1.  android-ndk-r10c 다운로드

 다운로드 URL : https://developer.android.com/tools/sdk/ndk/index.html

2. Application.mk 설정

 예1) NDK가 지원하는 아키텍처 전체 지원
 APP_ABI := all
 예2) arm & x86 계열만 지원
 APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64

3. 빌드하여 마켓에 다시 올린다...

 arm & x86 계열만 지원하게 빌드된 예시 이미지

2014년 5월 20일 화요일

language - Scala

Scala

http://www.scala-lang.org/

특징
  • 객체지향 프로그래밍언어와 함수형 프로그래밍 언어의 장점을 결합
  • 자바가상머신(JVM)에서 동작
  • 보다 간결한 코딩
  • 자바 라이브러리 사용가능 및 상호연계
  • 병행프로그램 작성 수월

관련 책소개

스칼라 프로그래밍(쉽게 배워서 빨리 써먹는)

http://book.naver.com/bookdb/book_detail.nhn?bid=7188923

폴리글랏 프로그래밍

http://book.naver.com/bookdb/book_detail.nhn?bid=7440444

2014년 5월 12일 월요일

language - java 8

java 8

 Java 8 (JSR-337)은 새로운 기능과 향상된 기능 및 버그 픽스가 포함되어 Java 프로그램 개발 및 실행의 효율을 높여주는 최신 Java 릴리스.


java 8의 특징적 변화

1. Jigsaw
 자바 응용프로그램과 자바 플랫폼을 위한 모듈화 기능을 대비할 수 있다. (향후 지원)

2. Nashorn JavaScript Engine
 VM에서 경량화된 고성능의 JavaScript 엔진 내장

3. Autoconf-Based Build System
 빌드 속도 향상을 위해 ./configure-style의 autoconf 빌드시스템 지원(소개? : 미지원?)

4. JavaFX
 RIA(Rich Internet Application)를 위한 그래픽과 미디어 통합 패키지로서 개발되었지만 Swing GUI을 대체하기 위해 지속적으로 개발되어 Java 8의 기본 GUI로 자리매김.

5. Mission Control / Flight Recoder
 Oracle JRocket JVM에서 제공하는 기능을 지원

6. Lambda
 멀티코어 응용프로그램을 작성할때 보다 쉽고 간결하게 작성할 수 있다.

7. Default Methods for Interface (Extension Methods)
 다중 상속에 대한 프로그래밍 모델 제공

8. Annotation on Types
 Annotation을 method의 parameter의 type에도 적용 지원

9. Generalized Target-Type Inference
 Generic 사용성 개선

10. Access To Parameter Names at Runtime
 method, constructor의 parameter의 name을 runtime 시점에 획득가능

11. Remove the Annotation-Processing Tool (apt)
 apt 관련 tool, API, docs 제거됨

15. Bulk Data Operations for Collections
 Java를 위한 Filter, Map, Reduce를 제공

16. Parallel Array Sorting
 멀티코어를 활용하는 sorting 기능 제공

17. Date and Time API
 새로운 Date, Time, Calendar API 추가

18. JDBC 4.2 지원
 사용성과 이식성 향상

19. Base64 Encoding and Decoding
 Base64 기본 API에 추가됨

20. Reduced core-library memory usage
 메모리 사용량 감소

21. Statically Linked JNI Libraries
 Embedded Application을 위한 JNI 라이브러리의 정적 링크

22. Locale Data Packing
 CLDR(Common Locale Data Repository) 지원

23. Unicode 6.2
 Unicode 6.2 지원

24. Enhanced Certificate Revocation-Checking API
 인증서버 연결관련 확장된 기능 제공

25. HTTP URL Permission
 URL 기반 네트워크 퍼미션 추가

26. Launch JavaFX Application
 Command line에서 JavaFX 어플리케이션 실행기능 제공

27. Remove The Permanent Generation
 Hotspot VM에서 Permanent Generation이 제거되었다.

28. Small VM
 3Mbytes 미만의 경량의 VM 구성 가능

29. Compact Profiles
 경량 디바이스에서 배포되고 실행될 어플을 위해 Java SE Spec의 서브 프로파일을 정의함

30. Reduce class Metadata Footprint
 경량 디바이스에서 성능 향상을 위해 class의 metadata memory footprint를 줄임

31. Leverage CPU Instructions for AES Cryptography
 불필요한 AES key의 재확장을 피하고 가용할 경우 x86 CPU의 AES 명령셋을 사용하여 성능을 향상시킴

32. Mechanical Checking of Caller-Sensitive Methods
 JDK의 메소드 핸들링의 보안성을 향상시킴

33. Charset Implementation Improvements
 Chartset 성능 향상

34. JAXP 1.5
 JAXP 1.5로 버전업

...


eclipse에서 java 8 사용하기

현재 eclipse 릴리즈 버전인 Kepler 까지는 java 8을 지원하지 않습니다.
그래서 해당 java 8에서 제공하는 특징을 사용하기 위해서는 java 8을 위한 eclipse plugin을 설치해야 합니다.

1. eclipse의 메뉴에서 Help ==> Eclipse Marketplace 선택

2. Search에서 "java 8"로 검색하여 "Java 8 Support for Eclipse ..." 를 설치

3. Project의 JRE System Library에서 JavaSE1-8로 변경


build tool - Gradle

Build tools
  • Ant
  • Maven
  • Ivy
  • Gradle
  • ...
Gradle
  • Groovy 기반 DSL(Domain Specific Language) 채용
  • 선언적 언어의 특징을 제공
  • 멀티 프로젝트 관리가 쉬움
  • Gradle Wrapper를 이용하여 Gradle이 설치되지 않은 환경에서도 빌드 가능
참고 URL - http://www.gradle.org, http://kwonnam.pe.kr/wiki/gradle

Groovy
  • 타입이 유연하고  간결한 동적 객체지향 프로그래밍 언어
  • 자바, 루비, 스몰토크, 파이썬 언어에서 영향을 받음
  • 자바와 상호 연동
  • 클로저(List Processing Programming) 지원
참고 URL - http://groovy.codehaus.org, http://groovy.codehaus.org/Korean+Home

참고)
 Grails - Ruby on Rails를 Groovy용으로 개발한 웹 개발 프레임워크


eclipse에서 android project를 Gradle로 빌드/배포

1. 적용방안
  • Gradle Project로 전환하여 사용하기
    •  Gradle IDE plugin 3.5 버전을 설치하여 테스트한 결과 plugin 버그인지 안드로이드 프로젝트로 세팅된 정보에 문제가 생긴다.
    •  eclipse에 완전히 통합된 느낌이 없다.
    •  개발환경에서는 오히려 불편한점이 더 많다.
  • Gradle 세팅파일만 추가하여 배포시에만 Gradle 사용하기
    • 기존 안드로이드 프로젝트에 아무런 영향을 미치지 않는다.
    •  IDE에 영향을 받지 않는다.
  ==> Eclipse에서 Android Project를 Gradle Project로 전환하여 개발하는것은 다소 문제가 있어 보여 두번째 방안으로 적용하는 방법을 설명하였다.


2. Gradle 설치
  • http://www.gradle.org 에서 다운로드 후 압축해제
  • 해당 경로로 GRADLE_HOME 환경변수 추가
  • 해당 경로의 bin 디렉토리를 PATH 환경변수에 추가
3. Eclipse에 Gradle plugin 설치
  • Eclipse 메뉴의 Help >> Eclipse Marketplace 에서 gradle 로 검색
  • Gradle 설정파일 편집기인 Minimalist Gradle Editor 설치
  • Gradle Integration for Eclipse 설치

4. 기존 Android Project에 gradle 적용하기

 1) 단일 프로젝트 구조

  (1) Gradle에 Android SDK 경로 설정
  • 프로젝트 홈 디렉토리에 "sdk.dir" 이름으로 Android SDK Home 경로가 설정된local.properties를 추가
  • 또는 ANDROID_HOME  환경변수 추가

  (2) 프로젝트 홈 디렉토리에 build.gradle 파일 생성
  • 직접 파일을 생성하여 Gradle을 작성할 수 있다.
  • 프로젝트 Export 기능에서 제공하는 "Generate Gradle build files" 기능을 사용하여 손쉽게 작성할 수 있다.
 ==> Eclipse에서 제공하는 "Generate Gradle build files" 기능으로 생성된 build.gradle 파일을 수정하여 사용하는것이 효과적이다.

  (3) build.gradle 파일 수정
  • lint 에러가 발생할 경우 build가 중단되는 문제
 ==> android { ... } 안에 다음 항목 추가
    lintOptions {
        abortOnError false
    }
  • java 소스파일에 입력된 한글이 깨지는 문제
 ==> android { ... } 안에 다음 항목 추가
    compileOptions.encoding = '인코딩문자열'

==> 인코딩문자열은 java 소스파일과 같은 인코딩으로


 2) 복수 프로젝트 구조

  (1) Gradle에 Android SDK 경로 설정

 ==> 단일 프로젝트와 동일

  (2) 프로젝트 홈 디렉토리에 build.gradle 파일 생성
  • 직접 파일을 생성할 경우 프로젝트별로 각각 Gradle 파일을 생성해야 한다.
  • 프로젝트 Export 기능에서 제공하는 "Generate Gradle build files" 기능을 사용하여 생성시 메인 프로젝트만 선택하면 메인 프로젝트에서 참조하는 android library project를 자동으로 인지하여 선택하게 하며 참조된 프로젝트들 각각에 build.gradle 파일을 생성한다.

  (3) 메인 프로젝트 홈 디렉토리에 settings.gradle 파일 생성
  • android library project를 사용하는 복수 프로젝트 구조에서는 사용하는 library 프로젝트를 include하여야 한다. 해당 기능은 Eclipse Export 기능에서 생성되지 않는다.
  • eclipse에서 사용하는 프로젝트 방식에서는 메인 프로젝트와 라이브러리 프로젝트가 모두 workspace 하위에 동일한 레벨의 경로에 위치한다. 이럴 경우 gradle에서는 라이브러리 프로젝트를 못 찾는 문제가 발생하며 이를 해결하기 위해 "projectDir"로 프로젝트 홈 경로를 지정해주어야 한다.
 ==> 프로젝트 디렉토리 구조
 - workspace
     - MainProject
     - LibProject1
     - LibProject2

==> setting.properties 설정 예
 include ':LibProject1', ':LibProject2'
 project(':LibProject1').projectDir = new File("../LibProject1");
 project(':LibProject2').projectDir = new File("../LibProject2");

 5. 빌드 및 기타 기능 추가

 1) 빌드
  • 메인 프로젝트 홈 디렉토리에서 gradle [task 이름]형식으로 빌드
 ==> gradle build
 ==> gradle clean build
 ==> gradle build --daemon

 2) signed apk 설정
  • 메인 프로젝트의 build.gradle에 signing 설정 추가
 ==> android { ... } 안에 다음 항목 추가 : keystore경로는 keystore파일의 실제 경로를 프로젝트 홈 기준 상대경로로 지정하고 별칭은 프로젝트 영문이름을 사용


     signingConfigs {
          release {
              storeFile file("keystore경로")
              storePassword "notYourRealPassword"
              keyAlias "별칭"
              keyPassword "notYourRealPassword"
         }
     }
     
     buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }


 ==> password 입력 task 추가


task askForPasswords << {
    def storePw = new String(System.console().readPassword("Keystore password: "))
    def keyPw  = new String(System.console().readPassword("Key password: "))

    android.signingConfigs.release.storePassword = storePw
    android.signingConfigs.release.keyPassword = keyPw
}

tasks.whenTaskAdded { theTask -> 
    if (theTask.name.equals("packageRelease")) {
        theTask.dependsOn "askForPasswords"
    }
}


2014년 4월 30일 수요일

android - Custom Lock Screen

Custom Lock Screen

안드로이드는 루팅하지 않은 단말기에서 락스크린을 교체할 수 없다. 런처처럼 정상적인 방법으로 대체하는 방법을 제공하지 않는다.

다만 Android 4.2 부터 락스크린에 위젯을 표시할 수 있는 락스크린 위젯 기능이 추가되었다.
하지만 4.1 이하 단말기에서는 사용할 수 없다.

Android Version에 영향을 덜 받고 원하는 화면을 락스크린에 표시할 방법은 없을까?
락스크린과 유사한 동작을 하는 어플을 만들고자 한다면 필요한 기능은 무엇일까?

  • 기존의 락스크린이 표시되거나 해제되는 시점을 알 수 있어야 한다. 최소한 락스크린이 표시되는 시점은 알아야 한다.
  • 기존의 락스크린 위에 화면을 표시 할 수 있어야 한다.
  • 기존의 락스크린을 해제할 수 있으면 좀더 유연한 동작이 가능할 것 같다.
  • 보안이 설정된 락스크린은 어떻게 처리하는게 바람직한가?
  • 전화, 메시지, 기타 락스크린을 사용하는 알림 기능에 대해 대처해야 한다.


구현이 가능한지 여부를 확인해보자.

락스크린의 상태변경에 대한 콜백기능이 없다.

SCREEN_ON, SCREEN_OFF 상태를 브로드캐스트 메시지로 수신할 수 있다. 다만 코드로 등록하는 경우만 해당 메시지 수신이 가능하여 통상 서비스를 이용한다.

서비스는 LMK에 의해 종료될 수 있기에 SLEEP 상태에서 화면 On/Off 메시지를 수신할 방법이 필요하다.
AlarmManager를 이용하여 주기적으로 서비스 구동요청을 할 수 도 있고 foreground service 기능을 사용할 수 도 있다.

Android 5 부터 WindowManager.LayoutParams의 FLAG_SHOW_WHEN_LOCKED 플래그 설정으로 락스크린 위에 표시되는 액티비티를 지원했다.

Android 1 부터 KeyguardManager를 이용해 보안이 적용되지 않은 락스크린을 해제하는 기능을 제공했으며 이 기능을 Android 5 부터는 WindowManager.LayoutParams의 FLAG_DISMISS_KEYGUARD 플래그 설정으로도 제공하고 있다.


결론 

화면 On/Off 시점을 알고 액티비티를 락스크린 위에 표시하는 기능과 기존 락스크린을 해제할 수 있는 기능을 조합해 기존 락스크린을 어느정도 대체할 수 있고 이를 이용한 많은 락스크린 앱들이 플레이 스토어에 올라와 있다.

락스크린과 유사한 UX를 구현하고 보안 락이 걸려있지 않은 경우 기존의 락 스크린을 해제하여 락스크린 기능을 대체하여 사용할 수 있다.

개인적인 사견으로는 정상적인 방법으로 락스크린 자체를 대체하는 방법을 제공하지 않는데는 보안문제가 가장 큰것 같다.



2014년 4월 29일 화요일

programming paradigm - AOP (Aspect Oriented Programming)

영역 지향 프로그래밍 또는 국면 지향 프로그래밍

 - 프로그램의 영역이 수행 프로그램의 컴파일 방법을 결정하는 프로그래밍 방법

Aspect : 프로그램의 성격을 나타내는 서브 프로그램, 단위 모듈

실예)
 - 단위 모듈(로깅, 예외처리, 트랜잭션, 보안 등)을 설정파일로 로직의 수정없이 적용
 - plugin


AOP를 지원하는 패턴

DI (Dependency Injection) 또는 IoC (Inversion of Control)

 - 코드의 의존성을 설정 (DI Tool)
 - 코드의 의존성을 제거 (DI Pattern)
 - 불필요한 의존도를 줄여 직교성을 높임

DI 종류

 - 생성자 기반 DI
 - Setter 기반 DI
 - Annotation 기반 DI
 - 설정파일 기반 DI


AOP를 지원하는 툴

AspectJ

 - compile된 bytecode에 대한 re-compile을 통한 새로운 bytecode 작성으로 인한 AOP가 갖는 성능 저하의 최소화
 - pointcut, advisor 의 직접적인 선언을 이용한 보기 쉬운 코드작성

 참고)
  • Pointcut : AOP가 적용되는 시점을 정의
  • Advice : Pointcut과 연동되어 AOP가 적용될 method를 의미
  • Advisor : Point + Advice로 정의된 class를 의미
Spring AOP





architecture - GoF Design Pattern

GoF Design Pattern (Gang of Four : Erich Gamma, Richard Helm, Ralph Johnson, Jone Vlissides)


디자인 패턴 관계도


(출처 : http://nasir.files.wordpress.com/2009/11/design_patterns1.jpg)


객체생성에 관한 패턴

1. Factory Method : 팩토리 메소드 패턴
 - 객체를 생성하는 작업을 대신해주는 패턴
 - 객체 생성이 복잡할때 유용

2. Singleton : 싱글톤 패턴
 - 클래스의 인스턴스가 오로지 하나만 존재해야 하는 경우 사용
 - 무분별한 객체 생성을 방지

3. Prototype : 프로토타입 패턴
 - 동일한 객체를 여러번 생성해야 하는 비용을 줄이기 위한 패턴으로 한번 로딩된 객체를 복제하여 사용

4. Builder : 빌더 패턴
 - 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴
 - 객체를 이루는 구성요소를 분리하여 객체의 세부 구성요소 클래스를 별도로 만들고 그 구성요소 클래스를 조합하여 하나의 객체를 만드는 것

5. Abstract Factory : 추상 팩토리 패턴
 - 인터페이스를 통해 생성하려는 객체의 실제 타입을 은닉


행동에 관한 패턴

1. Iterator : 반복자 패턴
 - 내부 구현에 대한 이해 없이 자료의 집합체를 탐색할 수 있게 해줌

2. Template Method : 탬플릿 메소드 패턴
 - 메소드 실행 순서를 추상 클래스에서 정의하며 실제 동작하는 알고리즘은 구현 클래스에서 정의하도록 함

3. Strategy : 전략 패턴
 - 알고리즘 인터페이스를 정의하고 각각의 알고리즘을 클래스별로 캡슐화 하여 각각의 알고리즘을 교체가 가능하게 함
 - 유지보수의 효율성을 높이기 위해 동적으로 알고리즘을 변경할 수 있는 패턴

4. Visitor : 방문자 패턴
 - 객체의 구조와 기능을 분리시키는 패턴
 - 구조는 변하지 않으며 기능만 따로 추가되거나 확장되는 경우 사용
 - Composite Pattern과 함께 사용되는 경우가 많음

5. Chain of Responsibility : 책임 연쇄 패턴
 - 요청을 처리할 수 있는 기회를 하나 이상의 객체에게 부여함으로써 요청하느 객체와 처리하는 객체 사이의 결합도를 없애는 패턴
 - 요청을 해결할 객체를 만날 때까지 객체 고리를 따라서 요청

6. Mediator : 중재자 패턴
 - 모든 클래스간 복잡한 로직을 캡슐화하여 하나의 클래스에 위함하여 처리하는 패턴

7. Observer : 옵저버 패턴
 - 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 통지하여 자동으로 갱신처리 되는 패턴
 - 일방적 통지 방식의 패턴

8. Memento : 메멘토 패턴
 - 객체의 상태 정보를 저장 및 복원하는 패턴
 - 객체의 내부 상태정보만 가지는 클래스를 따로 생성하여 관리하는 구조

9. Command : 커맨드 패턴
 - 메소드(작업요청)을 객체의 형태로 캡슐화하는 것
 - 특정한 일을 하는 기능을 따로따로 객체화하여 요청의 종류와는 무관하게 프로그램 작성이 가능하게 함

10. Interpreter : 해석자 패턴
 - 문법 규칙을 클래스화 한 구조로 일련의 규칙으로 정의된 언어를 해석하는 패턴
 - 스크립트나 컴파일러 등 언어분석기
 - 통신 프로토콜, SQL 구문 분석기 등

11. State : 상태 패턴
 - 객체의 상태에 따라 각각의 행위를 변경할 수 있게 캡슐화


구조에 관한 패턴

1. Adapter
 - 클래스의 인터페이스를 사용자가 기대하는 인터페이스 형태로 적용시킴
 - Wrapper 와 유사

2. Bridge : 브리지 패턴
 - 구현부에서 추상층을 분리하여 각자 독립적으로 변형할 수 있게 하는 패턴
 - 상속을 이용한 패턴으로 확장 설계에 용이

3. Composite : 컴포지트 패턴
 - 객체와 객체 그룹을  구분없이 하나의 인터페이스로 다룰 수 있게함

4. Decorator : 데코레이터 패턴
 - 객체에 기능을 덧 붙이는 패턴
 - 기능 확장시 서브클래스의 대안

5. Facade : 파사드 패턴
 - 통일된 인터페이스를 통해 복잡한 서브시스템들을 간단히 사용하도록 만든 패턴

6. Flyweight : 플라이웨이트 패턴
 - 데이터를 공유 사용하여 메모리를 절약하는 패턴
 - 객체는 풀로 관리

7. Proxy : 프락시 패턴
 - 실제 사용하려는 객체를 대신해서 역할을 해주는 패턴으로 원래 객체의 접근제어를 목적으로 사용됨


참고)

GoF Design Pattern Reference Card

http://www.mcdonaldland.info/files/designpatterns/designpatternscard.pdf








architecture - UML

UML (Unified Modeling Language)

 객체지향 소프트웨어 모델링을 위한 표준 그래픽 언어


요구사항 표현

1. 사용사례 다이어그램(Use case diagram)

 1) 설명

 - 시스템과 외부와의 상호 작용을 묘사하는 다이어그램

 2) 다이어그램 표시 요소

 - 사용사례 : 시스템의 목적
 - 액터 : 사용사례를 이용하는 사람이나 외부 시스템
 - 관계: 액터와 사용사례간 관계

 3) 관계 표현

  - 확장(Extension) : 선택적인 인터랙션을 명시적으로 나타낼때 또는 예외적인 사례를 다룰때 사용(상속개념과 유사)
  - 일반화(Generalization) : 여러 유사 사용사례로 일반화 함
  - 포함(Inclusion) : 여러 사용사례의 공통적인 부분을 표현

 4) 장점
  - 시스템의 범위를 정하는데 도움이 됨
  - 개발과정을 계획하는데 도움이 됨
  - 요구를 개발하고 검증하는데 사용됨
  - 테스트케이스를 정의하는데 기초가 됨
  - 사용자 매뉴얼 구성하는데 사용될 수 있음


정적인 부분 표현

1. 클래스 다이어그램 (Class Diagram)

 1) 설명

  객체지향 시스템의 가장 근간이 되는 다이어그램으로 시스템의 정적인 구조를 나타내는 다이어그램

 2) 다이어그램 표시 요소

  - 클래스 : 자료 타입 그 자체를 나타냄
  - 속성 : 객체의 상태 또는 성질
  - 오퍼레이션 : 클래스와 그 인스턴스에 의하여 수행될 함수를 나타냄
  - 연관관계 : 클래스 인스턴스 사이의 관계를 나타냄

 부가설명)
  - 추상 클래스 : 인스턴스가 없는 클래스
  - 추상 오퍼레이션 : 구현이 없는 오퍼레이션

 3) 가시성 표현
  • '+' : public : 패키지 외부에서도 접근 가능
  • '#' : protected : 하위 클래스로부터의 접근이 가능
  • '-' : private : 클래스 내부에서만 접근이 가능
  • '~' : package : 속한 동일 패키지 내에서만 가능
 부가설명)
    
 유도 속성 ( '/' : derived)
  - 다른 속성에 의해 결정될 수 있는 속성 
  - 가시성과 속성의 이름 사이에 표시


 4) 관계 표현
  - '*' : 연관관계 끝에 다중도를 표시
  - Aggregation : 전체와 부분의 관계를 나타냄 (is part of)
  - Composition : 합성관계는 Aggregation보다 강한 관계로 집합이 소멸되면 부분도 소멸됨(is composed of)
  - Propagation : 전체 개념의 오퍼레이션이 부분 개념의 오퍼레이션에 의하여 구현되는 현상

 부가설명)
  - 인터페이스 : 객체집합이 가지는 행위의 일부를 가시화 한 것으로 클래스와 유사하나 인스턴스 변수와 구현 메소드가 없음

2. 객체 다이어그램 (Object Diagram)

 1) 설명

 인스턴스들과 링크 파악, 객체들 사이의 관계 표현하는 다이어그램
  • Data/Object 구조를 설명하고 Snapshot 구체화
  • 분석과 설계단계에서 정의됨
참고)

 클래스 다이어그램과 차이점 : 객체들의 이름 밑에 밑줄이 그어지며 연관된 모든 인스턴스들이 표현됨


3. 패키지 다이어그램 (Package Diagram)

 1) 설명

 관련된 클래스를 패키지로 묶어 의존도를 낮추기 위해 사용

 2) 패키지

  - 공통적인 특성을 가진 클래스들의 모임
  - 재사용의 단위가 됨

 3) 관계

  - 점선 화살표는 의존관계를 나타냄
  - 컴파일 타임에 바인딩

4. 컴포넌트 다이어그램 (Component Diagram)

 1) 설명

 구현에 대한 물리적인 컴포넌트 간의 구성과 의존 관계를 표현

5. 배치 다이어그램 (Deployment Diagram)

 1) 설명

 시스템의 소프트웨어와 하드웨어 컴포넌트 사이의 관계 표현, 물리적 단계와 응용 프로그램과의 네트워크 연결을 모델링

  • 컴포넌트 분포의 구체화
  • 성능상의 병목현상 파악


동적인 부분 표현

1. 순차 다이어그램 (Sequence Diagram)

  1) 설명
 
  객체 간의 메시지 전달을 시간적 흐름에서 표현하는 다이어그램

  - 개체가 다이어그램에 수평으로 정렬됨
  - 인터랙션을 구동하는 액터를 왼쪽에 표시
  - 수직축은 시간의 흐름을 나타냄
  - 객체마다 라이프 라인이라 불리는 수직 점선을 붙임
  - 객체가 소멸된 후에는 라이프 라인이 중지되며 'x'로 표시
  - 객체가 구동 중임을 표시하려면 라이프 라인에 액티베이션 박스를 그림
  - 메시지는 송신자와 수신자의 액티베이션 박스 사이에 화살표로 표시
  - 메시지 이름과 매개변수를 나타내는 레이블을 붙임

참고)
 객체에 대한 반복 수행은 메시지 이름 앞에 '*'를 붙여 표시

2. 협동(협력) 다이어그램 (Collaboration Diagram)

 1) 설명

 객체와 객체가 주고받는 메시지를 중심으로 표현하는 다이어그램

  - 협동 다이어그램은 객체가 노드인 네트워크임
  - 객체들 사이에 커뮤니케이션 링크가 추가 됨
  - 메시지가 링크에 추가
  - 화살표에 메시지 이름을 붙임
  - 메시지가 호출되는 순서는 메시지 앞에 숫자를 적어 표시

 2) 커뮤니케이션 링크

  - 객체에서 다른 객체로 메시지를 보내는 것은 항상 커뮤니케이션 링크로 표시

 3) 메시지 교환이 발생하는 경우

 - 두 객체의 클래스가 연관관계에 의하여 결합한 경우
 - 메시지를 받는 객체가 보내는 객체의 로컬 변수로 저장된 경우
 - 메시지를 받은 객체에 대한 레퍼런스가 보내진 이전 메시지의 매개변수가 되는 경우
 - 받는 객체가 전역변수 인 경우
 - 객체가 네트워크를 통하여 커뮤니케이션 되는 경우

참고)

 순차, 협동 다이어그램 선택방법

  • 순차 다이어그램
    • 메시지의 순서를 드러내 보여주고 싶을때
      • 사용사례의 시간 순서를 드러내 보이고 싶을때
      • 사용사례로부터 인터랙션 모델을 구축할 차 다이어그램
    • 메시지에 자세한 사항을 나타내고 싶을때
      • 협동 다이어그램에서는 자세한 것을 나타낼 공간이 없음
  • 협동 다이어그램
    • 클래스 다이어그램의 투영
    • 클래스 다이어그램을 검증할때 이용

3. 상태 다이어그램 (Statechart Diagram)

 1) 설명

  - 외부 자극에 대한 시스템의 동적 상태 변화를 나타냄.
  - 외부 이벤트에 대하여 민감하게 상태를 변화시키는 객체를 모델링
  - 시스템 전체, 일부, 개별 객체에 대한 동작을 나타냄
  - 노드는 상태를 나태내며 아크는 트랜지션을 나타내는 방향성 있는 그래프

 2) 상태

  - 주어진 시점에 시스템은 어떤 상태에 있음
  - 이벤트가 발생하여 상태를 변화시키기 전까지는 그 상태에 머물러 있음
  - 상태는 둥근 사각형 안에 상태 이름을 표시하여 나타냄

참고)

 특수상태

  - 시작 상태는 검은 원으로 표시
  - 종료 상태는 원이 둘려진 검은 점으로 표시

 3) 트랜지션

  - 이벤트에 대한 상태변화를 나타냄
  - 트랜지션 위에는 상태변화를 일으키는 이벤트를 표시

 4) 액티비티

  - 시스템이 어떤 상태에 있을때 발생하는 것
  • 일정 시간이 소비됨
  • 어떤 상태에서 빠져 나오는 트랜지션이 이루어짐

 5) 액션

  - 시간의 경과 없이 바로 일어날 수 있는 것(감지할 수 없는 적은 시간을 소비)
  • 특정 트랜지션이 이루어질 때
  • 특정 상태로 진입할 때
  • 특정 상태에서 빠져나올 때

4. 액티비티 다이어그램 (Activity Diagram)

 1) 설명

 업무의 흐름을 모델링하거나 객체의 생명주기를 표현하는 다이어그램

  - 상태 다이어그램과 유사하나 대부분의 트랜지션이 내부 이벤트에 의해 이루어진다는 것이 다름
  - 객체나 컴포넌트가 수행하는 작업의 흐름을 이해하는데 사용
  - 다른 사용사례 사이의 관계나 상호작용을 가시화하는데 사용

 2) 병행처리 표현

  - fork는 단일 입구 트랜지션과 다수의 출구 트랜지션을 가짐
  • 실행경로가 두개의 병행 스레드로 분할됨
  - Rendezvous는 다수의 입구 트랜지션과 다수의 출구 트랜지션을 가짐
  • 모든 입구 트랜지션이 트리거되면 시스템의 모든 출구 트랜지션이 이루어짐 
  - Join은 다수의 입구 트랜지션과 단일 출구 트랜지션을 가짐
  • 모든 입구 트랜지션이 이루어진 후 출그 트랜지션 발생
  • 입구 트랜지션은 분리된 스레드에 의하여 트리거됨
  • 먼저 이루어진 입구 트랜지션은 다른 입구 트랜지션이 이루어질 때까지 기다림

 3) 스웜레인

  액티비티 다이어그램은 대체러 여러 클래스와 관련되며 같은 스웜레인 안에 있는 액티비티들은 동일한 클래스와 관련된 것


참고)

UML Spec : http://www.omg.org/spec/UML/



소프트웨어 모델링이 필요한가?

모델링은 건축이나 토목처럼 실제 만들다가 잘못된 것이 발생했을때 고치기 어렵거나 비용이 많이 드는 분야에서 사전에 모델링하여 문제를 최대한 파악하기위해 사용되었다.
소프트웨어 개발에서 모델링은 건축이나 토목처럼 그리 효율적이지 못하다.
그러나 하드웨어와 소프트웨어가 함께 구성되고 복수의 소프트웨어가 연동되거나 복잡한 구조를 갖는 소프트웨어 개발에는 효과적일 것이다.


실제 구현하면서 문제를 바로잡는 비용 ? 모델로 설계하여 예측/검사 후 구현하는 비용?

개발자의 경험이나 협업의 정도에 따라 UML을 효과적으로 적용하여야 한다.


효과적인 모델링 적용을 위해 필요한것은?

UML은 프로젝트를 위한 협업, 의사소통의 수단일뿐 툴이나 형식에 의존하면 안됨.
핵심적인 부분은 상세화하고 그 외는 전체 시스템을 파악할 수 있는 형태로 작성.
설계를 위한 다이어그램은 코드 작성하다 꼭 필요한 경우에만 작성.


기타 활용

개발을 위한 사전 설계 외에 교육 및 유지, 관리에도 효과적.








2014년 2월 4일 화요일

android - Google Location Services API

기존에는 위치기반 서비스를 위해 android.location API를 사용했으나 이제 Google Play Services에서 제공하는 Google Location Services API를 사용하길 권장한다.

이는 저전력과 최상의 위치정보 획득/유지 등 파워풀한 기능을 제공한다.

참고) 구글에서는 개발자가 Google Play Service에서 제공하는 API를 최우선으로 사용하길 권장한다.
Android API는 OS version에 종속적이고 이는 제조사에서 디바이스에 OS 업그레이드를 제공하는 시점에서야 새로이 추가된 API를 사용할 수 있기에 제조사에따라, 단말에 따라 신규 API 사용이 제한되기 때문이다.
이를 해결하기 위해 구글의 핵심 API들을 Google Play Service에 통합해나가고 있다.
이로 인해 구글은 제조사의 OS 업그레이드에 영향을 받지않고 새로운 서비스나 기능을 보다 빠르게 사용자들에게 제공할 수 있게되었다.
(Android 2.2이상에서 Google Play Service는 Play Store를 통해 자동 업데이트된다)


Location API 사용을 위한 사전준비

1. Google Play Services 설치
해당 API를 사용하기 위해서는 먼저 Google Play Services Library를 설정하여야 한다.

 1) SDK Manager 실행

 2) Google Play Services SDK 설치
  - Android 2.2에서는 Google Play Services for Froyo를 설치
  - android 2.3 이상에서는  Google Play Services SDK 4.0.30 이상 버전을 설치

 3) Google API 설치
  - emulator에서 테스트 하기 위해선 Android 4.2.2 이상의 Google API를 설치해야 함

 4) 라이브러리 프로젝트로 아래 경로의 프로젝트를 임포트 한다.
 <android-sdk>/extras/google/google_play_services/libproject/google-play-services_lib/


2. 프로젝트에 적용

 1) 자신의 프로젝트에서 사용하려면 임포트한 Google Play Services Library Project를 추가

 2) manifest 파일의 <application> 태그에 아래 설정을 추가한다.
<meta-data android:name="com.google.android.gms.version"
           android:value="@integer/google_play_services_version" />

 3) proguard를 사용하고 있다면 아래 설정을 proguard 설정파일에 추가한다.
-keep class * extends java.util.ListResourceBundle {
    protected Object[][] getContents();
}
-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
    public static final *** NULL;
}
-keepnames @com.google.android.gms.common.annotation.KeepName class *
-keepclassmembernames class * {
    @com.google.android.gms.common.annotation.KeepName *;
}
-keepnames class * implements android.os.Parcelable {
    public static final ** CREATOR;
}

참고 URL) http://developer.android.com/google/play-services/setup.html


Google Location API 사용하기

1. 최근 위치 가져오기
Location Service는 현재 위치를 자동으로 관리하며 Location permission에 따라 정확도가 결정된다

 1) Location permission 지정
  - ACCESS_COARSE_LOCATION은 도시의 블럭단위 정도의 정확도를 갖는다.
  - ACCESS_FINE_LOCATION은 보다 정확한 위치정보를 획득할 수 있으며 ACCESS_COARSE_LOCATION과 함께 사용한다.
  - 사용하고자 하는 Location permission을 manifest 파일에 추가한다.

 2) Google Play Services 체크
  필요한 Google Play Services 버전이 설치되어 있는지 체크하고 설치가 필요한 경우 설치를 유도하게 한다.

 3) LocationClient를 이용한 최근 위치 획득
  - ConnectionCallbacks과 OnConnectionFailedListener 콜백을 정의한다.
  - LocationClient를 Activity의 onCreat()에서 생성하고 onStart()에서 연결하고 onStop()에서 연결을 끊는다.
  - getLastLocation() 함수로 최근 위치를 획득한다.

비고) Google Location API를 사용하면 위와 같이 간단히 최근 위치를 획득할 수 있으나 android.location API를 사용하면 Criteria로 정확도를 선택하고 LocationManager로 최적의 Provider를 선택하고(실패시 대응코드 적용도 필요) 얻어진 Provider의 결과에 따라(GPS_PRIVIDER, NETWORK_PROVIDER, PASSIVE_PROVIDER) 가장 적합환 위치정보를 획득하는 시나리오를 구성하여야 한다.


2. 위치 갱신정보 획득
 위에 언급한 1번 최근 가져오기와 동일한 구성하에 아래 항목을 추가한다.

 1) LocationListener 콜백 정의
   위치정보가 갱신되면 Location정보를 콜백

 2) 위치정보 갱신 요청
  - GooglePlayServicesClient.ConnectionCallbacks의 onConnected()메소드로 연결이 성공한 경우 위치정보 갱신을 요청
  - 갱신정보는 LocationRequest를 사용하며 중요 파라미터들은 다음과 같다.

  • setPriority() : 전력소모와 정확도중 우선순위를 두는 정도에따른 설정값
    • PRIORITY_HIGH_ACCURACY : 배터리소모를 고려하지 않고 정확도 최우선
    • PRIORITY_LOW_POWER : 도시정도의 정확도로 저전력 고려
    • PRIORITY_NO_POWER : 추가적인 배터리 소모없이 위치정보 획득
    • PRIORITY_BALANCED_POWER_ACCURACY : 도시의 블럭정도의 정확도
  • setInterval() : 업데이트 되길 바라는 주기(milliseconds), 다른 어플에서 설정한 setInterval()값에 영향을 받는다.
  • setFastInterval() : 명확한 업데이트 주기(milliseconds), 자신의 어플에서 위치정보 획득 후 처리하는 로직의 시간비용값보다 커야한다.
  • setSmallestDisplacement() : 위치정보 갱신의 최소 거리(m, 미터), 해당 거리이상 위치가 변경되어야 갱신함

  - requestLocationUpdates() 메소드로 위치정보 갱신 요청
  - removeLocationUpdates() 메소드로 우치정보 갱신 요청 취소


3. 위치정보의 주소 얻기
  - android.location API 사용
  - Geocoder 클래스의 getFromLocation() 메소드를 사용하여 Address 컬랙션 획득


4. Geofence(가상경계) 생성과 모니터
  - Geofence를 모니터링 하려면 ACCESS_FINE_LOCATION 권한을 필요로 한다.
  - 위도, 경도, 반경을 지정하여 Geofence를 생성하고 이에 얼마나 근접한지 들어갔는지 나갔는지 등을 모니터할 수 있다.
  - Geofence에 만료시간을 지정하여 만료처리 할 수 있다.
  - Geofence는 유니크한 ID를 가지며 이를 통해 해지 할 수 있다.

위에 언급한 1번 최근 가져오기와 동일한 구성하에 아래 항목을 추가한다.

 1) manifest 파일에 ACCESS_FINE_LOCATION 권한을 추가한다.

 2) Geofence를 Geofence.Builder로 생성한다.
  - setCircularRegion() : latitude, longitude, radius로 가상경계 설정
  - setExpirationDuration() : 가상경계 만료시간 설정(millisecond)
  - setLoiteringDelay() : 가상경계에 들어갔는지 나갔는지 판단할때 지연시간(millisecond)
  - setRequestId() : 가상경계의 유니크한 ID 문자열
  - setTransitionTypes() : 가상경계의 이동 행위

  • GEOFENCE_TRANSITION_DWELL : 가상경계에 들어가 지정된 시간 이상 머무를때
  • GEOFENCE_TRANSITION_ENTER : 가상경계에 들어갔을때
  • GEOFENCE_TRANSITION_EXIT : 가상경계에서 나왔을때

 3) OnAddGeofencesResultListener 정의

 4) LocationClient의 addGeofences() 메소드로 Geofence를 등록한다.
  Activity, Broadcast, Service 등의 형태로 생성된 PendingIntent에 의해 Intent에 행위정보가 전달된다.

 5) LocationClient의 removeGeofences() 메소드로 Geofence를 해지한다.


5. 사용자의 현재 행위 인지
 걷는중, 운전중, 서있기등 사용자의 현재 행위를 인지

 1) ACTIVITY_RECOGNITION 권한 추가
  ACCESS_COARSE_LOCATION이나 ACCESS_FINE_LOCATION 권한을 필요로 하지 않는다.

 2) Google Play Services 체크

 3) ActivityRecognitionClient를 Activity의 onCreate()에서 생성

 4) onStart()에서 연결하고 onStop()에서 연결끊기

 5) ActivityRecognitionClient가 연결이 성공하여 ConnectionCallback에서 onConnntected()가 호출되면 requestActivityUpdates() 메소드로 행위 인식 요청
  -  Activity, Broadcast, Service 등의 형태로 생성된 PendingIntent에 의해 Intent에 행위정보가 전달된다.
  - IntentService를 이용하는것이 효화적이다.

 6) ActivityRecognitionResult로 intent로 전달된 행위정보를 처리


6. Mock Location을 이용한 테스팅
 - 위치정보를 바꾸기 위해 실제로 이동하거나 할 필요없이 Mock Location을 이용하여 테스트가 가능하다.
 - Location Service를 Mock Mode로 설정하고 Mock 데이터를 전달하면 Location Service는 Mock Data를 처리한다.
 - 별도의 앱에서 Mock 데이터를 전송할 수 있으며 이는 실제 앱에 테스트 코드를 삽입하지 않아도 되기에 보다 좋은 방법이다.

 Mock Data Provider 앱 구현

 1) ACCESS_MOCK_LOCATION 퍼미션 추가
  - Mock location을 전송하려면 ACCESS_MOCK_LOCATION 퍼미션이 필요함

 2) LocationClient에 setMockMode() 메소드로 모드 설정

 3) Mock location sending...

참고 URL) http://developer.android.com/tools/device.html#setting-up

2014년 1월 22일 수요일

android - Chart Library

Android Chart Library

1. AChartEngine
 - URL : https://code.google.com/p/achartengine/
 - URL2 : http://www.achartengine.org/
 - License : Apache 2.0 License, 소스 오픈

2. AFreeChart
 - URL : https://code.google.com/p/afreechart/
 - License : GNU Lesser General Public License(LGPL), 소스 오픈
 - JFreeChart에 기반해서 만들어짐

3. StockChartView
 - URL : http://stockchartview.org/
 - License : Apache 2.0 License, 소스 오픈
 - 주식에 특화된 차트

4. EssenceChart
 - URL : http://essenceware.blogspot.kr/
 - License : MIT License와 유사하나 소스가 오픈되지 않음
 - Essence Chart SDK 설치 필요

5. GraphView
 - URL : http://android-graphview.org
 - License : GNU Lesser General Public License(LGPL), 소스 오픈

6. HoloGraphLibrary
 - URL : https://bitbucket.org/danielnadeau/holographlibrary
 - License : Apache 2.0 License, 소스 오픈

7. TeeChart
 - URL : http://www.steema.com/teechart/mobile
 - License : commercial

8. SecondPrism AndroidChartLibraray
 - URL : https://www2.secondprism.com/android-charting-library.html
 - License : commercial

9. ShinobiCharts
 - URL : http://www.shinobicontrols.com/android/shinobicharts
 - LIcense : commercial

10. InfraGistics AndroidControls
 - URL : http://www.infragistics.com/products/android/
 - License : commercial

...


안드로이드도 이젠 상용 라이브러리가 많이 나와있네요.

android - GCM(Google Cloud Message)

GCM(Google Cloud Message) 사용을 위한 준비단계

1. Google Developer Console 접속
 - 접속 URL : https://cloud.google.com/console

2. API Project를 생성하지 않았다면 "CREATE PROJECT" 버튼 클릭으로 생성
 - Project가 생성되면 Project ID와 Number가 표시됨
 - Project Number는 이후 GCM Sender에서 사용하게 됨

3. GCM Service 활성화
 1) 왼쪽 사이드바에서 APIs & auth 선택
 2) API 목록에서 Google Cloud Messaging for Android 항목을 ON으로 변경

4. Android API Key 얻기
 1) 왼쪽 사이드바에서  Credentials 선택
 2) 아래 Public API access에서 "Create new key" 버튼 클릭
 3) Create new key 다이얼로그에서 "Android key" 버튼 클릭
 4) 결과 다이얼로그의 입력창에 SHA1 fingerprint와 packageName 입력
   - SHA1 fingerprint 추출방법
    keytool -list -v -keystore mystore.keystore
   - 입력양식 예
    B6:F1:37:71:BB:1D:A6:D8:59:61:A6:D3:02:5D:54:64:5F:FA:23:F6;com.myexample
  5) "Create" 버튼 클릭
  6) 갱신된 화면에 표시된 API key는 Android Application의 인증에 사용됨

5. Server API Key 얻기
 1) 왼쪽 사이드바에서  Credentials 선택
 2) 아래 Public API access에서 "Create new key" 버튼 클릭
 3) Create new key 다이얼로그에서 "Server key" 버튼 클릭
 4) 결과 다이얼로그의 입력창에 GCM Message를 전송할 서버주소 또는 네트웍 주소를 입력한다.(예 : 192.168.0.1 또는 192.168.0.0/24)
 5) "Create" 버튼 클릭
 6) 갱신된 화면에 표시된 API key는 3rd-Party Application Server의 인증에 사용됨


GCM 클라이언트 구현

GCM enabled app 구현시 이전버전의 client helper library를 아직도 지원하고는 있지만 GoogleCloudMessaging로 대체되었기에  GoogleCloudMessaging API를 사용할 것을 천한다.

1. Google Play Service 설정
 GoogleCloudMesasging API를 사용하기 위해선 Google Play Service를 설정해야 한다.

Google Play Service 설정 참고 URL : http://developer.android.com/google/play-services/setup.html

2. Manifest.xml 수정
아래 해당항목을 추가
 1)  GCM 메시지 수신을 위해 다음 퍼미션 추가
  com.google.android.c2dm.permission.RECEIVE
 2) android app가 3rd-party app sever에 registration id를 전송하기 위해 다음 퍼미션 추가
  android.permission.INTERNET
 3) GCM은 Google account를 필요로 하며 android 4.0.4 미만 버전에서는 다음 퍼미션 추가
  android.permission.GET_ACCOUNTS
 4) 메시지가 수신될때 sleep모두에서 프로세스를 보호하기 위해 다음 퍼미션 추가(해당 퍼미션은 부가적인 옵션)
  android.permission.WAKE_LOCK
 5) 다른 안드로이드 어플에서 메시지 수신하는것을 막기 위해 다음 퍼미션 추가(해당 양식을 지켜야 함)
 " + applicationPackage + ".permission.C2D_MESSAGE
 6) com.google.android.c2dm.intent.RECEIVE 메시지를 수신하기 위한 receiver 등록
  - applicationPackage 이름의 category
  - com.google.android.c2dm.SEND 퍼미션을 요구
 7) GCM Message를 처리할 Service를 등록(해당 기능은 부가적인 옵션)

<manifest package="com.example.gcm" ...>

    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <permission android:name="com.example.gcm.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" />

    <application ...>
        <receiver
            android:name=".GcmBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="com.example.gcm" />
            </intent-filter>
        </receiver>
        <service android:name=".GcmIntentService" />
    </application>
</manifest>

  8) Google Play Service 사용을 위해 application 태그에 com.google.android.gms.version meta-data 태그 추가(gcm helper libraray : gcm.jar 사용시 불 필요)

    <application ...>
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
    </application>
3. App 구현

1) Check Google Play Service APK
 Google Play Service가 사용가능한지 체크하며 불가시 다운로드를 유도하는 다이얼로그 표시
 Google Play Service version 3.1 이상을 필요로한다.

private boolean checkPlayServices() {
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
    if (resultCode != ConnectionResult.SUCCESS) {
        if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
            GooglePlayServicesUtil.getErrorDialog(resultCode, this,
                    PLAY_SERVICES_RESOLUTION_REQUEST).show();
        } else {
            Log.i(TAG, "This device is not supported.");
            finish();
        }
        return false;
    }
    return true;
}

 2) GCM 등록
메시지를 수신하기 위해 GCM server 등록이 필요하며 등록되면 registration id를 받는다.
등록시 Project Number를 사용함(SENDER_ID는 Project Number)

gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (regid.isEmpty()) {
    registerInBackground();
}

private String getRegistrationId(Context context) {
    // 저장된 regId}
private void registerInBackground() {
    new AsyncTask() {
        @Override
        protected String doInBackground(Void... params) {
            String msg = "";
            try {
                if (gcm == null) {
                    gcm = GoogleCloudMessaging.getInstance(context);
                }
                regid = gcm.register(SENDER_ID);
                msg = "Device registered, registration ID=" + regid;

                sendRegistrationIdToBackend();

                storeRegistrationId(context, regid);
            } catch (IOException ex) {
                msg = "Error :" + ex.getMessage();
            }
            return msg;
        }

        @Override
        protected void onPostExecute(String msg) {
            mDisplay.append(msg + "\n");
        }
    }.execute(null, null, null);
    ...
}
private void sendRegistrationIdToBackend() {
    // 등록 성공시 리턴받은 regId를 자신이 구현한 3rd-party server에 알려준다.
}
private void storeRegistrationId(Context context, String regId) {
    // regId를 저장
}

 3) 메시지 수신
  - 시스템은 들어는 메시지를 수신하여 메시지 payload 에서 key/value를 분류한다.
  - 시스템은 대상 어플에게 Intent의 extra에 com.google.android.c2dm.intent.RECEIVE 값으로 전달한다.
  - 안드로이드 어플은 해당 메시지를 broadcast receiver로 전달받아 값을 처리한다.

device에 메시지는 broadcast message로 전달되는 방식이이며 WakefulBroadcastReceiver는 wake lock에 특화된 broadcast receiver이다.
WakefulBroadcastReceiver sleep모드에서 GCM message를 수신하기 위해 사용하며 sleep 모드에서 GCM message를 받을 필요가 없다면 일반 broadcast receiver를 사용하면 된다.

startWakefulService 메소드로 GCM message를 처리할 서비스를 실행시킨다.
이는 startService와 동일하나 service가 시작되면 wake lock 잡고 있는다.
service가 작업을 완료하면 wake lock을 풀어주기 위해  GcmBroadcastReceiver.completeWakefulIntent()를 호출한다.

pblic class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ComponentName comp = new ComponentName(context.getPackageName(),
                GcmIntentService.class.getName());
        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_OK);
    }
}
public class GcmIntentService extends IntentService {
    public static final int NOTIFICATION_ID = 1;
    private NotificationManager mNotificationManager;
    NotificationCompat.Builder builder;

    public GcmIntentService() {
        super("GcmIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Bundle extras = intent.getExtras();
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        String messageType = gcm.getMessageType(intent);
        if (!extras.isEmpty()) {
            if (GoogleCloudMessaging.
                    MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {

            } else if (GoogleCloudMessaging.
                    MESSAGE_TYPE_DELETED.equals(messageType)) {

            } else if (GoogleCloudMessaging.
                    MESSAGE_TYPE_MESSAGE.equals(messageType)) {
                sendNotification("Received: " + extras.toString());
                Log.i(TAG, "Received: " + extras.toString());
            }
        }
        GcmBroadcastReceiver.completeWakefulIntent(intent);
    }


GCM Server 구현

 Google에서 제공된 GCM Connection Server는 3rd-party app server로 부터 메시지를 수신하여 GCM enabled된 android app에 전달(Google에서 제공하는 HTTP 또는 CCS(XMPP))

참고)
 XMPP(Extensible Messaging and Presence Protocol) : XML에 기반한 메시지 지향 미들웨어용 통신 프로토콜

 smack :android를 위한 XMPP library (참고 URL : https://github.com/Flowdalic/asmack, http://www.igniterealtime.org/projects/smack/)

1. GCM Connection Server 프로토콜 선택
 HTTP와 CCS(XMPP) 방식중 아래 특징을 확인 후 선택
 1) 양방향 메시지
  - HTTP : cloud-to-device 만 가능
  - CCS : cloud-to-device, device-to-cloud 모두 가능
 2) 비동기 메시지
  - HTTP : post 방식을 사용하며 요청 후 응답을 기다리는 동기방식
  - CCS : server나 client 모두에서 메시지를 전송하면 CCS에서 이에 대한 결과를 알려주는 비동기 방식
 3) JSON
  - HTTP : post 방식으로 전송
  - CCS : XMPP 메시지에서 JSON을 암호화 시킴

참고)
 - HTTP를 사용할 경우 GCM server helper library를 사용하거나 직접 HTTP 통신 구현
 - XMPP를 사용할 경우 GooglePlayService의 GCM또는 java smack demo app 참조

데이터 보안이나 메시지 전송의 성능 및 양방향 메시지 구현을 위해서 CCS 방식이 추천되나 단지 메시지 통보가 목적이라면 HTTP 방식도 괜찮겠다.

2. 3rd-party app server 구현 필요사항
 3rd-party app server는 다음과 같은 기준을 만족해야 함
 1) client와 통신이 가능해야 함
 2) GCM connect server에 올바르게 요청해야 함
 3) 지연시간 기반의 전송장애를 체크하여 요청이나 재전송을 할 수 있어야 함
 4) API keys와 registration id를 저장할 수 있어야 함
 5) 각각의 메시지는 유니크한 id를 가져야 함

3. 메시지 전송
 일반적인 형태의 메시지 전송 순서
 1) app server가 GCM connection server로 메시지 전송
 2) device가 offline이면 메시지를 저장하고 큐에 넣음
 3) device가 online일때 메시지를 device에 전송
 4) device내에서 부합하는 퍼미션을 갖는 어플에 시스템 메시지가 브로드캐스트로 전달되어진다.(메시지를 받기 위해 어플이 사전에 실행되고 있을 필요가 없음)
 5) android app가 해당 메시지를 처리

4. 메시지 전송을 위한 최소 필요사항
 1) Target
  - HTTP 방식에서 다음중 하나를 명시해야 한다.
   registration_ids : 1 ~ 1000대까지 복수의 디바이스에 복수의 registration_id를 사용하여 메시지 전송시(멀티캐스트 메시지)
   notification_key : 한명 소유의 복수 단말에 메시지 전송시 사용
  - CCS 방식
   "to" 필드로 단일의 registration_id나 notification_key를 가져야 하며 멀티캐스트 메시지는 지원하지 않음
 2) Payload
  -  부가적으로 메시지에 payload를 갖고 있다면 data 파라미터를 가져야함(HTTP와 CCS 모두 해당)
 3) Message Parameters
  - JSON 메시지 파라미터(HTTP와 CCS 모두 해당)
  - Plain text (HTTP only)

  참고 URL : http://developer.android.com/google/gcm/server.html#send-msg

5. 제약사항
 - GCM Message는 최대 4kbytes까지만 가능하다.
 - Message는 순서대로 전달되는것을 보장하지 않는다.

 참고 URL : http://developer.android.com/training/cloudsync/gcm.html

6. XMPP 방식의 GCM Message 전송(Google Play Service)
device에서 GCM Server로 메시지를 전송하느느 send_event 타입

AtomicInteger msgId = new AtomicInteger();

private void sendGCMMessage() throws IOException {
    Bundle data = new Bundle();
    data.putString("message", "GCM test message");

    String id = Integer.toString(msgId.incrementAndGet());

    if (gcm == null) {
        gcm = GoogleCloudMessaging.getInstance(context);
    }
    gcm.send(SENDER_ID + "@gcm.googleapis.com", id, data);
}

7. XMPP 방식의 GCM Message 전송(Smack Library 이용)
- 라이브러리는 http://www.igniterealtime.org/projects/smack/에서 다운로드 받도록한다.
- 실제 구현시 로그인, 로그아웃, 메시지 전송을 분리하여 로그인된 커넥션을 재사용하여 메시지를 전송하게 하여야 한다.
- PacketListener를 통해 메시지 패킷을 수신한다.

private void sendGCMMessage() {
    final String SENDER_ID = // Project Number
    final String apiKey = // Server API Key에서 얻은 API Key

    SASLAuthentication.supportSASLMechanism("PLAIN", 0);

    ConnectionConfiguration config = new ConnectionConfiguration("gcm.googleapis.com", 5235);
    config.setSASLAuthenticationEnabled(true);
    config.setSocketFactory(SSLSocketFactory.getDefault());
    config.setDebuggerEnabled(true);

    Connection connection = new XMPPConnection(config);

    try {
        PacketListener recvListener = new PacketListener() {
            public void processPacket(Packet packet) {
                System.out.println("PacketListener:processPacket:\n" + packet.toXML());
            }
        };
   
        Packet sendPacket = null;// create Packet ...
   
        connection.connect();
        connection.login(SENDER_ID + "@gcm.googleapis.com", apiKey);
        connection.addPacketListener(recvListener, null);
        connection.sendPacket(sendPacket);
    } catch (XMPPException e) {
        e.printStackTrace();
    }
}

참고 URL : http://developer.android.com/google/gcm/ccs.html#implement

8. HTTP 방식의 GCM Message 전송
 - Client에서 전달받은 registrationId를 최대 1000개까지 배열 형태로 세팅하여 전송가능
    --> 단일 메시지 전송으로 최대 1000대의 디바이스에 메시지를 멀티캐스팅할 수 있음.
 - registration_ids만 필수항목이고 나머진 옵션이다.
 - data가 전송할 메시지의 payload가 된다.

private void sendGCMMessage() {
    try {
        String regId = ""; // GCM Client에서 등록할때 받은 registrationId
        String apiKey = ""; // Server API Key 얻기에서 생성된 API Key
        String json = "{ \"registration_ids\" : [\"" + regId + "\"], \"data\" : { \"message\" : \"GCM test message\" } }";

        URL url = new URL("https://android.googleapis.com/gcm/send");
        HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection();
        urlConn.setRequestMethod("POST");
        urlConn.addRequestProperty("Authorization", "key=" + apiKey);
        urlConn.addRequestProperty("Content-Type", "application/json");

        urlConn.setDoInput(true);
        urlConn.setDoOutput(true);

        OutputStream os = urlConn.getOutputStream();
        os.write(json.getBytes());
        os.close();

        System.out.println("(" + urlConn.getResponseCode() + "):" + urlConn.getResponseMessage());
   
       InputStream is = urlConn.getInputStream();
       byte[] buf = new byte[1024];
       while(is.available() > 0) {
           int readCount = is.read(buf);
           if(readCount > 0) {
           System.out.println(new String(buf, 0, readCount));
      }
    }
} catch (Exception e) {
    e.printStackTrace();
}

9. GCM Server Helper Library(gcm-server.jar)를 이용한 GCM Message 전송
 - HTTP 방식으로 구현된것으로 판단됨
 - Client에서 전달받은 registrationId를 최대 1000개까지 컬렉션 형태로 세팅하여 전송가능
    --> 단일 메시지 전송으로 최대 1000대의 디바이스에 메시지를 멀티캐스팅할 수 있음.

private void sendGCMMessage() {
    String regId = ""; // GCM Client에서 등록할때 받은 registrationId
    String apiKey = ""; // Server API Key 얻기에서 생성된 API Key
    Sender sender = new Sender(apiKey);
    Message message = new Message.Builder();addData("message", "GCM test message").build();

    try {
        Result result = sender.send(message, regId, 5);
        System.out.println("result:" + result);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

...

참고 사항

에러 대응하기

1. XMPP로 upstream이 되지 않을때,
XMPP 기반의 upstream을 사용하고자 한다면 아래 URL에 접속하여 신청서를 작성하세요.
https://services.google.com/fb/forms/gcm/

2. Server returned HTTP response code: 401 for URL: https://android.googleapis.com/gcm/send
--> 사용된 apiKey가 잘못되었을 수 있음(Server API Key를 사용해야 함)

3. smack 라이브러리로 CCI(XMPP) 방식으로 메시지 전송시 다음과 같은 에러는 1번처럼 CCI 기반의 upstream 허가가 나지 않은 경우임
 1) "SASL authentication PLAIN failed: text:"
 2) Project not whitelisted.