참, 제목을 고르기 힘든데...

잠깐 상황을 설명하자면 Singleton (Utility class)에서 파라메터로 Context를 요구하는 메소드가 여럿 있는데, 이것 때문에 콜 스택에서 이것을 위해 몇 번이고 Context를 계속 넘겨줘야 하는 한다. 여기서 고민이 시작된다. 웬만하면 굳이 activity의 context가 없어도 application context만으로도 충분하니깐, application context를 어디에 저장해 두고 static method를 통해 가져오면 어떨까?


그래서 일반적으로 떠 올릴 수 있는 방법이 다음과 같다.


--- in AndroidManifest.xml ---

<application

android:name="MyApplication"

.

.

.



--- MyApplication.java ---

// The class name must be same to the name of application declared in AndroidManifest.xml

public class MyApplication extends Application {

private static Context sContext;


@Override

public void onCreate() {

super.onCreate();

sContext = getApplicationContext();

}


public static Context getAppContext() {

return sContext;

}

}


위와 같이 AndroidManifest.xml 파일의 <application> 에 android:name를 선언해 준다. 그리고 그와 같은 이름으로 Applciation을 상속받은 클래스를 만들고 onCreate()에서 Application Context를 static 변수에 저장을 한다. 이렇게 하면 Application이 시작될 때(다른 모든 컴포넌트들이 초기화 되는 것보다 빨리) Application Context가 초기화 되기 때문에 이 Context를 이용하여 Singleton을 초기화 하거나 혹은 다른 컴포넌트에서 Singleton을 이용할 때 Context에 대한 고민없이 이용할 수 도 있다.


그럴듯 해 보이지 않은가?

그런데 이렇게 작성하면 Static Field Leaks라는 android lint이슈가 발생한다.


말 그대로 Context 클래스를 static 필드에 저장하면 leak이 발생할 수 있다는 이야기다. 일반적으로 activity context를 어느 Singleton에 static으로 잡아 두었다고 생각해 보자. activity가 종료되어도 Singleton에 남아있는 activity에 대한 static reference 때문에 Application이 종료될 때 까지 activity가 Garbage Collection이 되지 않고 메모리에 남아 leak을 발생시킨다.


그런데 application context도 같을까? 안드로이드 팀에서 공식적으로 여기에 대해서 언급한 것은 없다. 다만 Application 클래스의 레퍼런스 문서를 보면 아래와 같은 내용이 나온다.


Note: There is normally no need to subclass Application. In most situations, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), include Context.getApplicationContext() as a Context argument when invoking your singleton's getInstance() method.


Application을 상속받아 global context를 이용해도 되는데, 그것 보다는 Context argument를 이용하는게 singleton을 더 모듈화할 수 있다고 적혀있다. 이걸로 유추했을 때, 위와 같이 이용해도 문제는 없으리라 생각된다.


또한, stackoverflow를 보면 저렇게 해도 문제가 없다는 의견이 많은 듯 하다. 다만 android core쪽에서 언제든 변경될 수 있기 때문에 지금은 문제가 없다 하더라도 미래를 생각하면 이용하지 않는 게 좋다는 것 같다.


회사의 다른 팀의 이야기를 들어봐도 위와 같이 이용했을 때, leak은 없었다는 이야기를 들었다.


결론적으로 나도 이 방법은 사용하지 않기로 하였다. 미래를 생각했을 때 공식적으로 인정받지 않은 로직을 피하고 싶기도 하고, Testability측면에서 모듈화 되어 있는 것이 더 낫다고 판단했다.



P.S

Application과 Activity의 생명주기에 대한 이해 없이 저 Context를 막 같다 쓸까봐 걱정되기도 했다.


반응형

설정

트랙백

댓글

  • 575 2016.12.21 14:21 답글 | 수정/삭제 | ADDR

    android:process 라는 tag를 써서 service를 별도의 process에서 수행한다거나 하면, 해당 service로 인해서 별도 process가 수행될 때, application 객체가 하나 더 만들어집니다.

    그래서 application context가 반드시 하나로만 유지되지는 않습니다. :)

    • BlogIcon 아자 2016.12.21 15:09 신고 수정/삭제

      그런 경우도 있을 수 있겠군요!
      멀티 프로세스인 경우가 그리 많지 않을 것 같긴 합니다만, 그 상황에서는 Application Context를 다룰 때는 주의해야 겠네요.

  • good 2020.02.14 12:14 답글 | 수정/삭제 | ADDR

    좋은 글 잘 봤습니다.
    같은 고민하고 있었는데 글이랑 댓글 보고 해결 됐습니다. good

Android Lint 이슈 중 UselessParent 이슈를 해결하기 위해서 보던 중 알게된 사실..


불필요하다고 생각되는 중복 LinearLayout을 하나로 합치고 나서,

Layout xml 파일의 Root LinearLayout 에 마진을 주고 background 를 설정했는데,

