월별 글 목록: 2012년 1월월

Android java.util.concurrent.RejectedExecutionExecution 핸들링(1/2)..

안드로이드 API의 AsyncTask는 API Level 3부터 지원하는 클래스로, UI 스레드가 아닌 스레드(백그라운드 스레드)를 생성하고 실행한 결과를 UI 스레드에 전달(Looper라는 놈이 전달함)하는 래퍼이다. AsyncTask를 사용하는 방법은 이 클래스를 상속해서 doInBackground() 메서드를 구현하면 된다. 아래 내용은 안드로이드 2.2 기반에서의 경험한 내용이다.

그래고, 많은 데이터를 가지고 오기 위해서 페이징을 합니다. 안드로이드는 하단에 spinner와 Loading과 같은 메세지가 표준처럼 사용되고 있죠.

사진에 대한 Thumbnail이 매우 많고, 30개씩 가져온다고 가정을 합니다.. 보통 ArrayAdapter나 ListAdapter를 상속받아서 Adapter 객체를 만들고, Adapter 객체의 getView()에서 랜더링을 위한 작업을 구현하게 됩니다.

@Override
public View getView(int position, View cView, ViewGroup parent) {
}

Thumbnail을 가져와야 하니, 보통 위 메소드 안에서 Thumbnail을 가져오기 위해, AsyncTask를 상속받은 쓰레드로 처리를 하는 것이 일반적입니다. 이 상황은 쓰레드를 많이 생성하게 됩니다..

위의 경우에 RejectedExecutionException 이 발생할 수 있습니다.. 안드로이드는 내부적으로 java.util.concurrent 패키지의 ExecutorService를 Thread 디스패칭에 사용하고 있습니다. 따라서, 요 문제는 아래의 ExecutorService가 처리해야 하는 AsyncTask의 POOL_SIZE가 순간적으로 넘어서고, 이 경우에 AsyncTask가 풀에 들어갈 수 없어서 발생하는 익셉션입니다. 위 문제는 빈번하게 발생하지 않을 것 같지만, 게임이라던지 쓰레드가 많이 필요한 작업을 하다 보면 발생할 수 있습니다..

아래는 Android 플랫폼에서 기본적으로 가지고 있는 AsyncTask의 정책값입니다.

private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 10;
private static final int KEEP_ALIVE = 10;

따라서, 위 문제는 하나의 쓰레드로 30개의 이미지를 가져오는 형태로 살짝 바꾸면 해결이 됩니다. 당근 성능과 안전성의 트레이드 오프라고 할 수 있겠습니다..

위에서 간단하게 RejectedExecutionException을 피해갈 수 있는 방안에 대해서 살펴보았습니다..
위에서 고려한 사진의 Thumbnail에 대한 예로, 한번 더 고민을 해 보면, 만약에 30개를 가져오는 쓰레드를 getView()외에서 처리를 한다면, 지금 보고 있는 화면에서 사진이 안 보일 겁니다.. 왜냐하면, getView()에서 처리를 하지 않았기 때문에 빈 사진만 보이겠죠.

그래서, 위 가정대로, 사진이 많아서 페이징을 해야하고 Thumbnail을 보여준다면, 사진을 가져오는 첫 페이지에서는 getView()에서 AsyncTask를 불러서 사진을 가져오고, 두 번째 페이지 부터는 데이터를 가져와서 Adapter에 바인딩하는 쓰레드가 30개의 사진을 처리하는 쓰레드를 다시 부르는 형태로 구현을 하는 것이 좋을 것 같습니다..

Android Toast duration 조정하기..

안드로이드에서는 메세지를 보여주고 사라지는 용도로 Toast를 빈번하게 사용하고 있습니다.
하지만, 아쉬운 점이 하나 있다면, duration을 설정할 수 없습니다.
그래서, 간단하게 duration을 변경하는 방법으로, Toast의 show()를 쓰레드로 원하는 시간 만큼 계속 보여주면 쉽게 해결할 수 있습니다.

