안드로이드 성능 이슈들에 대한 분석
이 튜토리얼은 안드로이에서 안드로이드 애플리케이션들의 성능 분석을 수행할 수 있는 가용한 툴에 대해서 설명하는 튜토리얼입니다.
목차
- 개요
- 안드로이드 기본
- StrictMode
- 개발자 세팅
- Lint
- Traceview
- 소개
- 안드로이드 스튜디오에서 Traceview사용하기
- 커맨드 라인에서 Traceview사용하기
- Traceview 연습 :
- 예제 프로젝트 생성
- 성능 추적
- 성능 문제 해결
- 상속계층 뷰
- 연습 : 상속계층뷰어
- 레이아웃 최적화
- 메모리 덤프
- Systrace
- 픽셀 density 시뮬레이션
- 안드로이드 템플릿
- GPU 랜더링 프로파일
- Overdraw 분석하기
- 애플리케이션 분석을 위한 유용한 오픈 소스 툴들
- Leak Canary 를 이용한 메모리 누수 찾기
- AndroidDevMetrics 를 사용한 성능 데이터 표출
- 웹사이트에 대해서
- Links and Literature
- 안드로이드 애니메이션 리소스들
- vogella GmbH training and consulting support
1. 개요
안드로이드 애플리케이션이 모든 오퍼레이션들 가능한 빠르게 처리하게 만드는 것은 매우 중요한 문제이다. 본 문서는 안드로이드 애플리케이션을 최적화하고 추적이 가능한 툴들을 설명하는 리스트입니다.
2. 안드로이드 기본
안드로이드 개발에 대한 기본적인 지식을 이미 갖고 있다는 전제에서 이문서는 출발합니다. 기본을 배우고 싶다면 다음 안드로이드 개발 튜토리얼 사이트를 참고 하십시요
3. StrictMode
UI 쓰레드 상에서 시간이 오래 걸리는 동작을 수행하는 것은 피해야 합니다. 이는 파일과 네트워크 접근 관련한 사항을 포함해서 말하는 것입니다.
StrictMode 를 사용 해야만 합니다. StrictMode 는 API9 (Android 2.3.3) 에서 사용가능하며 애플리케이션의 쓰레드 정책을 설정 할 수 있도록 해줍니다.
사용자 인터페이스 쓰레드 내의 I/O 같은 긴 수행 시간을 가지는 기능이 있다면, StrictMode 통해서 안드로이드 시스템이 해당 애플리케이션의 탈락 (혹은 충돌)을 지시 할 수 있다.
다음 코드는 StrictMode 사용하기 위한 예를 보여 준다. .액티비티는 해당 설정을 위반 하는 경우에 탈락(충돌) 시킵니다.
package de.vogella.android.strictmode;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import android.app.Activity;
import android.os.Bundle;
import android.os.StrictMode;
public class TestStrictMode extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Activate StrictMode
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder()
.detectAll()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
// alternatively .detectAll() for alldetectable problems
.penaltyLog()
.penaltyDeath()
.build()
);
StrictMode.setVmPolicy(
new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
// alternatively .detectAll() for alldetectable problems
.penaltyLog()
.penaltyDeath()
.build()
);
// Test code
setContentView(R.layout.main);
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
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();
}
}
}
StrictMode 는 개발 기간 중에 사용하는 것이지 실제 애플리케이션에서 사용하지 말아야 한다.
4. 개발자모드 세팅
안드로이드 폰에서 애플리케이션 분석을 단순화 할 수 있는 환경 설정이 가능하도록 개발자 모드 세팅(Developer Settings) 을 할 수 있습니다. 예를 들어 터치 영역이 활성화가 가능하도록 할 수 있습니다.
안드로이드 4.2 폰을 가지고 있다면 “Settings” 내의 “About”섹션으로 가서 “Build number” 엔트리를 7번 태핑 해 보도록 합니다.
만약 이 옵션(개발자 옵션)을 활성화 할 수 없으면 에뮬레이터를 사용하도록 합니다.
세팅이 적용되려면 애플리케이션을 재 시작 해야 하는 경우도 있습니다.
5. Lint
AndroidLint tutorial 을 참조 하도록 합니다.
6. Traceview
1. Introduction 소개
Traceview 는안드로이드 애플리케이션이 생성한 로그들을 보기 위한 그래픽 뷰어이다. 이 뷰어를 통해서 성능 문제를 일으키는 애플리케이션에 대한 성능을 측정할 수 있게 됩니다.
Traceview 는 안드로이드 SDK 설치폴더내에 tools 폴더 내에 있는 자체구동 툴이다.이 툴은 안드로이드 개발자 툴(ADT)를 통해서 이클립스와 통합되어 있습니다..
2. 안드로이드 스튜디오에서 Traceview 사용하기
안드로이드 스튜디오는 Android Device Monitor 를 통해서 추적을 지원합니다.
Tools → Android → AndroidDevice Monitor 순으로 선택 실행하여 사용하십시오
Devices뷰의 애플리케이션 프로세스들 중에서 추적할 애플리케이션을 선택하셔 추적 할 수 있으며, 아래 스크린샷에서 처럼 Start Method Profiling 을 선택 합니다.
애플리케이션을 사용하고 같은 버튼을 다시 누르면 프로파일링이 멈춥니다. 해당 추적 결과물을 보여 주는 새로운 에디터 창에 활성화 될 것입니다.
해당 그림을 확대하면 좀 더 세세한 내용을 볼 수 있습니다. 축소하기 위해서는 타임라인을 더블 클릭하면 됩니다.
3. 커맨드라인에서 TraceView사용하기
다음 코드 조각을 추적하기 위한 코드에 둘러 싸 주면 추적이 시작됩니다.
android.os.Debug.startMethodTracing("yourstring");
// ... your code is here
android.os.Debug.stopMethodTracing();
"yourstring" 파라메터는 "/sdcard/yourstring.trace" 디렉토리 안에 데이터를 저장해야만 한다는 것을 시스템에 알려주는 역할을 합니다. sdcard에 데이터를 저장하기 위해서는 WRITE_EXTERNAL_STORAGE 권한이 필요 합니다. 애플리케이션이 구동된 후에는 기기에서 나온 결과치를 adb 커맨드라인 툴 명령으로 복사 할 수 있습니다.
adb pull /sdcard/yourstring.trace
traceview yourstring
그래픽적인 요소를 통해서 애플리케이션 성능 데이터를 분석할 수 있도록 해주는 Traceview는 위의 명령을 통해서 수행이 가능합니다. DDMS 뷰에서 trace 버튼을 사용할 수 있습니다. 이렇게 해서 구동 중인 애플리케이션의 성능추적을 할 수 있으며, 추가적인 인증 작업은 없습니다.
7. 연습: Traceview
1. 예제 프로젝트 생성하기
com.vogella.android.traceview 라는 최상위 패키지 이름을 가진 안드로이드 애플리케이션을 생성합니다.
values/strings.xml 파일에 다음 키 값을 추가합니다.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Traceview Example</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
<string name="number_template"><b>Random number: %1$s</b></string>
</resources>
다음 rowlayout.xml 이름의 레이아웃 파일을 생성합니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Entry"
android:textAppearance="?android:attr/textAppearanceListItem" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stub"
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/imageView1"
android:layout_width="10dp"
android:layout_height="?android:attr/listPreferredItemHeight"
android:background="@android:color/black" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
ListView 에 다음 adapter 를 구현합니다.
package com.vogella.android.traceview;
import java.util.Collections;
import java.util.List;
import android.content.Context;
import android.content.res.Resources;
import android.os.Debug;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
public class MyArrayAdapter extends ArrayAdapter<String> {
private List<String> values;
private Context context;
public MyArrayAdapter(Context context, List<String> values) {
super(context, R.layout.rowlayout);
this.context = context;
this.values = values;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Debug.startMethodTracing("getViewOfTrace");
// Ensure sorted values
Collections.sort(values);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.rowlayout, parent, false);
Resources res = context.getResources();
String text = String.format(res.getString(R.string.number_template),
values.get(position));
CharSequence styledText = Html.fromHtml(text);
TextView textView = (TextView) view.findViewById(R.id.textView3);
textView.setText(styledText);
Debug.stopMethodTracing();
return view;
}
}
1000개의 랜덤 생성 문자열들을 보여주는 를 ListView 액티비티 내에 다음과 같이 구현합니다.
package com.vogella.android.traceview;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import android.app.ListActivity;
import android.os.Bundle;
public class MainActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
List<String> list = createValues();
MyArrayAdapter adapter = new MyArrayAdapter(this, list);
setListAdapter(adapter);
}
private static List<String> createValues() {
SecureRandom random = new SecureRandom();
List<String> list = new ArrayList<String>();
for (int i = 0; i < 1000; i++) {
String string = new BigInteger(130, random).toString(32);
list.add(string);
}
return list;
}
}
애플리케이션의 외장 스토리지 퍼미션을 추가해 줍니다.
2. Performtrace 성능 추적
애플리케이션을 구동합니다. 그런 다음 디바이스에 adb를 통해서 연결한 다음, 드라이브의 성능추적결과를 복사하고 이 결과를 분석 합니다.
3. Solveperformance problems 성능 문제 해결하기
Traceview 의 측정결과를 기반으로 성능을 향상 시킬 수 있습니다. 다음은 성능 향상을 위한 주 포인트를 설명입니다.
- getView() 내에 볼드 스타일을 레이아웃 내의 이에 상응하는 android:textStyle="bold 로 바꾸어Html.fromHtml()메서드 호출을 하지 않도록 합니다.
- 소팅 부분을 다른 곳을 옮깁니다.
- null 널이 아닌 경우, 어댑터의 convertView를 재사용 합니다.
- ListView 내에 HolderPattern을 사용하기 위한 findViewById() 메서드 호출을 피해야 합니다.
8. 상속 계층 뷰어
HierarchyView perspective 는 안드로이드 애플리케이션의 View 상속구조를 그래픽적으로 보여 줄 수 있으며 상속구조 상에서 필요없는 레이어들을 찾아 낼 수 있도록 도와 줍니다.
Window → Open Perspective → Other... → HierarchyView 순으로 선택해서 Hierarchy View 윈도우를 활성화 할 수 있습니다.
Windows 뷰내의 상속 뷰에서 분석하기를 원하는 프로세스를 선택 할 수 있습니다.
현재 활성화 된 레이아웃이 분석되고 화면에 표시 되어 있는 것을 확인 할 수 있습니다.
TreeView 뷰는 뷰들을 위해서3개의 전구 모양 아이콘을 보여 줍니다. 첫번째 아이콘은 뷰의 크기를 계산한 시간을 나타냅니다. 두번째 아이콘은 레이아웃을 생성한 시간을 나타내고 마지막 세번째 아이콘은 뷰를 그리는 시간을 나타냅니다. 성능 향상이 필요한 작업은 노란색이나 빨간 색으로 표시됩니다.
9. 연습 : Hierarchy Viewer
HierarchyView perspective 를 열고 뷰 레이어를 분석해 보도록 하겠습니다.
이 레이아웃 레이어들에 대해 성능적인 측면에 대한 고려는 할 필요가 없어 보이지만, 많은 레이어들과 필요없는 뷰들은 성능 향상이 매우 필요해 보입니다.
10. 레이어웃 최적화
레이어웃은 최적화 해야만 하는 과제 입니다. 다음은 다음에 설명할 결과 스크린샷을 생성할 ImageView 와 하나의 TextView 를 가진 FrameLayout 를 사용하는 방법을 나타냅니다. 레이아웃 내의 TextView 는 상대 위치를 사용하고 해당 텍스트 콘텐츠의 서로 다른 스타일을 사용합니다..
com.vogella.android.textview.spannablestring 이라는 프로젝트를 생성합니다.
styles.xml 파일에 두개의 새로운 스타일을 추가합니다.
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a API-level are here. -->
</style>
<style name="textHeader">
<item name="android:padding">4dip</item>
<item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
<item name="android:textColor">#000000</item>
<item name="android:fontFamily">sans-serif-condensed</item>
</style>
<style name="textbody">
<item name="android:padding">4dip</item>
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
<item name="android:textSize">16sp</item>
<item name="android:textColor">#c0c0c0</item>
</style>
</resources>
다음과 같이 레이아웃을 생성한다.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/FrameLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:id="@+id/input"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_margin="16dp"
android:text="@string/hello_world"
android:textSize="32sp" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="image"
android:src="@drawable/vogella" />
</FrameLayout>
아래 코드에서 보여주듯이 분리된 TextAppearanceSpan 을 통해서 TextView 문자 텍스트에 스타일을 적용한다.
package com.vogella.android.textview.spannablestring;
import android.app.Activity;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.TextAppearanceSpan;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.input);
String header = "This is the header";
String description = "This is the description";
Spannable styledText = new SpannableString(header + "\n" + description);
TextAppearanceSpan span1 = new TextAppearanceSpan(this,
R.style.textHeader);
TextAppearanceSpan span2 = new TextAppearanceSpan(this,
R.style.textbody);
styledText.setSpan(span1, 0, header.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
styledText.setSpan(span2,
header.length() + 1,
header.length() + 1 + description.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(styledText);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
결과 레이아웃은 RelativeLayout 기반의 레이아웃 보다 좀 더 빠른 레이아웃이다 또한 HTML 파서를 사용하는 것은 비교적 비싼 오퍼레이션이므로 HTML 스타일링은 피했다.
11. MemoryDumps 메모리 덤프
Eclipse Memory Analyzer를 사용해서 메모리 스냅샷을 생성하고 분석 할 수 있다.
12. Systrace
Systrace 는 커널 레벨에서 직접 성능을 측정하게 해준다. Systrace 를 활성화 하기 위해스는 개발자 옵션을 선택하고, Enable traces 엔트리를 선택해 준다. 그 다음 다이얼로그박스에서 어떤 타입의 이벤트를 프로파일 해야만 할지를 정의해야만 한다. (그래픽이나 뷰)
Systrace를 사용하기 위해서는, 터미널을 열고 android_sdk_installdir/tools/systrace 디렉토리에서 systrace.py 를 구동합니다. 이 파일에 실행 권한을 주어야 할 수도 있습니다.(리눅스에서는 chmod a+x systrace.py)
또, 이클립스의DDMS perspective 를 통해서 Systrace 를 직접 구동 할 수도 있습니다.
Systrace 는 5초 동안 이벤트를 캡쳐하고, 결과물로 Systrace 는 기본적인 문제들을 분석하게 해주는 HTML 파일을 생성합니다.
13. 픽셀density 시뮬레이션
상이한 기기 density와 스크린 리졸루션을 커맨드라인을 이용해서 사용 할 수 있습니다.
이렇게 되면 다른 기기를 시뮬레이션 하기 위한 높은 디스플레이 해상도와 기기 density를 가지도록 해줍니다.
// Set the display size
adb shell am display-size 600x800
// Set the display density
adb shell am display-density 80
14. 안드로이드 템플릿
안드로이드 프로젝트 생성 위저드를 위해서 자신만을 템플릿을 정의 할 수 있다. 다음의 링크에 좀 더 많을 정보를 얻을 수 있습니다.
Roman Nurik:
https://plus.google.com/113735310430199015092/posts/XTKTamk4As8
Official documentation:
https://dl.dropbox.com/u/231329/android/templatedocs/index.html
Additional code templates -
https://github.com/jgilfelt/android-adt-templates
15. 랜더링 프로파일
Setting 의 Developer options 내에 혹은 안드로이드 디바이스 에서 Profile GPU rendering을 할 수 있습니다. 이 옵션을 사용하면 마지막 128 프레임을 그리기 위해서 걸린 시간을 시스템이 추적 할 수 있도록 해줍니다.
활성화 하고 난 후 다음 명령을 통해서 앱을 재 구동 해 주면 정보를 얻을 수 있을 것입니다.
adb shell dumpsys gfxinfo your_package
|
애플리케이션의 리스트 뷰를 스크롤 하는 등의 프레임 율을 측정하기 위해서, 많은 경우에는 기기 자체를 새롭게 그리기 위한 트리거를 조정 해 줄 수 있어야만 합니다.
결과 로그 내에서 Profile data in ms 라는 섹션을 살펴 볼 수 있습니다.
16. Overdraw 분석하기
Overdraw 는 뭔가의 최상위에 뭔가를 그릴 때 발생한다. 예를 들어 activity 는 Window 를 백그라운드로 가진다. 만약 애플리케이션에 TextView 가 추가되어야 한다면 이 TextView 는 Window 위에 그려지게 됩니다.
그러므로 Overdrawn는 일어난다는 것입니다. 하지만 불필요한 overdraw는 최상의 성능을 얻기 위해서는 피해야만 합니다.
불필요한 overdraw는 복잡한 뷰 상속을 야기할 수도 있습니다. 일반적으로 두 배 overdraw(픽셀이 세번 그려지는 것)는 표준이며 예상 할 수 있는 결과 이나 그 이상은 피해야만 합니다.
DevelopmentSettings 의 Show GPU overdraw 세팅 Overdraw의 표현이 가능하게 할 수 있습니다. 이 섹션은 . overdraw들의 개수를 기반인 스크린에 색깔을 추가 할 수 있습니다. 다음 테이블은 사용되어지는 색상 스키마를 설명 합니다.
Table 1. Overdrawncolors
Color
|
Meaning
|
No color
|
No overdraw
|
Blue
|
1x overdraw, pixel was painted twice
|
Green
|
2x
|
Light red
|
3x, might indicate a problem, small red areas are still ok
|
Dark red
|
4x, pixel was pained 5 times or more, indicates a problem.
|
기본 문제영역의 표현 후에, Hierarchy Viewer 의 View 상속구조를 분석 할 수 있습니다.
17. 애플리케이션 분석을 위한 유용한 오픈소스 툴
1. 를 사용한 메모리 누수 찾기
Leak Canary 는 안드로이드 애플리케이션에 통합 되어질 수 있으며 자동으로 액티비티들이나 프레그먼트들의 메모리 릭을 찾아 낼 수 있습니다. 사용법은 매우 단단하며, Gradle 빌드 파일에 dependency 를 추가 해주고 애플리케이션 클래스에서 라이브러리를 초기화 해줍니다. 이렇게 하면 액티비티들에 대한 생명주기 리스너들을 등록하게 되고 적절한 시점에 destroy 메서드를 호출하면 추적되게 됩니다.
Leak Canary은 오픈소스 프로젝트이며 아주 빨리 변화하는 측면이 강한 프로젝트입니다.
어떻게 사용하는 지에 대한 가이드는 LeakCanarys Github page를 보십시오.
2. 성능데이터를 표출하기 위한 AndroidDevMetrics 사용하기
AndroidDevMetrics는 오브젝트 초기화나 액티비티 생명주기 메서드들(onCreate(), onStart(), onResume()) 생성 등 가장 일반적인 동작이 얼만큼 빨리 일어나는 지 볼 수 있게 해주거나, (in Dagger 2 graph)
AndroidDevMetrics도 오픈소스 프로젝트이며, 어떻게 사용하는 지에 대한 가이드는 AndroidDevMetrics Github page를 활용 하십시오.
19. Linksand Literature
1. AndroidAnimation Resources
Traveview homepage
이상.
'모바일프로그래밍 > 안드로이드' 카테고리의 다른 글
커맨드 창과 에디터로 안드로이드 프로그래밍 해보기 (1) | 2023.03.02 |
---|---|
[안드로이드] 액티비티 테스트 자습서 (0) | 2023.02.19 |
[안드로이드]dex 파일 65535 크기 넘어가는 오류 잡아 보기 (0) | 2023.01.21 |
[안드로이드&자바]자바와 안드로이드 자바 간 비교 (0) | 2023.01.15 |
[ProGuard] 안드로이드 애플리케이션의 최적화, 난독화 및 최소화 (0) | 2023.01.14 |