실제 확인을 해 보면 마진이 안 들어가 있는 문제가 있었음.

이리저리 고민해보다가 구글링 하여 알아낸 사실.


http://stackoverflow.com/questions/16278159/why-linearlayouts-margin-is-being-ignored-if-used-as-listview-row-view


위 링크에 해답이 있었음. 마진이라는 게 Parent View에서 Child의 LayoutParams를 가져와서,

여기에서 마진 만큼의 영역을 제외하고 Child를 그리도록 하는데,

Child View가 어떤 타입인지 알 수 없으므로, Parent에서는 자기가 사용하는 LayoutParams타입으로

Cast해서 사용한다.


ListView는 AbsListView.LayoutParams을 사용하는데 이게 마진을 지원하지 않음.

그래서 ListView의 아이템으로 사용하는 view의 각각에 마진을 설정하려면 중복으로 Layout을 가져가야 하여

어쩔수 없이 SupressLint 사용하였음..


RecyclerView의 경우 ViewGroup.MarginLayoutParams를 상속받아 사용하는데,
이것은 이름에서 알 수 있듯이 margin을 지원하기 때문에 ListView와 같은 layout을
사용하더라도 문제가 되지 않는다. RecyclerView가 성능이 더 좋다고 하니,
ListView을 쓴다면 갈아타는 것을 고려해보자.
RecyclerView는 Android Support Library이므로 참고할 것.


반응형

설정

트랙백

댓글

안드로이드 애플리케이션 개발 환경이 Eclipse에서 Android Studio로 넘어오면서
Memory Analzyer(이하 MAT - Memory Analyzer Tool)를 사용하는 것이 좀 애매해 졌습
니다.

기존에는 MAT가 Eclipse의 플러그인 형태로 제공되었기 때문에 MAT를 이용하려면
Eclipse를 추가로 실행해야 합니다. 또한 더 이상 Eclipse용 ADT가 업데이트 되지
않으므로 DDMS -> MAT 로 연계시켜 이용하는 방법이 어려울 거라 예상됩니다.

그래서 앞으로는 Android SDK에 포함된 Android Device Monitor(sdk/tools/monitor)에
서 hprof 덤프를 떠서 MAT Stand-alone 에서 로딩 하는 방법으로 이용해야 합니다.
(나중에라도 Android SDK에 포함된 Android Device Monitor 에서 MAT 플러그인을 설치
할 수 있도록 지원이 되어 바로 DDMS - MAT 연계가 되면 좋겠지만, 지금 테스트 해 본
결과, 현재 Eclipse MAT 플러그인 설치는 되지 않습니다.)

먼저 MAT Stand-alone 버전의 다운로드는 아래 링크에서 받을 수 있습니다.

http://ftp.kaist.ac.kr/eclipse/mat/1.6/rcp/
(이 글을 쓰는 시점 기준으로 1.6 최신 버전입니다. 공식 사이트의 속도가 너무 느려
서 일부러 한국 미러 사이트 링크를 걸어 두었습니다.)

위 링크에서 플랫폼에 맞는 버전을 선택해서 다운로드 하시면 되겠습니다.
단, 리눅스 버전의 경우 openjdk를 사용하게 되면 자꾸 죽는 현상이 발생했습니다.
혹시 같은 문제가 생기시는 분은 MemoryAnalyzer.ini 파일에 아래 두 줄을 추가해 주
세요. jdk 위치는 각자 환경에 맞게 수정하시면 됩니다.

-vm
/usr/lib/jvm/jdk1.7.0_51/bin


그리고 가져온 hprof 파일의 포맷을 변환하는 방법은 아래와 같습니다.

sdk/platform-tools/hprof-conv src dst


추가로 adb 명령으로도 hprof 덤프를 받을 수 있습니다.

adb shell am dumpheap pid혹은process명 hprof파일경로


위 명령으로 생성된 파일은 디바이스 내장메모리에 저장이 되므로, adb pull 명령으로
PC로 가져와야 합니다.  하지만 'Cause GC' 를 수행 할 수 없기 때문에 힙 덤프 분석
이 좀 더 귀찮아 질 것 같습니다.



P.S
이후 시간이 된다면 hprof-conv를 좀 더 편하게 할 수 있는 Front-End 툴을 만들어
첨부 하도록 하겠습니다.

반응형

설정

트랙백

댓글

* 이 글은 안드로이드 adb를 사용할 줄 아는 분을 기준으로 작성되어 있습니다.

   혹시 adb 가 무엇인지 모르는 분은 아래 링크의 글을 참고하시기 바랍니다.

   http://olatsee.blog.me/70162857025



안드로이드 4.0 ICS 부터는 루팅을 하지 않아도 adb를 통해 백업/복원이 가능하다.

Kairosoft 의 Game Dev Story의 세이브 데이터를 백업/복원하는 방법을 가지고 예를 들어보겠다.