간단하게, 안드로이드 Toast 클래스에서 제공하는 SHORT과 LONG의 기본 시간값입니다..

private static final int LONG_DELAY = 3500; // 3.5 seconds
private static final int SHORT_DELAY = 2000; // 2 seconds

아래는 CountDownTimer를 이용해서 총 4초동안, 1초씩 줄어들면서 Toast의 show() 메소드를 호출해서, 총 6초(Toast.LENGTH_SHORT이 duration 이니)동안 메세지를 보여주는 코드입니다..

final Toast toast = Toast.makeText(context(), getString(R.string.login_error),Toast.LENGTH_SHORT);
toast.show();
new CountDownTimer(4000, 1000) {
  public void onTick(long millisUntilFinished) {toast.show();}
  public void onFinish() {toast.show();}
}.start();

또 다른 방법으로 https://github.com/quiqueqs/Toast-Expander/blob/master/src/com/thirtymatches/toasted/ToastExpander.java 에서 쓰레드로 시간을 빼면서 루프를 돌리는 방법도 있습니다.

2011년을 보내며..

올해가 벌써 한 달이 가까워지게 지나가고 있다. 세월은 엄청 빠르게 지나가고 있다. 특히, 내가 나이가 먹으니 더 느껴진다.. ^^;; 작년에 대한 포스트 모텀이지만, 작년을 얘기하려면 제 작년에 N사를 그만두면서 느꼈던 시간의 아까움에 대해서 얘기를 해봐야겠다..

시간, 정말 중요한 거 같다. 경력이 주로 N사다 보니 참 편하게 회사에 다녔던 거 같다. 마지막(?) N모사를 나오면서 롤 모델로 삼고 싶은 CTO님을 만나게 된 것이 행운이었다. 어찌 보면 아이러니 하지만, 그분을 보면서 시간의 아까움과 동시에 뭔가를 해봐야겠다라는 생각을 강렬하게 받았기 때문이다. 그래서, 결국 N사를 퇴사하고 비교적 고행(?)길로 들어서게 되었다. ^^

그래서, 작년에 한 일에 대해 살펴보면..

1. 전 직장에서 퇴사하다

잘한 결정이고 후회는 없다. 이 곳에서의 아키텍처링과 백엔드에 대한 경험은 매우 좋았다. 당연히, 좋은 경험은 좋은 분들을 함께 일하면서 느낀 감정이라서, 좋은 분들을 알게 된 것이 가장 큰 소득이겠다.

2. 카산드라 완벽 가이드를 번역하다

개인적으로 작년에 최대의 산출물이라고 생각한다. 그 이유는 번역을 하면서 오랜만에 집중해서 공부를 해본거 같다. 선배 형하고 같이 진행을 하면서, 서로 바쁜 업무로 인해서 쉽지는 않았지만, 정말 편집자(한동훈님)를 잘 만나서 만족스러운 책이 나온거 같다. 업무에서 사용자의 데이터 스냅샷에 카산드라 서버를 사용하고 있었고, 번역하면서 카산드라에 대한 경험치를 많이 터득할 수 있었다.

3. 안드로이드 앱을 런칭하다

전 회사에서 아이폰 앱을 런칭하려고 시도했으나, 매우 힘들게 진행되는 것을 보고 답답했었다. 그래서, 지금 회사에 남는 TO가 모바일이었고, 커리어에 대한 고민은 하긴 했지만, 우선 고하기로 했다. 작년에 첫 타블릿 앱이 출시가 되었다. 처음 개발한 앱이라서 힘도 들긴 했지만, 그럭저럭 좋은 앱을 개발한 거 같아서 좋다. 작년부터 지금까지 새로운 안드로이드 앱을 준비하고 있다. 이 앱은 우여곡절이 좀 있다. 일 하기가 매우 힘들었고, 난항도 많았다. 그래도, 거의 개발이 완료가 되서 출시를 목전에 두고 있다.

