2011년 12월 19일 월요일

Google App Engine의 제약사항

Google App Engine(이하 GAE) 가격도 싸고 구글의 인프라를 이용하니 글로벌 서비스를 하기에도 적합하다.

여기저기 장단점이 나열된 사이트는 많이 있는데 장점을 포괄적으로 얘기하자면 "개발자는 인프라 신경 안쓰고 개발에만 집중하면 된다.(이건 개발만 하는 사람으로서는 굉장히 큰 장점이다.)" 이고... 단점을 포괄적으로 얘기하자면 "너무 제약사항이 많다."는 것이다.

장점이 좋으니.. 게다가 GUI를 클라이언트 프로그래밍 하는것 처럼 할 수 있으니 좋다 생각하고 이래저래 환경 설정을 했다.

근데.. Socket을 적으니... 빨간줄... 쓰레드도 생성이 안된다.
외부 서버와 통신을 하려면 HTTP나 HTTPS만 된다는 것이다.

써보고 바로 발견한건

1. Socket, SSLSocket 안됨
2. Thread 안됨

바로 접었다.
그리고 지금 다시 톰켓과 오라클을 만지고 있다.

코딩 방법에 제한도 많지만 활용도에도 제한이 너무 큰 듯...

사용 예정인 사람은 참고 바란다.

2011년 12월 13일 화요일

안드로이드 내장 이미지 쓰는 법

오늘은 초 간단이지만 나름 유용한 피드...

안드로이드 SDK 설치 경로

(...\android-sdk\platforms\android-<version>\data\res\drawable-<density>)

에 가 보면 안드로이드 기본 이미지들이 많이 있다.
만약 이 이미지 중에 ic_menu_edit를 사용하고 싶다면...자바 소스에서는

android.R.drawable.ic_menu_edit

를 사용하면 된다.
xml에서는 어떻게 사용할까?

android:src="@android:drawable/ic_menu_edit"

로 속성을 정의하면 사용할 수 있다.

주의할 점은 해 보면 알겠지만... 다 사용할 수는 없다. android.R 에 정의된 것들만 사용할 수 있고...


2011년 12월 11일 일요일

3G, WIFI & CONNECTIVITY_ACTION receiver

현재 진행하고 있는 안드로이드 프로젝트... 원래는 내가 해야할 일은 아닌 것 같은데 소방수로 투여되었다.

문제는 일전에 말했던 "차트" 와 "네트워크 안정화"...
당연히 있을 줄 알고 미친듯이 테스트 하면서 왜 안되지 하는 삽질 끝에..

소스를 쭉.. 살펴보니  CONNECTIVITY_ACTION 에 대한 Receiver가 없었다.

모르는 사람들을 위해 간략히 설명하자면 "네트워크 상태에 대한 실시간 감시" 라고나 할까...
사용법에 대해서는 CONNECTIVITY_ACTION, Receiver 등으로 구글에서 검색하면 미친듯이 나오기 때문에 따로 설명하지는 않고 소스만 예제 첨부 하겠다. 아래 예제는 네트워크가 끊어짐을 감지하는 소스이다.

public class ConnectionBroadcastReceiver extends BroadcastReceiver
{
    private static final int STATE_NONE = 0;
    private static final int STATE_WIFI_CONNECTED = 1;
    private static final int STATE_MOBILE_CONNECTED = 2;
    private int state = STATE_NONE;

