출처 : http://dev.paran.com/2012/01/31/android-strict-mode-howto/

 

Android 어플리케이션의 반응 속도 및 효율성을 모니터링 할 수 Strict mode 에 대해서 이야기해 보려고 합니다. Strict mode 는 Android Developer 블로그 사이트에 Brad Fitzpatrick에 의해 이미 New Gingerbread API: StrictMode라는 제목으로 포스팅 되어있습니다. 이 후 Honeycomb 버전부터 신규 API 가 추가되고 Ice Cream Sandwich(이하 ICS) 에서의 변화가 있어 Brad Fitzpatrick 의 블로그 내용을 바탕으로 다시 한번 재조명하는 시간을 갖고자 합니다.

2. Strict Mode 의 필요성

이미 많은 개발자들이 아시다시피 Android 메인 thread는 라이프 사이클과 다양한 callback들을 처리하고 있습니다.
따라서 메인 thread에서 시간이 오래 걸리는 작업들을 하게 되면 화면 응답속도가 현저히 느려져 사용자들에게 불편함을 가중시키게 됩니다.
특히, Activity 에서는 이벤트등의 처리 시간이 5초, BroadcastReceiver 에서는 완료 시간이 10초 이상인 작업을 하게 되면 Android 에서는 ANR(Application Not Responding) 라는 팝업이 발생하게 됩니다. 이는 사용자들이 외면하는 어플리케이션으로 자리매김하도록 인도해 주니 공들여 개발 해놓고 아쉬운 소리 듣기 않기 위해서라도 어플리케이션 응답속도는 최대한 민감하게 반응할 수 있도록 설계와 개발을 진행해야 합니다.

Android 플랫폼에서는 이를 좀 더 쉽게 찾아 개선할 수 있도록 Strict Mode API를 제공하고 있습니다.
Strict mode 는 크게 Thread 와 VM에 관련된 정책을 만들 수 있고 ThreadPolicy를 통해 특정 Thread 에 File Access, Netwrok Access 부분을 감지하여 위반되는 위치를 찾아주고 VmPolicy를 통해 메모리 Leak의 위치를 감지해 주는데 개발자가 이를 인지 할 수 있도록 Log, 강제 종료, dropbox, dialog, splash의 방법으로 알림을 제공합니다.

그러나 모든 부분을 다 감지 하지는 못합니다. 특히 요즘 들어 native 를 사용하는 어플리케이션이 많이 증가하고 있는데 native 에서 사용하는 File, Network 는 감지 하지 못하는 case 가 있으니 참고하여 개발을 진행해야 합니다.

3. 왜 File, Network Access 가 위험한가?

앞서 말씀 드린 블로그 사이트에 가면 Android File System 에 언급이 되어 있습니다.
Android 에서 사용하는 YAFFS(Yet Another Flash File System) 파일 시스템은 File I/O 작업을 진행하면 Global 범위 lock이 사용됩니다.

즉, 한 프로세스가 백그라운드 I/O 작업을 하고 있으면 다른 Process 선행 작업이 끝나기 전까진 대기 상태가 됩니다. 때문에 운이 나쁘면 아주 오랜 시간 동안 대기를 할 수 있는 상황이 발생됩니다. Network 사용은 모든 개발자들이 인지하고 있듯이 시간이 매우 오래 걸릴 수 있고 예측 가능하지 않습니다. 따라서 File Access 보다 더 위험하니 절대로 메인 Thread 에서는 Network를 사용하면 안됩니다.

4. Strict Mode 사용법

Strict mode 는 아주 간단하게 선언하여 사용 가능합니다. 어플리케이션의 Activity 내의 onCreate() 메소드 내의 다음과 같이 선언만 해주면 됩니다.

    public static final boolean SUPPORT_STRICT_MODE = Build.VERSION_CODES.FROYO < Build.VERSION.SDK_INT;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        boolean debaggable = (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;

        if (SUPPORT_STRICT_MODE && debaggable) {
            StrictMode.ThreadPolicy.Builder builder = new StrictMode.ThreadPolicy.Builder();
            builder.detectCustomSlowCalls(); // api level 11
            builder.detectNetwork();
            builder.detectDiskReads();
            builder.detectDiskWrites();
            builder.detectNetwork();
            //위반시 로그로 알림
                    builder.penaltyLog();
            //위반시 dropbox 에 저장
            builder.penaltyDropBox();
            //위반시 다이얼로그로 알림
            builder.penaltyDialog();
            //위반시 강제종료
            builder.penaltyDeath();
            //네트워크 사용 위반시 강제종료 , 반드시 detectNetwork() 가 활성화 되어 있어야함.
            builder.penaltyDeathOnNetwork(); // api level 11
            //위반시 화면에 빨간 사각형 Spash로 알림.
            builder.penaltyFlashScreen(); // api level 11
            StrictMode.setThreadPolicy(builder.build());

            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                    .detectLeakedSqlLiteObjects()
                    .detectActivityLeaks() // api level 11
                    .detectLeakedClosableObjects() // api level 11
                    .setClassInstanceLimit(DummyClass.class, 1) // api level 11
                    .penaltyLog()
                    .penaltyDeath()
                    .penaltyDropBox()
                    .build());
        }
        super.onCreate(savedInstanceState);
    }

