안드로이드에 AOP를 적용할 수 있는지 구글 형님께 알아보니 가능하다는 답변이 나옵니다.

=> http://blog.punegtug.org/2010/11/adding-aspect-to-android.html


1. 그러나 안드로이드는 런타임 바이트코드 생성을 지원하지 않기 때문에 컴파일타임 위빙만 가능합니다.

=> http://stackoverflow.com/questions/3759232/aspect-oriented-programming-in-android

2. 구글 Guice는 메소드 인터셉터 같은 일부 AOP 기능을 제공하는데

  이는 런타임 바이트코드 생성 방법을 사용하므로 안드로이드에서는 사용할 수 없습니다.

=> http://code.google.com/p/google-guice/wiki/OptionalAOP

3. 어쨋든 안드로이드에서도 컴파일 타임 위빙을 통한 AOP는 가능하기 때문에

  AspectJ 라이브러리를 이용해 AOP 프로그래밍을 할 수 있습니다.

=> http://deansserver.co.uk/~dean/2011/07/18/aspect-oriented-android-development-tool-integration/
=> http://code.google.com/p/android-aspectj/


4. 그럼 안드로이드에서 AOP를 어디에 활용할수 있을까요?

(1) 공통 에러 처리

- 발생된 Exception을 잡아서 에러로그를 서버로 보내 앱 유지보수에 참고할 수 있습니다.

  (물론 에러정보를 서버로 전송할지 사용자에게 물어야겠죠).

- Exception 발생시 안드로이드는 안드로이드 고유의 에러 창과 함께 앱프로세스가 죽어버리는데,

  이렇게 하기보다는 좀더 우아한 메시지와 함께 에러창을 꾸밀 수 있습니다.

- 또한 앱프로세스가 종료되지 않게 하거나 에러가 발생한 Activity만 종료하게 할 수 있을 것입니다.

  (앱을 종료할지 말지는 에러정책에 따라 달라질 것입니다)


(2) 개발을 위한 로깅

- 개발시 편의를 위해 디버그용 로그를 모든 Activity의 라이프사이클 메소드에 추가할 수 있습니다.

- Activity의 UI 렌더링 속도를 측정하기 위해 onCreate() 실행 전과 onResume() 실행 후의 경과시간을 계산할 수 있습니다.


(3) 아키텍처 위반 체크

- 예를 들어 레이어링 아키텍처를 갖도록 설계된 앱에서 아키텍처를 깨는 호출이 존재하는 경우 아예 컴파일이 안되게 처리할 수 있습니다.


(4) Activity 라이프사이클 메소드에 기능 추가

- 예들들어(이건 정말 그저 예입니다) 로그인이 포함된 앱에서 앱 자체의 세션 타임아웃을 체크하여 세션이 만료된 경우 로그인 Activity로 이동하게 할 수 있습니다. AOP를 이용하여 로그인 Activity를 제외한 다른 모든 Activity의 onCreate()나 onResume() 메소드에 세션타임아웃 체크 로직을 추가하면 됩니다.  단 이 경우 반드시 해당 메소드 "코드"가 액티비티에 존재해야합니다(왜냐면 안드로이드에서는 컴파일타임 위빙만 가능하기 때문)

- 또다른 예로 카톡같은 화면잠금을 생각할 수 있습니다. 모든 Activity의 onResume()과 onPause()에 화면잠금 로직을 쉽게 추가할 수 있습니다.


(5) ... ...



Posted by 에코지오
,

아래는 안드로이드 개발시 AspectJ 라이브러리를 사용하는 경우 기본적인 ProGuard 설정에 추가해주어야 할 내용입니다.

# aspect 클래스 및 aspect가 적용되는 클래스에서 AspectJ 라이브러리를 참조할 수 없다는 경고 제거
# (can't find referenced class)
-dontwarn org.aspectj.**

# AspectJ 클래스 변경금지
-keep class org.aspectj.**

# Aspect 클래스 보존
-keep @org.aspectj.lang.annotation.Aspect class * { *; }
-keepclasseswithmembers class * {
  public static *** aspectOf();
}