이제, 제 작년부터 준비를 했던, 실리콘 밸리에서의 개발자로서의 삶을 목전에 두고 있다. 어찌보면 영어도 못 하면서 실리콘 밸리에 있는 회사를 들어갔으니, 나름 성공적으로도 보일 수 있겠다. 개인적으로, 미국에 있었던 반년은 매우 힘들었다. 정말 몰랐으니 덥썩 덤벼든 거였다. 가족이 있다보니, 가족과 떨어져 있는 것이 이렇게 힘들다는 것을 뼈져리게 느끼게 되었다.

앞으로 누구한테도 가족과 떨어져서 일을 오래해야 하는 형태는 추천하지 않겠다..

그리고, 영어가 부족하다 보니, 일할때 문제가 생기곤 한다. 소프트웨어 개발은 다양한 컨텍스트 상황에서 어떻게 처리를 해야 하는지 명확하게 짚어야 하고, 결정된 내용을 잘 구현해야 좋은 소프트웨어를 개발할 수 있겠다. 그런데, 명확하게 이해를 하지 못하는게 간혹 있기도 하다. 다행이 나는 옆에서 일하는 개발자가 영어를 잘해서 통역도 해준다.. 땡큐.. ^^ 영어는 생존이 걸려 있기 때문에 열심히 해야될 필요가 있겠지만서도 개발과 영어 둘 다 집중할 시간에 대한 선택을 한다면 또 다시 개발을 잘하기 위한 시간에 투자를 할것 같다..

한국에서는 실리콘 밸리가 개발자들의 천국이라고 말하고 있다. 과연 어떤 것들이 개발자들에게 어필할 수 있기에 천국이라는 표현을 과감하게 썼을까 하는 고민을 해 본다. 천국이라.. 너무 과분한 표현 아닌가? 과분을 떠나 단지 미국에서 일하는 것에 대한 환상 아닐까? 요즘은 국내 개발자들에 대한 대우도 좋아지고, 인식도 매우 좋다. 그리고, 기술력도 미국에 비해서 그렇게 나쁘지 않다고 생각한다.

다만, 국내의 유수의 기업에서는 소스에 대한 공개와 유지에 대한 비용문제에 대해서 좋지 않은 견해를 가지고 있는 회사들이 많고, 회사 내부에서도 기득권에 대한 유지를 위해서 소스공개를 터부시하는 문화도 한 목 하기 떄문일 것이라 생각한다.

실리콘 밸리에서 개발자로서의 삶을 살아가기 위해서, 다시금 맘 가짐을 잡자면, 잘 할수 있는것에 집중을 하자이다. 단지 수요에 비해서 공급이 못 따라가는 것이라는 느낌이라서, 경쟁력을 가지는게 현명할 대처일 수 있다는 마지막 소견이라는.. ^^

안드로이드에서 Facebook 연동시에 발생할 수 있는 2가지 이슈..

Facebook은 정말 플랫폼 회사라고 할 수 있는 다양한 SDK를 지원하고 있고, 그 중에 안드로이드에서 Facebook을 쉽게 연동할 수 있는 SDK가 있습니다.. 보통 SDK라고 하면, 전통적인 관념에서 보자면, 소트프웨어를 개발하가 위해서 지원하는 툴킷을 포함한 라이브러리(?)라고 정의할 수 있겠습니다..

그런데, Facebook SDK를 받아서 사용해 보면, 라이브러리는 아니지만, 그렇다고 해서 SDK라고 봐야 하는지도 약간은 의문인데.. 형태는 프레임웍의 모습을 띄고 있습니다.. 흠, 모습이 좀 애매해서, 그냥 SDK라고 봐야 하나 봅니다..
서두가 너무 길었네요.. ^^;;