어플리케이션이 Debuggable 속성을 조회하여 true 일 때만 Strict Mode 를 활성화 시키도록 코드를 구성했습니다.
StrictMode API 는 Android OS 버전이 업그레이드 되면서 계속적으로 확장할 예정이기 때문에 여러분이 개발한 상용버전의 어플리케이션에 Strict mode 를 포함 할 경우 강제 종료 되는 현상을 야기 할 수 있습니다. 그러므로 상용버전에는 반드시 Strict mode 를 빼고 마켓에 등록해야 합니다. (설마 상용 바이너리에 debuggable 속성값을 true로 하진 않겠죠?)

API 를 보면 detectXXX() 는 정책을 적용하는 것이고 penaltyXXX() 는 정책에 위반 되었을 때 어떻게 알려줄 것인가를 지정해 주는 API 입니다.
penaltyXXX() 의 경우는 여러 가지를 한꺼번에 써도 무방하니 개발 환경에 알맞은 방법을 선택해서 사용하시면 됩니다.

위에서 선언한 방식은 조금 번거로운 방식입니다. 좀더 모든 기능을 사용 가능하도록 하는 간단한 API 가 있습니다.

    if (SUPPORT_STRICT_MODE
            && debaggable) {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build());
    }
    //or
    if (SUPPORT_STRICT_MODE
            && debaggable) {
         StrictMode.enableDefaults();
    }

만약 어플리케이션이 프로요 이하 버전에서만 작동된다면 다음과 같이 리플랙션을 사용하여 선언하고 Gingerbread 이상 버전의 에뮬레이터에서 테스트 하면 Strict mode 기능을 활용 할 수 있습니다.

    //Strict mode 가 지원되지 않는 os 버전에서 구동되는 어플리케이션의 경우
    //리플렉션을 이용하여 진저브레드 이상의 에뮬레이터 및 단말에서 테스트 진행 가능함.

    Class strictMode = null;
    Method enableDefaults = null;
    try {
        strictMode = Class.forName("android.os.StrictMode");
    } catch (ClassNotFoundException e) {
         Log.e(LOG_TAG, e.getMessage(), e);
    }

    if(strictMode != null){
        try {
           enableDefaults = strictMode.getMethod("enableDefaults", null);
        } catch (NoSuchMethodException e) {
            Log.e(LOG_TAG, e.getMessage(), e);
        }
    }

    if(enableDefaults != null){
        try {
            enableDefaults.invoke(null, null);
        } catch (IllegalArgumentException e) {
             Log.e(LOG_TAG, e.getMessage(), e);
        } catch (IllegalAccessException e) {
             Log.e(LOG_TAG, e.getMessage(), e);
        } catch (InvocationTargetException e) {
             Log.e(LOG_TAG, e.getMessage(), e);
        }
    }

5. 결과물 확인

  • penaltyLog() 사용: 개발자들이 흔히 쓰는 logcat 을 통해서 확인이 가능하고 “StrictMode” Tag 로 위반되는 부분만 필터링 또한 가능합니다.
  • null

  • panaltyDropBox() 사용: 안드로이드 OS 내의 DropboxManagerService 에 로그가 저장이 됩니다.
    이는 windows command 창에서 “adb shell dumpsys sropbox data_app_strictmode –print > strict.txt” text 파일로 저장하여 확인이 가능합니다. 정보 또한 detail 하여 가장 추천하는 방식입니다.
  • null

  • penaltyDialog() 사용: 설정한 정책에 위배됐을 경우 확인 dialog 창에 보여 줍니다.
    빈번하게 발생한다면 무척 번거로워 질 것입니다. 성능개선이 많이 이뤄지고 QC 를 진행할 때 사용하면 좋을 것으로 판단됩니다. 다이얼로그의 모습은 다음과 같습니다.(Honeycomb 기준)
  • null