# 패키지 변경 금지(주석해제시 런타임에 java.lang.NoSuchMethodError 에러 발생)
#-repackageclasses ''
#-allowaccessmodification

# around 어드바이스가 적용되는 target 클래스에서 around 어드바이스 메소드를
# 참조할 수 없다는 경고 제거(can't find referenced method) : aspect 클래스를 지정
-dontwarn my.app.aop.**
(또는 -dontwarn my.app.**.*Aspect 식으로 설정)


Posted by 에코지오
,

저같은 경우 안드로이드 앱 개발시 JSON 데이터 처리를 위해 주로 Codehaus의 jackson json 라이브러리 (jackson-core-asl-x.x.jar, jackson-mapper-asl-x.x.jar)를 사용합니다. 

이렇게 앱에서 Jackson JSON 라이브러리를 사용하는 경우 ProGuard 설정을 어떻게 해줘야하는지 제대로 정리된 자료가 별로 없더군요.

그동안은 릴리스 빌드시에 잘못된 proguard 설정때문에 발생하는 에러나, 앱 실행시에 잘못된 소스난독화로 인한 런타임 에러를 만날때마다

임시방편으로 설정의 의미도 모른채 설정을 조금씩 고쳐가면서 소뒷걸음질하다 운좋게 에러를 해결하곤 했습니다. 에러가 없게 설정했다고 해도 왜 그런 설정이 필요한지 자세한 이유를 알지는 못했죠.

미루고 미루다 이제야 ProGuard 홈페이지 문서 Gson에서 사용하는 proguard 설정을 참고하여 아래와 같이 정리해보았습니다.

실제로 제가 개인적으로 개발중인 앱에 적용해서 빌드하고 테스트해보니 이상이 없었습니다.

# 어노테이션 보존

-keepattributes *Annotation*,EnclosingMethod


# 제너릭 타입 정보 보존

-keepattributes Signature


# Jackson이 참조하는 다른 라이브러리(joda-time 등) 없다는 경고 제거

-dontwarn org.codehaus.jackson.**


# java.lang.NoSuchFieldError: PUBLIC_ONLY 에러 제거

-keepnames class org.codehaus.jackson.** { *; }


# Jackson에 의해 JSON데이터와 매핑(바인딩)되는 모델(POJO) 클래스 보존

-keep public class my.app.model.** { *; }


# 모델 클래스외에 getter/setter 보존이 필요한 경우

#-keep public class my.app.** {

#  public void set*(***);

#  public *** get();

#}



Posted by 에코지오
,
아래글에서는 WebView와 웹서버간의 HTTP 통신내용을 모니터링하는 방법을 설명했습니다. 이번에는 Spring-Android 라이브러리를 이용하여 앱과 웹서버간에 REST로 통신하는 경우에 통신 내용을 엿보는 방법입니다.

import org.apache.http.HttpHost;
import org.apache.http.conn.params.ConnRoutePNames;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
...

public class MyRestActivity extends Activity {