제가 안드로이드에서 Facebook을 연동하면서 이슈가 될 만한 것중에 2가지를 골라봤습니다..

1. 인증창이 안뜨는 경우가 종종 발생한다..
제가 2.2와 2.3버전에서 테스트를 해 봤는데, 에뮬레이터는 잘 동작합니다..
하지만, 폰에 설치를 해보니, 되는 폰도 있고 아닌 것도 있습니다..
그래서, 인증창을 띄울때 기존의 예제에서 조금 더 추가해줘야 할 필요가 있습니다.  

– 예제코드(http://developers.facebook.com/docs/mobile/android/build/)는 아래의 코드입니다.. 

facebook.authorize(this, new String[] { “email”, “publish_checkins” },
      new DialogListener() {
           @Override
           public void onComplete(Bundle values) {}

           @Override
           public void onFacebookError(FacebookError error) {}

           @Override
           public void onError(DialogError e) {}

           @Override
           public void onCancel() {}
      }
);

– 위의 코드로 인증창이 안뜨는 문제가 해결된 코드..

facebook.authorize(this, Facebook.FORCE_DIALOG_AUTH, new String[] { “email”, “publish_checkins” },
      new DialogListener() {
           @Override
           public void onComplete(Bundle values) {}

           @Override
           public void onFacebookError(FacebookError error) {}

           @Override
           public void onError(DialogError e) {}

           @Override
           public void onCancel() {}
      }
);

2. Facebook accessToken의 캐싱타임..
– 요 문제는 기본적으로 accessToken의 캐싱 타임이 하루로 설정되어 있기 때문에 발생할 수 있습니다.
https://github.com/facebook/facebook-android-sdk/blob/master/facebook/src/com/facebook/android/Facebook.java 소스의 대략 91번째 줄이 accessToken의 캐싱타임입니다..
코드를 살짝 보니, 두둥 final private long REFRESH_TOKEN_BARRIER = 24L * 60L * 60L * 1000L; <– 수정도 보지도 마라라는 코드.. ^^;;
흠, 그래서 입맛에 맞게 accessToken의 캐싱 타임을 줄이려면, Facebook SDK의 REFRESH_TOKEN_BARRIER 값을 변경해야 합니다..

위의 기본적인 2가지만 해결하면 사용하는데 이슈가 별로 없을 것 같습니다.. ^^

Android에서 String의 Base64 Utility 클래스..

보통 Base64는 특수문자나 바이너리를 인코딩해서 네트웍이나 파일처리를 위해서 사용합니다.
물론, 설정파일에 입력하기 어려운 ” 이나 특수문자가 포함된 데이터는 설정파일에 넣게 되면 에러가 발생하게 됩니다..

그래서, 민감한 데이터를 암호화해서 로컬에 저장하기도 하는데, 암호화를 하다보면 특수문자 때문에 데이터가 로컬의 설정파일에 들어가지 않는 경우가 종종 발생합니다.. 그 때도 Base64로 인코딩해서 저장을 하면 쉽게 해결이 됩니다..

그래서, 간단한 Base64 클래스를 끄적여 봅니다..

import java.io.UnsupportedEncodingException;
import android.util.Base64;

/**
 * <pre>
 * net.sjava.android.util.Base64Util.java
 * </pre>
 *
 * @author : mcsong@gmail.com
 * @version :
 * @data : 2011. 10. 2. 오후 3:34:23
 *
 */
public class Base64Util {
    
    /**
     * Encode txt
     * @param txt
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String encode(String txt) throws UnsupportedEncodingException {
        byte[] data = txt.getBytes(“UTF-8”);
        return Base64.encodeToString(data, Base64.DEFAULT);
    }
    
    /**
     * Decode txt
     * @param txt
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String decode(String txt) throws UnsupportedEncodingException {
            return new String(Base64.decode(txt, Base64.DEFAULT), “UTF-8”);
    }
}