6.Honeycomb 이상에서 제공되는 API

  • custom policy 의 적용
  • 특정 소스 위치에 StrictMode.noteSlowCall(“CustomTest”)를 선언하고 Thread 정책에 detectCustomSlowCalls() 를 넣어주게 되면 StrictMode 에서 noteSlowCall() 이 선언된 부분을 모두 감지하고 penaltyXXX() 정책에 따라 개발자에게 알려주니 사용하시면 됩니다.

  • penaltyDeathOnNetwork()
  • Main 쓰레드에서 Network Access 가 진행되면 무조건 어플리케이션이 강제 종료가 되도록 설정하는 것입니다.
    이 기능은 detectNetwork() 정책이 활성화 되어 있을 때만 유효합니다.

  • penaltyFlashScreen()
  • 정책이 위반 됐을 때 화면상으로 아래와 같은 빨간 테두리의 사각형이 splash 로 보여줘 인지하기가 수월해 집니다.
    null

  • VM policy
  • Activity 서브클래스의 leak 을 감지 해주는 detectActivityLeaks() 과
    File/Network IO 관련 API 사용시 close() 를 명시적으로 호출 안하고 종료할 경우 나오는 리소스 leak 을 감지 해주는 detectLeakedClosableObjects() 와
    특정 클래스가 어플리케이션 Heap 에 생성되는 최대 개수를 설정하여 object leak 현상을 모니터링 할 수 있도록 setClassInstanceLimit() 가 있습니다.

7. ICS에서 강화된 정책

Honeycomb 까지는 이상 없이 구동된 어플리케이션이 ICS 에서는 메인 thread 에서 File / Network Access 를 하게 되면 무조건 강제 종료가 됩니다.
어쩔 수 없이 써야 한다고 하면 Strict Mode 에서 permitAll() API 를 사용하면 죽는 현상을 막을 수는 있지만 근본적인 해결책은 아닙니다.
해당 어플리케이션에 Strict mode 를 적용하고 위반되는 부분을 찾아 로직 개선을 해야 합니다.

8. Strict Mode 에 위배된 로직의 개선

앞서 말씀드린 블로그에서 언급된 것 처럼 Thread 와 java.util.concurrent.*의 pacakge 및 HandlerThread, AsyncTask, AsyncQueryHandler, IntentService 등의 API 를 활용하시면 됩니다.

9. Strict Mode 샘플소스

제가 만든 간단 테스트 소스를 첨부 하였습니다.
Acitivity 내에서 Handler 를 만들어 놓고 메인 Thread 에 영향을 미치는 코드와 이를 개선하기 위해 HandlerThread 를 사용해서 간단히 로직 개선한 코드를 넣었으니 참고 하시면 됩니다.
또, Brad Fitzpatrick 이 간단하고 이해하기 쉽게 만들어 놓은 샘플이 있어서 링크 걸어 둡니다.
앞에서 언급한 API 레벨별 사용가능한 정책을 확인하시고 테스트 해보시면 됩니다.

10. 결론

안드로이드 어플리케이션을 개발할 때 Strict Mode 는 옵션이 아닌 필수사항입니다.
기존에 Strict mode 없이 개발된 어플리케이션이 있다면 지금 당장 Strict mode를 사용해서 검증하고 Stric mode 에 위반된 소스를 수정 해보세요~
어플리케이션이 눈에 띄게 빨라지는 효과를 경험하게 되실겁니다.

추가로 어플리케이션에 Strict Mode 를 Thread 와 VM 의 detectAll () 을 선언하고 penaltyDropBox()로 세팅 후에 “Monkey Test” 를 돌리면 정말 유용하지 않을 까 생각되어 집니다.

모두 즐코딩 하시기 바랍니다~ 감사합니다

 

'Android > Tip&Tech' 카테고리의 다른 글

Android HttpClient Get 과 Post 정리  (0) 2012.05.14
DB별 Auto_increatement 정리  (0) 2012.05.04
Listview 안에서 list 뒤집기  (0) 2012.04.18
Android/Intent 사용 예  (2) 2012.03.20
[펌]svn 구축  (0) 2012.02.23

+ Recent posts