  public xxx someMethod() {
  
    RestTemplate restTemplate = new RestTemplate();
    ... ...
    
    if (isDebugMode(this.getApplicationContext())) { 
      HttpComponentsClientHttpRequestFactory factory =
                   (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();
      HttpHost proxy = new HttpHost("본인로컬PC의 IP주소", 프록시포트, "http");
      factory.getHttpClient().getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
    }

    ... ...
    ResponseEntity<Xxx> result = 
restTemplate.exchange(url, HttpMethod.POST, requestEntity, Xxx.class);
    ... ...
 
  }
}  


아래글에서 지적했듯이 개발자 PC의 IP주소에 localhost나 127.0.0.1 루프백 주소를 쓰면 안됩니다.

이클립스 TCP/IP 모니터 뷰 설정하는 방법은 아래글과 동일합니다.


실제 앱 개발시에는 위와 같이 프록시를 설정한 RestTemplate 인스턴스를 공통모듈단에서 한개만 만들어놓고 재사용하면 됩니다.
 
Posted by 에코지오
,
WebView를 사용하는 앱 개발시 Eclipse의 TCP/IP Monitor를 통해 WebView와 웹서버간의 HTTP 통신내용을 보는 방법을 설명합니다.

1. 프록시 세팅

import myapp.ProxySettings;
...
public class MyWebActivity extends Activity {

   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      
      if (isDebugMode(
this.getApplicationContext())) { 
         ProxySettings.setProxy(this.getApplicationContext(), "본인로컬PC의 IP주소", 프록시포트);
      } 
      ...
      ...
      myWebView.loadUrl("http://웹서버IP:웹서버포트/xxxx.jsp");
   }


웹페이지를 로딩하기 전에 ProxySettings를 통해 프록시를 설정합니다. ProxySettings 클래스는 http://manojtk.blogspot.com/2011/01/android-webview-proxy-setting.html 사이트에서 구할 수 있습니다.

여기서 주의할 점이 2가지가 있습니다.

(1) 개발자 PC의 IP주소에 localhost나 127.0.0.1 루프백 주소를 쓰면 안됩니다. 앱은 PC가 아니라 폰에서 실행되므로 루프백 주소는 폰의 로컬 IP입니다.
(2) 프록시 설정은 실제 마켓에 올라가는(production 환경) 앱에 반영되면 안되므로 개발시점(development 환경)에서만 적용되도록 해야합니다. 위 예제소스에서는 앱이 debug모드로 서명되었는지를 체크하여 프록시를 세팅하고 있습니다.

    private boolean isDebugMode(Context context) {
        try {
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(
                    context.getPackageName(), 0);
            int flags = packageInfo.applicationInfo.flags;
            return (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
        } catch (NameNotFoundException e) {
            return true;
        }
    }

    

2. 이클립스에서 TCP/IP 모니터링 설정
 



(1) 이클립스 Preferences 창을 띄웁니다.
(2) TCP/IP Monitor 메뉴를 선택합니다.
(3) Add 버튼을 눌러 모니터를 추가합니다.

* Local monitoring port : 앞서 설정한 프록시 포트
* Host name : 웹서버 IP
* Port : 웹서버 포트

(4) Start 버튼을 눌러 모니터링을 시작합니다.


3. 이클립스에서 TCP/IP Monitor 뷰를 통해 HTTP통신내용 확인
폰에서 폰갭앱을 실행하여 웹페이지를 띄우면 이클립스에서 TCP/IP Monitor뷰가 뜨면서 통신내용을 볼 수 있습니다.

 
Posted by 에코지오
,
안드로이드에서 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 에코지오
,
웹구간 보안을 위해 SSL을 적용하려면 인증서를 생성해서 웹서버에 설치/설정해주고 브라우저에서는 "https" 프로토콜로 접속하면 됩니다. 그러나 만약 인증서가 Verisign 등 신뢰된 기관(root CA)에서 서명된 인증서가 아니라면, 브라우저는 사용자에게 보안 경고창을 보여주어 웹페이지를 볼 건지 말건지 물어봅니다.


root CA로부터 서명된 인증서라면 이런 경고창 없이 페이지 내용이 바로 보여집니다(참고로 특정 모바일기기에서 어떤 인증기관의 인증서를 root CA로 인식하는지는 http://www.ssltest.net/ 사이트에서 테스트 가능합니다).

root CA로부터 서명을 받기 위한 비용은 대략 1년에 수 십만원이기 때문에 가난한 프로젝트 또는 개인들은 root CA 서명이 아닌 자체(self) 서명된 인증서를 사용할 수밖에 없습니다. 문제는 이렇게 셀프 서명된 인증서는 공식적으로는 신뢰할 수 없으므로 위와 같은 보안 경고창이 뜬다는 것이죠.
사실 보안 경고창이 뜬다고 해도 사용자에게 "계속" 버튼을 누르도록 교육시키면 그리 큰 문제는 아닙니다. 그러나 폰갭에서 똑같은 페이지를 불러온다면 어떤 일이 벌어질까요?

loadUrl("https://yourserver.com/some/page.html");


웁스.. 기대와 달리 그냥 텅빈 화면만 나옵니다.-.-


공백 화면이 나오는 이유는 신뢰되지 않은 인증서인 경우 android.webkit.WebViewClient에서 아래와 같이 페이지로딩 작업을 cancel시키기 때문입니다.

public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
    handler.cancel();
}


자 이제 원인을 알았으니 해결해보죠.

먼저 SSL에러가 발생하더라도 페이지 로딩을 계속 진행하도록 onReceivedSslError() 메소드를 재정의한 클래스를 만듭니다.(DroidGap을 상속한 Activity의 inner 클래스로 정의)

private class MyGapViewClient extends GapViewClient {
    public MyGapViewClient(DroidGap ctx) {
        super(ctx);
    }

   @Override
   public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        handler.proceed();  //SSL 에러가 발생해도 계속 진행!
    }
}


이렇게 만든 MyGapViewClient 클래스를 onCreate()에서 아래처럼 세팅해줍니다.

public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   … …
   super.init();
   this.setWebViewClient(this.appView, new MyGapViewClient(this)); 
   … …
   loadUrl("https://yourserver.com/some/page.html");
}

 
빌드하고 앱을 다시 실행해보면 정상적으로 SSL보안이 적용된 웹페이지가 뜨는 것을 확인할 수 있습니다.
Posted by 에코지오
,
PhoneGap(폰갭)에서는 anrdoid.app.Activity.onKeyDown() 메소드를 아래와 같이 재정의하여 사용자가 하드웨어 취소키(back키)를 누르면 브라우저의 뒤로가기를 수행합니다.

