안드로이드에서 Ant build.xml 스크립트를 통해 앱을 빌드하는 경우에 ant release 명령어를 실행하면 기본적으로는 서명 안된 앱이 만들어집니다. 서명된 앱을 만들려면 build.properties에 아래처럼 키 파일 위치를 지정해주면 되죠.

key.store=path/to/mykeystore.jks
key.alias=mykeystorealias


그러나 문제는 이렇게 설정하고 ant release를 다시 실행하면 키스토어 비밀번호를 묻는 프롬프트가 뜨면서 우리에게 입력을 요구한다는 겁니다.

... ....
-package-release:
[apkbuilder] Creating MyApp-unsigned.apk for release...

-release-prompt-for-password:
    [input] Please enter keystore password (store:mykeystore.jks):
mykeystorepass (입력값)
    [input] Please enter password for alias 'mykeystorealias':
mykeystorealiaspass (입력값)
... ....


어쩌다가 빌드한다면 별 문제 아니지만 수시로 소스 수정해서 빌드해야하는 경우라면 무지 귀찮은 일입니다. 한마디로 완전한 자동화가 아닌겁니다.
버뜨, 방법이 있습니다. build.properties에 아래 프로퍼티를 추가해주면 비밀번호 프롬프트 없이 release모드로 서명된 apk가 만들어집니다.

key.store.password=mykeystorepass
key.alias.password=mykeystorealiaspass


보안이 염려스럽다면 ant 실행옵션에 추가해주셔도 됩니다.

ant -Dkey.store.password=mykeystorepass  -Dkey.alias.password=mykeystorealiaspass release


Posted by 에코지오
,
앱의 규모가 커지고 여러가지 내부 로직/기능이 추가되다보면 전체 애플리케이션 라이프사이클 동안 하나의 인스턴스만 유지될 필요가 있는 클래스들이 늘어나게 됩니다. 그렇다고 이러한 공통모듈 성격의 클래스들에 싱글턴 패턴을 일일이 적용하는 것은 그리 좋은 방법은 아닙니다.

Spring프레임워크를 써보신 분은 다 아시겠지만 스프링에는 빈들을 체계적으로 생성해서 관리해주는 BeanFactory라는 놈이 있습니다. 안드로이드에서도 이러한 bean들의 컨테이너 역할을 하는 뭔가가 있으면 좋겠다고 생각해서 안드로이드에 Spring 프레임워크를 포함시키면 자칫 배보다 배꼽이 커질 수 있습니다.
다행히 안드로이드에는 android.app.Application 클래스가 있어서 이러한 컨테이너 역할을 수행하게 할 수 있습니다.

android.app.Application 클래스
이 클래스는 액티비티들보다 좀더 상위레벨 라이프사이클을 갖는 클래스라고 볼 수 있습니다. 우리는 이 클래스를 상속받아 간단하게나마 여러 컴포넌트의 인스턴스를 초기화하고 (하나의 인스턴스만 유지되도록) 관리할 수 있습니다.

나만의 Application 만들기
먼저 android.app.Application 클래스를 상속받아 우리만의 MyApplication을 만듭니다. 그리고 onCreate()에서 싱글턴으로 존재하길 원하는 객체를 생성합니다.

public class MyApplication extends Application {

    private static Context context;
    private static MyComponent myComponent;
    ... ... 

    public void onCreate() {
        MyApplication.context = getApplicationContext();
        MyApplication.myComponent = new MyComponent(MyApplication.context);
        ... .... 
    }
 

    /**
     * Activity와 관계없는 컴포넌트에서 부득이 Application Context를 참조하기 위한 용도.
     * Activity에 대한 참조가 가능한 티어에서는 굳이 이 메소드를 통해 context를 구하지 않아도 된다.
     */
    public static Context getAppContext() {
        return context;
    }

    public static MyComponent getMyComponent() {
        return myComponent;
    } 
... ... 


매니페스트에 Application 등록
이렇게 만든 MyApplication을 매니페스트 파일에 등록해두면 MyApplication은 앱 실행시 다른 액티비티들보다 가장 먼저 먼저 초기화됩니다.
따라서 MyApplication의 onCreate()메소드는 애플리케이션의 entry point에 해당하므로 공통컴포넌트들을 초기화할 수 있는 적당한 장소가 됩니다.

<application 

     android:icon="@drawable/icon"

     android:label="@string/app_name"

     android:name="myapp.MyApplication">

        <activity android:name=".xxx.MyActivity">

         … …

