안드로이드 앱 성능 최적화 – 1. 즉각적인 반응을 위한 StrictMode 사용

안드로이드 플랫폼이 국내에 출시 된지도 약 3년이라는 시간이 지났습니다. 개발자들도 많은 경험이 축척되면서 앱의 퀄리티는 점점더 높아지고 있으나, 아직 해외 만큼의 퀄리티를 가진 국내앱은 손에 꼽힐 정도라고 생각합니다. 이런 퀄리티 높은 앱을 개발하기위해 좀 더 성능을 높이는 방법에 대해 소개 하기위해 “안드로이드 앱 성능 최적화”라는 주제를 가지고 연제글을 쓰게 되었습니다.

안드로이드앱 성능 최적화 – 1. 즉각적인 반응을 위한 StrictMode 사용

안드로이드앱 성능을 최적화 하는 방법중 첫번째 시간으로 사용자의 즉각적으로 반응을 보이기위해 도움을 줄 수있는 StrictMode에 대해서 이야기해보겠습니다.

안드로이드 앱을 사용 하면서 참 빠르다라는 것을 느끼게 되는것은 사용자의 이벤트에 대한 화면응답 속도가 빠른경우입니다. 

안드로이드 Main Thread(ui)에서 LifeCycle과 Linstener에 대한 Callback등을 처리 하고 있습니다. 

이렇게 Main Thread에서 오래 걸리는 작업(DB에 데이터 insert한다거나 네트워크를 통해 이미지를 불러온다거나)을 하게 된다면, 작업시 동안 아무런 화면에 반응이 없을 뿐더러, 최악의 경우 5초이상 걸리게 된다면 ANR발생으로 앱이 종료되어 버립니다.

Network Access나 파일 IO등에 대한 처리는 새로운 쓰레드에서 작업(AsyncTask, Loader)을 함으로써 이런 반응 속도에 대한 문제를 해결 할 수 있습니다.

하지만 개발자들은 이렇게 코드에 대한 응답 속도를 예측이 불가능 하기때문에, 새로운 쓰레드작업을 해야 할지 말아야 할지에 대해 선택이 어렵습니다.

이런 문제점을 해결 해줄수 있게 안드로이드 플랫폼에서는 StrictMode API를 제공하고 있습니다.

StrictMode는 Thread, VM에 관련된 정책을 만들 수 있으며, ThreadPolicy를 통해 특정 Thread 에 File I/O Netwrok Access 부분을 감지하여 위반되는 위치를 찾아주고 VmPolicy를 통해 메모리 Leak의 위치를 감지해 주는데 개발자가 이를 인지 할 수 있도록 Log, 강제 종료등 다양한 방법으로 알림을 제공합니다.

문제점이 있다면 Native에서 사용하는 File I/O, Network Access등에 대한 감지는 어렵습니다. 그리고 StrictMode는  API 9(2.3.3)부터 사용이 가능합니다.

StrictMode 사용으로 간단한 테스트를 해보겠습니다. 

package com.example.testapp;

import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import android.app.Activity;
import android.os.Bundle;
import android.os.StrictMode;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Activate StrictMode
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectAll().penaltyLog().penaltyDeath().build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
                .penaltyLog().penaltyDeath().build());

        setContentView(R.layout.activity_main);
        
        // Test code
        String eol = System.getProperty("line.separator");
        try {
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
                    openFileOutput("myfile", MODE_WORLD_WRITEABLE)));
            writer.write("This is a test1." + eol);
            writer.write("This is a test2." + eol);
            writer.write("This is a test3." + eol);
            writer.write("This is a test4." + eol);
            writer.write("This is a test5." + eol);
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Main Thread에서 File I/O작업을 하게 테스트코드를 만들었습니다. StrictMode에서는 이를 감지(긴 작업) 하며 강제적으로 앱을 종료하고 로그를 남기게 되었있습니다.

이렇게 LogCat에 위반된 로그를 남깁니다. StrictModeDiskViolation이라고 File I/O대한 문제라는 것을 알려 줍니다. 작업 시간이 114ms가 걸렸다는 것도 보실 수 있습니다. File I/O가 더 많이 일이나게 되면 시간이 훨씬 더 걸리므로 File I/O작업을  Main Thread가 아닌 새로운 Thread에서 작업을 하도록 구조를 바꾸면 됩니다. Loader나 AsyncTask, AsyncQueryHandler, HandlerThread등 안드로이드에서 제공하는 API는 많으니 활용하면 좋을것 같습니다.

안드로이드 API 12(4.0) 이상일때 아래와 다양한 API기능을 제공합니다.

CustomPolicy 

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

PenaltyDeathOnNetwork() 

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

PenaltyFlashScreen() 

정책이 위반 됐을 때 화면상으로 아래와 같은 빨간 테두리의 사각형이 splash 로 보여줘 인지하기가 수월해 집니다. 특정 작업을 하는 동안 빨간 테두리가 오래 나타나게 된다면 그 부분의 로직은 수정되어야 할 부분입니다.

VM policy 

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

 

이렇게 StrictMode는 개발자가 작업데 대한 예측이 어려운 부분을 알려줌으로써 사용자 반응성에 즉각적인 응답을 할 수 있는 개발에 도움을 줄 수있습니다. 선택사항이긴하나 앱의 퀄리티를 높이기위해 필수사항이라고 생각되니 꼭 사용 하길 권해드립니다.

다음 시간은 Main Thread에서 구동되는 UI를 좀더 빠르게 그려지기위해 레이아웃 구조를 최적화를 확인 하는 방법과 이를 바탕으로 어떻게 최적화 해야 하는지에 대한 방법에 대해 이야기해보겠습니다.

댓글 남기기