// com.phonegap.DroidGap.onKeyDown(int, KeyEvent) 메소드 

public boolean onKeyDown(int keyCode, KeyEvent event) {
 ... ...
     // If back key
     if (keyCode == KeyEvent.KEYCODE_BACK) {

     // If back key is bound, then send event to JavaScript
     if (this.bound) {
     this.appView.loadUrl("javascript:PhoneGap.fireEvent('backbutton');");
     }

     // If not bound
     else {

     // Go to previous page in webview if it is possible to go back
     if (this.appView.canGoBack()) {
     this.appView.goBack();
     }

     // If not, then invoke behavior of super class
     else {
     return super.onKeyDown(keyCode, event);
     }
     }
     }
... ...


그러나 때로는 사용자가 취소키를 누르면 이전 웹페이지로 이동하지 않고 앱을 종료시키는 것으로 정책을 바꾸고 싶은 경우도 있습니다. (특히나 아이폰을 고려하여 이전버튼을 포함하도록 웹페이지를 디자인한 경우).

폰갭에서는 취소키 동작을 재정의하는 2가지 방법이 있습니다.
 
1. DroidGap의 onKeyDown 메소드 오버라이딩
DroidGap을 상속받은 Activity에서 onKeyDown 메소드를 오버라이딩해서 백버튼을 적절히 핸들링해줍니다. 아래코드에서는 앱 종료여부를 묻는 확인창을 띄우도록 했습니다.

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        confirmAppExit();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

private void confirmAppExit() {
    AlertDialog.Builder db = new AlertDialog.Builder(this);
    db.setTitle(R.string.exit_app_title)
         .setMessage(R.string.exit_app_message) // "프로그램을 종료하시겠습니까?"
         .setCancelable(true)
         .setPositiveButton(android.R.string.ok, new OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 finish(); //확인버튼 누루면 앱 종료
             }
         })
         .setNegativeButton(android.R.string.cancel, null)
         .show();
}



2. 폰갭 javascript의 'backbutton' 이벤트 재정의(phonegap 0.9.5 이상)

function onLoad() {
    document.addEventListener("backbutton", backKeyDown, true);
}
function backKeyDown() {
    if (confirm('프로그램을 종료하시겠습니까?')) {
        navigator.app.exitApp();
    }
}
...
...
<body onload="onLoad()">


단순히 취소키 눌렀을 때 페이지 뒤로가기를 막으려면 아래코드로 충분합니다.

navigator.app.overrideBackbutton(true);


Posted by 에코지오
,