    @Override
    public void onReceive(Context context, Intent intent) 
    {
        try
        {
            String action = intent.getAction();
            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
            {
                ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo niWifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
                NetworkInfo niMobile = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
                if (state == STATE_NONE)
                {
                    if (niWifi.getState() == State.CONNECTED)
                    {
                        state = STATE_WIFI_CONNECTED;
                    }
                    else if (niMobile.getState() == State.CONNECTED)
                    {
                        state = STATE_MOBILE_CONNECTED;
                    }
                }
                else if (state == STATE_WIFI_CONNECTED)
                {
                    if (niWifi.getState() == State.DISCONNECTED || niWifi.getState() == State.DISCONNECTING)
                    {
                        state = STATE_NONE;
                        Log.d("CBR", "Wi-Fi 끊어졌다!");
                    }
                }
                else if (state == STATE_MOBILE_CONNECTED)
                {
                    if (niMobile.getState() == State.DISCONNECTED || niMobile.getState() == State.DISCONNECTING)
                    {
                        state = STATE_NONE;
                        Log.d("CBR", "3G/4G 끊어졌다!");
                    }
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

위 소스를 보면 Log에서 먼가 처리를 하는 이벤트를 날리면 되는데... 만약 앱 내부에서 네트워크 수신을 쓰레드에서 계속 감시한다고 하면....

int len = is.read(...);

이라는 함수에서 블럭이 걸릴텐데... 유닉스든 뭐든 네트워크가 끊어지면 일반적으로 위 함수에서 -1을 리턴한다. 근데... Wi-Fi와 모바일 네트워크 간에 작동이 좀 틀리다.

Wi-Fi는 문제가 생기면 거의 바로 -1을 리턴해주는데, 모바일 네트워크는 지가 죽어버린지 모르는 것이다... 그래서 위와 같은 리시버를 이용하여 네트워크 상태를 감지하고 강제 접속 종료를 하는데... 문제가 하나 더 있다. Wi-Fi일 경우에는 리시버 이벤트 보다 소켓에서 더 먼저 -1 반응이 나온다... 그리고 모바일 네트워크는 지가 죽은지 모르기 때문에 당연히 리시버가 먼저 떨어진다. 그래서... 한곳에서 처리를 못하고 결국은.... 모바일 네트워크가 끊겼을 때만 리시버에서 네트워크를 강제로 끊어주게 했다.

정리를 하자면

1. Wi-Fi는 소켓에서 먼저 반응을 하므로 쓰레드의 read 함수에서 접속 종료 처리
2. 3G/4G등의 모바일 네트워크는 소켓에서 반응이 없으므로 CONNECTIVITY_ACTION 리시버에서 처리...

정말.. 모바일 네트워크는... 10년동안 하지만 짜증이 난다.. 특히 Wi-Fi가 들어가고 스마트폰이 나오고 고객의 요구사항이 늘어갈수록...
이벤트 등록과 제거.. 그리고 Manifest에서 처리해야 할것들도 보너스로 올려본다..(사실은 내가 나중에 소스 안뒤지고 그냥 블로그에서 카피하려고 쓰는거지만..)

// 이벤트 등록
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
ConnectionBroadcastReceiver cbr = new ConnectionBroadcastReceiver();
registerReceiver(cbr, filter);

// 이벤트 제거
unregisterReceiver(cbr);

<!-- Manifest에 등록할 내용 -->
<application .... >

    <receiver android:name=".ConnectionBroadcastReceiver">
        <intent-filter>
            <action android:name="CONNECTIVITY_CHANGE"/>
        </intent-filter>
    </receiver>

</application>




2011년 12월 9일 금요일

FlashLight 1.1 for Android 업그레이드

내친 김에 플래시도 업그레이드 해버렸다.


추가된 화면은 위와 같은 설정화면.. 예전에 (i)를 누르면 그냥 크레딧만 나오던 것을 바꿔서 간단한 설정을 하게 끔 했다.


1. 플래시/화면 전환모드 추가
2. 깜빡임 기능 추가

카메라 플래시가 깜빡이는게 하는 것을 구현했는데 생각보다 짧게 깜박일 수 있었다.(100ms)

근데 사실 플래시에 정말 필요한 기능인지는....-_-
기능 2-3개정도만 더 추가하면... 더 할 것이 없을것 같은데... 2.0정도가 최종이 될지도 모르겠다.

2011년 12월 8일 목요일

WinCalc 1.3 for Android 업그레이드

아.. 자주도 업그레이드 한다.
한 10일 만인가?
빨리 공학용 계산기로 가기 위해... 일반 계산기 기능을 완료해버렸다.

1. 히스토리 기능 추가
2. 메뉴 분리 (Options -> View. Edit. Help)
3. 메뉴 내 잘못된 명칭 수정 (Unit conversion -> Digit grouping) -> 아.. 쪽팔려

클론이라고 만들면서... 히스토리 기능을 넣는데 있어 공간이 부족한 바람에... 결국 제멋대로 뷰를 만들었다.


이게 이번에 추가된 주 기능인 히스토리 기능인데... 편집도 되고... 나름 히스토리가 절실한 사람에게는 유용할 수 있는 기능이다. 근데.. 윈도우 계산기의 기능은 커버되는데... 화면은 제멋대로이다.

허접하기는 하지만 덕분에 오늘 수익이 조금 늘어난 것 같아 기분은 좋음..ㅋㅋ

2011년 12월 1일 목요일

Collections 정렬방법(아이폰, 안드로이드)

예전부터... 즉, API가 지금 같이 많이 준비되지 않은 도스 시절부터 프로그래밍을 해오던 터라.. 있는 것을 가져다 쓰기 보다는 만들어 쓰는 버릇이 생긴 것 같다.
일을 시작하고는 초반에...링크드 리스트(Objective-C에서 NSArray 등, Java에서 Vector 등..) 같은 것은 학교 다닐 때 배우건걸 이용해서 만들어 쓰기도 하고...했는데...
충격적인 것은 약 2년전.. 10년차가 될 때 까지 정렬 알고리즘을 써서 만들어 정렬했다는 것...

좀 하시는 분들은 쟤 머냐 할 법한 코드들이 나름 우아하게 짠 아키텍쳐에 박혀있으니... 아마 비웃는 사람도 많았을 것이다.

근데... 이게 익숙하지 않아서 인지 어제 작업을 하다가 정렬 할 일이 있어 구글링으로 정렬하는 법을 또 찾았으니... 내 블로그에 정리하면서 절대로 안잊어 버리게 할 심산으로 포스팅 해본다.

1. Objective-C에서 NSArray의 정렬법

(1) 정렬하려고 하는 Object 클래스에 정렬 방법에 대한 함수를 만든다. 아래 예제에서는 compare라는 이름으로 구현하였다.

@interface MessageItem : NSObject

@property (strong, nonatomic) NSDate *time;
@property (strong, nonatomic) NSString *message;

- (NSComparisonResult)compare:(MessageItem*)item;

@end

@implementation MessageItem

@synthesize time = _time;
@synthesize message = _message;


- (NSComparisonResult)compare:(MessageItem*)item
{
    // 시간 순으로 정렬
    return [self.time compare:item.time];
}

@end

(2) 사용하는 부분에서 NSArray에 모두 추가한 후 sortUsingSelector 함수로 정렬한다.

NSMutableArray* array = [NSMutableArray array];

[array addObject:messageItem1];
[array addObject:messageItem2];
[array addObject:messageItem3];
[array addObject:messageItem4];
[array addObject:messageItem5];


[array sortUsingSelector:@selector(compare:)];

이렇게 하면 messageItem1부터 5까지에 대해 시간 순으로 정렬이 된다. compare 함수를 잘 조절하면 시간의 역순이나 message의 알파벳 내림차순/오름차순으로 정렬이 가능하다.


2. Java에서의 정렬법

(1) 정렬하려고 하는 Object 클래스를 만든다.


public class MessageItem
{
    public Date date;
    public String message;
}

(2) 사용하는 부분에서 Vector에 모두 추가한 후 Collections.sort 함수로 정렬한다. 정렬 비교 클래스는 재사용을 많이 할 경우에는 따로 구현하고 아니면 아래 예제처럼 구현해도 좋을 듯.

Vector array = new Vector();

array.add(messageItem1);
array.add(messageItem2);
array.add(messageItem3);
array.add(messageItem4);
array.add(messageItem5);

Collections.sort(array, new Comparator()
{
    @Override

    public int compare(MessageItem messageItem1, MessageItem messageItem2)
    {
        return (int)(messageItem1.time.getTime() - messageItem2.time.getTime())
    }
});

쓰고 보니 안드로이드와 아이폰에만 국한된 문제가 아니라 Objective-C와 Java모두에 해당되는 내용이다. 위 예제는 똑같이 작동하는 두 언어에 대해 예제이니 여러개 언어를 하실 분들은 참고 바란다.