(필요한 것 :  adb.exe, USB 케이블)


1. 환경설정 - 개발자옵션 - USB 디버깅 체크, 데스크톱 백업 비밀번호 설정. (백업할 폰/복원할 폰 모두)

2. 백업할 폰을 PC에 USB로 연결.

3. 커맨드창을 열고 아래 명령 실행

adb backup -f gamedevstory.ab -apk -shared net.kairosoft.android.gamedev3en

4. 휴대폰에서 데스크톱 백업 비밀번호 입력 후 백업이 끝날 때까지 대기.

5. 복원할 폰을 PC에 USB로 연결.

6. 커맨드창에서 아래 명령 실행.

adb restore gamedevstory.ab

7. 휴대폰에서 데스크톱 백업 비밀번호 및 백업받은 폰에서 입력했던 비밀번호 입력.


위와 같이 하면 루팅이 없이도 어플의 세이브 파일을 백업/복원이 가능하다.




반응형

설정

트랙백

댓글

  • 가을방학 2014.02.02 18:52 답글 | 수정/삭제 | ADDR

    아 이렇게 좋은 방법이!!
    간편하게 업데이트 할 수 있겠네용

JB 에서 사용 가능한 방법

이 방법은 내가 회사 테스트폰에서만 확인한 방법이라,

다른 폰에서도 공통적으로 사용가능 한지 모르겠다.


adb shell input <명령>


을 사용하는 방법인데, JB 의 input 에는 ICS 는 없던 명령이 추가 되었다.

바로 tap 이라는 명령.


adb shell input tap x좌표 y좌표

adb shell input keyevent <키코드값 or 키코드이름>


위 두 가지로 매우 편하게 터치/키이벤트를 보낼 수 있다.




ICS 에서 사용 가능한 방법

참고사이트: http://whitesnows.blogspot.kr/2011/02/android-monkey-tool-script.html

==> Monkey Tool Script 를 이용


1. 이벤트를 정의한 txt 파일을 만들어서

2. txt 파일을 폰의 임의의 위치에 밀어 넣고  --> adb push a.txt /mnt/sdcard/

3. adb shell monkey -f /mnt/sdcard/a.txt [반복횟수]



반응형

설정

트랙백

댓글

  • ksm 2013.01.29 11:51 답글 | 수정/삭제 | ADDR

    Adb shell input tap 300 180
    이런 식으로 하면 되나요?

    • BlogIcon 아자 2013.01.29 12:16 신고 수정/삭제

      네, 맞습니다.
      JB 설치된 휴대폰이 있으시다면 바로 확인해 보실 수 있을꺼예요.

  • 2013.01.29 22:57 답글 | 수정/삭제 | ADDR

    비밀댓글입니다

    • BlogIcon 아자 2013.01.30 11:37 신고 수정/삭제

      말씀하신 어플이 무슨용도로 쓰는건가요?
      폰에서 바로 shell 을 실행시킬 수 있는 어플인가요?

      그렇다면 shell 에서
      input tap <x> <y>
      라고 입력하면 동작할 꺼라 생각합니다만...

      그리고 강제 리붓 후, 안된다는게 무슨말인지 잘 모르겠네요..

  • 나노 2013.04.25 21:07 답글 | 수정/삭제 | ADDR

    안녕하세요. 젤리빈에서 몽키스크립트 테스트중인데... UserWait는 전혀 듣질 않더군요..
    혹시 이유를 아시나요?

    • BlogIcon 아자 2013.04.26 08:34 신고 수정/삭제

      안녕하세요?
      제가 테스트 해 보니 잘 동작을 하네요.

      혹시 UserWait 에서 오타가 발생했거나,
      ms 단위로 값을 넣어야 하는데 착오로 second 단위로 넣으신건 아닌지요?

  • BlogIcon 쎄미 2014.04.10 10:59 신고 답글 | 수정/삭제 | ADDR

    고맙습니다~

결론부터 이야기 하면, 한글 계정명 때문에 발생하는 문제로 보인다.
android-sdk 의 tools 폴더에서 직접 커맨드 입력으로 AVD 를 만들면 해결된다.
tools 폴더에서
'android create avd -n <name> -t <target ID> -p <path>'
명령으로 AVD 를 생성할 수 있다.
target ID 는 'android list targets' 명령으로 확인 할 것.

더 자세한 옵션은 아래 링크를 참고할 것.
http://developer.android.com/guide/developing/tools/avd.html


----------------------------------------------------------------------
회사에서 안드로이드 공부를 해야 할 일이 생겨서,
집에 있던 노트북에 jdk, eclipse, ADT, android-sdk 를 설치하였습니다.
그런데 회사에서는 문제없던 한글계정명이 집에선 말썽을 부리는 군요.
윈도XP 와 윈도7의 차이인 것인지...

반응형

설정

트랙백

댓글