         … …

</application>  


사용법
이제 Activity든 어디든 MyComponent 인스턴스가 필요하다면 아래와 같이 불러다 쓰면 됩니다.

SomeResult result = MyApplication.getMyComponent().someMethod();


주의점
개발자가 임의로 MyComponent의 인스턴스를 생성하지 않도록 적절히 가이드해야합니다. 가이드가 잘 안지켜지면 MyApplication과 MyComponent를 별도의 패키지로 분리하고 MyComponent 생성자에 대한 접근자를 default로 만들수도 있습니다. 

참고
http://stackoverflow.com/questions/987072/using-application-context-everywhere 
http://code.google.com/p/roboguice/  : 좀더 그럴듯한 빈컨테이너(Dependency Injection 지원)

Posted by 에코지오
,
android.os.AsyncTask는 시간이 오래 걸리는 작업을 백그라운드로 처리하기 위한 코드를 단순화해주는 유틸리티 클래스입니다. (AsyncTask에 대한 사용법은 http://tigerwoods.tistory.com/28 사이트에 친절히 설명되어있습니다.)

그런데 앱에서 AsyncTask를 이용하여 비슷비슷한 작업들을 처리하다보면 AsyncTask에 공통기능을 넣어야할 필요를 느끼게됩니다. 작업을 처리하는 동안 사용자에게 처리중임을 알리는 progress dialog를 띄운다든가, 처리중에 에러가 발생한 경우 에러 toast를 보여준다든가, 사용자가 취소키를 눌렀을 때 작업을 취소시키는 등의 기능이 주요 공통된 사항으로 들어갑니다.

추가된 기능
그래서 기존 AsyncTask에 몇가지 기능을 추가한 EnhancedAsyncTask를 만들어봤습니다.

AsyncTask와 다른 점은 다음과 같습니다.

1. Activity에 대한 약한 참조(weak reference) 유지
2. onPreExecute()에서 '작업처리중' 프로그레스 다이얼로그 자동 시작
3. onPostExecute()에서 프로그레스 다이얼로그 자동 종료
4. doInBackground()에서 에러발생시 프로그레스 다이얼로그 자동 종료 및 에러메시지 토스트보여줌
    - 참고로 doInBackground()에서 에러발생시 하위 클래스의 onPostExecute()는 실행되지 않음
5. '작업처리중' 프로그레스 다이얼로그에서 사용자가 취소키 누르면 onCancelled() 실행
6. onCancelled()에서 프로그레스 다이얼로그 자동 종료 및 작업취소 메시지 보여줌


EnhancedAsyncTask 사용법
EnhancedAsyncTask를 상속받아 생성자를 정의하고 doInBackground(WeakTarget, Params) 메소드를 구현합니다. 필요시 WeakTarget을 아규먼트로 받는 onPreExecute(), onPostExecute(), onCancelled() 메소드를 오버라이드합니다. 이렇게 구현한 EnhancedAsyncTask는 아래처럼 실행시킵니다.

MyLongTask task = new MyLongTask(this); //this는 Activity 인스턴스
task.execute(someParamObjects);

 
* 참고로 onPreExecute(), onPostExecute(), onCancelled() 메소드는 메인 UI쓰레드에서 실행되므로 UI 관련 작업이 가능하지만 doInBackground()는 별도의 쓰레드에서 실행되므로 직접적인 UI 작업은 불가합니다.

LoginTask 예제
다음 코드는 사용자가 입력한 ID/PW를 원격서버에 보내어 인증을 처리하는 작업을 구현한 예제입니다.
 

public class LoginTask extends EnhancedAsyncTask<LoginModel, Void, Boolean, LoginActivity> {
    public LoginTask(LoginActivity target) {
        super(target);
    }
 
    @Override
    protected Boolean doInBackground(LoginActivity target, LoginModel... params) {
        // ID/PW를 리모트 서버로 보내 원격으로 로그인 처리
        // return true or false;
    }

    @Override
    protected void onPostExecute(LoginActivity target, Boolean loginSuccess) {
        if (loginSuccess) {
            // 메인메뉴 화면 진입
        } else {
            // 로그인 실패 메시지 보여주고 로그인 화면 유지
        }
    }


액티비티에서는 아래처럼 LoginTask를 실행하면 됩니다.

 LoginModel loginModel = ...;
 LoginTask task = new LoginTask(this); //this는 LoginActivity 인스턴스
 task.execute(loginModel);


* 물론 EnhancedAsyncTask는 AsyncTask보다는 general하지 않기때문에 프로젝트에 따라서 적절히 수정을 가해야할 것입니다.
Posted by 에코지오
,