안드로이드 애플리케이션 패키지(apk)를 설치하기 위해서는 반드시 서명(signing)을 해야합니다. 
서명되지 않은 애플리케이션을 설치하려고 하면 아래처럼 [INSTALL_PARSE_FAILED_NO_CERTIFICATES] 에러가 발생하게 됩니다.

$>adb install ActivityLifecycleProject.apk
178 KB/s (8585 bytes in 0.046s)
        pkg: /data/local/tmp/ActivityLifecycleProject.apk
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]

애플리케이션 서명 여부 확인하기
그럼 어떤 애플리케이션이 서명되었는지 여부는 어떻게 알 수 있을까요? 우선은 JDK에 포함된 jarsigner 명령을 통해서 확인할 수 있습니다.

## 서명안된 apk
$>jarsigner -verify ActivityLifecycleProject.apk
no manifest.
jar is unsigned. (signatures missing or not parsable)


## 서명된 apk
$>jarsigner -verify ActivityLifecycleProject.apk
jar verified.

(좀더 자세한 서명 정보는 -verfiy -verbose -certs 옵션을 추가하면 볼 수 있습니다.)

또는 간단히 apk 파일을 알집 등으로 풀어서 META-INF 폴더 안에 CERT.SF 와 같은 파일이 있으면 서명된 패키지로 볼 수 있습니다. 서명되지 않은 apk에는 이러한 파일이 없습니다.



이클립스를 통한 자동 디버그 모드 서명
앞서 애플리케이션은 반드시 서명되어야 한다고 했습니다. 그런데 우리는 이클립스(+ADT) 환경에서 안드로이드 애플리케이션을 개발하면서, 서명 작업을 의식하지 않고 자연스럽게 타겟에 애플리케이션을 설치하고 테스트합니다. 이렇게 개발자가 직접 서명을 하지 않아도 되었던 이유는 이클립스가 애플리케이션을 빌드하면서 자동으로 apk에 서명까지 함께 해주었기 때문입니다.

apk 서명에는 debug 모드와 release 모드가 있는데, 이클립스는 debug 모드로 서명을 해줍니다. 이클립스가 apk를 디버그 모드로 서명하기 위한 키/인증서(key/certificate)는 "$HOME/.android/debug.keystore" 파일에 저장되어 있습니다. 'debug.keystore' 파일은 이클립스가 필요시 자동으로 생성해줍니다. 이클립스 Preferences 창의 Android > Build 메뉴를 보면, 이 파일이 기본 디버그 키저장소(Default debug keystore) 파일로 설정되어 있는 것을 확인할 수 있습니다.



네이티브 애플리케이션은 좀 다르다
그럼 네이티브 애플리케이션도 이클립스가 자동생성한 debug.keystore 파일로 서명해서 설치가 될까요? 일단 debug.keystore 파일로 서명된 네이티브 Calculator 애플리케이션을 설치해보죠. 

D:\Android\Calculator\bin>adb install Calculator.apk
789 KB/s (88433 bytes in 0.109s)
        pkg: /data/local/tmp/Calculator.apk
Failure [INSTALL_FAILED_ALREADY_EXISTS]
 
이미 동일한 패키지가 존재한다며 에러가 떨어집니다. -r 옵션을 추가해서 재설치를 시도해봅니다.

D:\Android\Calculator\bin>adb install -r Calculator.apk
425 KB/s (88433 bytes in 0.203s)
        pkg: /data/local/tmp/Calculator.apk
Failure [INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES]

일치하지 않는 인증서 에러가 발생하면서 설치되지 않네요. 그러면 우리가 원래 하던대로 이클립스에서 [Run As > Android Application]을 실행해보겠습니다.

[2010-04-02 16:54:10 - Calculator]Android Launch!
[2010-04-02 16:54:10 - Calculator]adb is running normally.
[2010-04-02 16:54:10 - Calculator]Performing com.android.calculator2.Calculator activity launch
[2010-04-02 16:54:10 - Calculator]Automatic Target Mode: using existing emulator 'emulator-5554' running compatible AVD 'myavd'
[2010-04-02 16:54:10 - Calculator]WARNING: Application does not specify an API level requirement!
[2010-04-02 16:54:10 - Calculator]Device API version is 7 (Android 2.1)
[2010-04-02 16:54:10 - Calculator]Uploading Calculator.apk onto device 'emulator-5554'
[2010-04-02 16:54:10 - Calculator]Installing Calculator.apk...
[2010-04-02 16:54:13 - Calculator]Re-installation failed due to different application signatures.
[2010-04-02 16:54:13 - Calculator]You must perform a full uninstall of the application. WARNING: This will remove the application data!
[2010-04-02 16:54:13 - Calculator]Please execute 'adb uninstall com.android.calculator2' in a shell.
[2010-04-02 16:54:13 - Calculator]Launch canceled!

역시 애플리케이션 시그너쳐가 다르다며 재설치가 실패합니다. 이로부터 우리는 타겟에 내장되어 있는 네이티브 애플리케이션은 이클립스가 생성하는 debug.keystore를 통해 서명된 것이 아니라 다른 인증서로 서명되었음을 알 수 있습니다. 

네이티브 애플리케이션 서명용 테스트 키
MYDROID/build/target/product/security/ 에는 시스템 패키지(네이티브 애플리케이션)들을 '디버그 모드'로 서명하기 위한 4가지의 준비된 표준 테스트 키가 있습니다. 이것들은 개발 단계에서 사용되며, 에뮬레이터에 내장된 네이티브 애플리케이션은 안드로이드 소스 full 빌드 중에 이 중 한가지 키로 서명이 됩니다.

- media.pk8 , media.x509.pem
- platform.pk8 , platform.x509.pem
- shared.pk8 , shared.x509.pem
- testkey.pk8 , testkey.x509.pem

4가지 키에 대한 간략한 설명은 다음과 같습니다(README 파일 참조).
  • testkey -- a generic key for packages that do not otherwise specify a key.
  • platform -- a test key for packages that are part of the core platform.
  • shared -- a test key for things that are shared in the home/contacts process.
  • media -- a test key for packages that are part of the media/download system.

네이티브 애플리케이션이 어떤 테스트 키로 서명되는지는 해당 애플리케이션 소스 루트의 Android.mk를 보면 알 수 있습니다. 네이티브 Contacts 애플리케이션의 Android.mk 파일을 보면 LOCAL_CERTIFICATE := shared 로 설정돼있습니다. 즉 shared 테스트 키로 서명을 한다는 것이죠.

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := user
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := googlelogin-client
LOCAL_PACKAGE_NAME := Contacts
LOCAL_CERTIFICATE := shared
include $(BUILD_PACKAGE)
# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))

Calculator의 Android.mk를 보면 LOCAL_CERTIFICATE 변수가 정의돼 있지 않은데, 그러면 기본적으로 testkey 키로 서명하게 됩니다.

네이티브 애플리케이션 자동 서명용 디버그 키저장소 만들기
에뮬레이터에 내장된 네이티브 애플리케이션이 어떤 키/인증서로 서명되었는지 알았으니, 커스텀 디버그 키저장소(Custom debug keystore) 파일을 만들고, 이것을 이클립스에 등록하여 이클립스가 자동으로 네이티브 애플리케이션에 서명하도록 할 수 있습니다.
키저장소를 만들기 전에 알아둘 사항이 있는데, 생성된 java keystore 파일을 이클립스에서 사용하기 위해서는 반드시 alias는 androiddebugkey, password는 android이어야 한다는 겁니다. 그리고 openssl 과 JDK(1.6 권장)가 설치돼 있어야 합니다.
자 그럼, testkey.pk8 및 testkey.x509.pem 파일로부터 testkey.jks 파일을 만들어봅시다. 

  • 키 파일 : testkey.pk8
  • 인증서 파일 : testkey.x509.pem
  • 키 알리아스 : androiddebugkey
  • 키 패스워드 : android
  • 키저장소 패스워드 : android
  • 생성할 키저장소 파일 : testkey.jks

1. PK8 유형의 키 파일을 PEM 유형 키로 변환

$ openssl pkcs8 -inform DER -nocrypt -in testkey.pk8 -out testkey.pem

2. 키와 인증서를 포함하는 PKCS#12 포맷의 저장소 생성

$ openssl pkcs12 -export -in testkey.x509.pem -inkey testkey.pem -out testkey.p12 -password pass:android
-name androiddebugkey

3. PKCS#12 포맷 저장소 파일을 자바 키저장소 포맷으로 변환
(1) JDK 1.5인 경우 jetty 라이브러리에 포함된 PKCS12Import 클래스를 이용

$ java -classpath jetty-core-6.1.14.jar org.mortbay.jetty.security.PKCS12Import testkey.p12 testkey.jks 

(2) JDK 1.6인 경우 JDK에서 제공하는 keytool을 이용

$ keytool -importkeystore -deststorepass android -destkeystore testkey.jks -srckeystore testkey.p12
-srcstoretype PKCS12 -srcstorepass android

* 참고로 위의 1 ~ 3까지의 작업을 단순화시킨 keytool-importkeypair라는 스크립트가 있습니다. 아래와 같이 사용합니다

$ keytool-importkeypair -k testkey.jks -p android -pk8 testkey.pk8 -cert testkey.x509.pem -alias androiddebugkey

드디어 네이티브 애플리케이션을 이클립스를 통해 자동으로 디버그 모드 서명하기 위한 키저장소 파일을 만들었습니다.

이클립스에 커스텀 디버그 키저장소 파일 설정하기
생성한 키저장소 파일 testkey.jks를 적당한 곳에 두고 이클립스 Preferences 창의 Android > Build 메뉴에서 커스텀 디버그 키저장소(Custom debug keystore) 필드에 설정해줍니다.



OK 버튼을 누르고 아까 설치 실패했던 Calculator 프로젝트에 대해서 [Run As > Android Application]을 다시 실행합니다.

[2010-03-29 13:30:31 - Calculator]Uploading Calculator.apk onto device 'emulator-5554'
[2010-03-29 13:30:32 - Calculator]Installing Calculator.apk...
[2010-03-29 13:30:36 - Calculator]Success!
[2010-03-29 13:30:36 - Calculator]Starting activity com.android.calculator2.Calculator on device
[2010-03-29 13:30:41 - Calculator]ActivityManager: Starting: Intent { cmp=com.android.calculator2/.Calculator }

성공이네요. 이제 우리는 (이클립스 환경에서) 네이티브 애플리케이션도 평범한 유저 애플리케이션처럼 서명 작업을 의식하지 않고, Run As 또는 Debug As > Android Application을 실행하면서 자연스럽게 개발/디버깅/테스트할 수 있게 되었습니다.

Posted by 에코지오
,


네이티브 애플리케이션(Native Application)

  • 내장 애플리케이션(Built-in Application) 또는 시스템 애플리케이션(System Application) 등으로 불림
  • 주로 제조사에서 개발되어, 디바이스(or 에뮬레이터)에 내장된 상태로 제공됨
  • Contacts, Browser, Phone, Camera, Email, Gallery 등(전체 목록은 안드로이드 풀소스의 packages 디렉토리 참조)
  • 디바이스의 /system/app에 설치됨 : ex) /system/app/Contacts.apk
  • 보통 안드로이드 full 소스 빌드시 함께 빌드되며 이 과정에서 별도의 인증서로 서명됨
  • 네이티브 애플리케이션을 디버그 모드로 서명하기 위한 인증서들은 MYDROID/build/target/product/security에 있음


유저 애플리케이션(User Application)

  • 일반적으로 이클립스 IDE 환경에서 새로운 Android Application 프로젝트로 개발되는 애플리케이션들임
  • 사용자에 의해 다운로드되어 설치됨
  • 디바이스의 /data/app에 [패키지명].apk 형태로 설치됨 : ex) /data/app/com.mycompany.myapp.apk
  • 대개 개발/테스트 단계에서는 이클립스 ADT플러그인이 생성해주는 Default Debug Keystore로 서명됨
  • Default Debug Keystore 파일 : USERHOME/.android/debug.keystore
Posted by 에코지오
,

애플리케이션에서 사용하는 SQLite DB의 table 구조나 data를 확인하는 3가지 방법을 정리해봅니다.

1. sqlite 프롬프트 이용
명령어 날리는게 귀찮지만 sqlite 활용의 기본이니 알아두면 좋습니다. 이 방법은 직접 디바이스 내부 DB에 접속하여 작업 가능하므로 DB 파일을 로컬로 복사할 필요가 없습니다.

자세한 이용 방법은 커니님의 글 참조 : http://androidhuman.tistory.com/entry/테이터베이스-그-속이-궁금하다

2. SQLite 클라이언트 툴 이용
SQLite Database Browser 같은 SQLite DB 전용 클라이언트 소프트웨어를 이용하여 DB 내용을 볼 수 있습니다.

(1) 클라이언트 SW 설치
- SQLite Database Browser : UI가 수수함. 주로 데이터 조회 기능 제공.
- SQLite Developer Lite : UI가 세련됨. 테이블/인덱스 외에 뷰/트리거도 조회. 테이블 관리 등 여러가지 기능 제공.

(2) DB 파일 꺼내기
디바이스 or 에뮬레이터의 내부 DB 파일(*.db)을 로컬로 꺼냅니다. 예를들어 Contacts Provider가 사용하는 DB파일을 C:\로 복사해보죠.

$ adb pull /data/data/com.android.providers.contacts/databases/contacts2.db C:/contacts2.db

(3) 꺼낸 DB 파일을 클라이언트 툴로 오픈
SQLite 클라이언트 툴로 오픈하여 DB 구조와 데이터를 확인합니다.





* 타겟 시스템의 바뀐 DB 내용을 확인하고자 한다면, DB 파일 꺼내는 작업을 반복해서 해주어야 하므로 DDMS의 File Explorer보다는 파일 꺼내는 명령을 배치파일로 만들어두고 실행하는 것이 편리합니다. 보통은 DB 파일을 꺼내고 툴로 오픈하는 명령을 하나의 배치파일로 만들어두고 활용합니다.

3. 이클립스 DB 클라이언트 플러그인 이용
SQLite 클라이언트 툴을 설치하는게 귀찮거나 이클립스 외에 다른 프로그램을 띄우는게 싫다면, 이클립스 안에서 SQLite DB를 볼 수 있게 세팅해주면 되겠습니다. 개인적으로 이 방법을 선호합니다.(그러나 이런 이클립스 플러그인들은 JDBC를 이용하기 때문에 전용 SW보다는 DB 관리 기능이 부족합니다.)

(1) DB 클라이언트 플러그인 설치
SQL Explorer나 Quantum DB 같은 DB 클라이언트 플러그인을 설치합니다.

(2) JDBC 연결 세팅
JDBC 드라이버 : http://www.zentus.com/sqlitejdbc/ 에서 구할 수 있습니다.
JDBC URL 형식 : jdbc:sqlite:/[로컬 DB파일 경로] (예를들어, jdbc:sqlite:/C:/contacts.db )

(3) DB 플러그인에서 DB 연결하여 내용조회
DB 파일을 JDBC URL에 설정된 고정된 경로로 꺼내고, DB 플러그인에서 DB에 연결하여 내용을 확인합니다.



* 2번 방법과 마찬가지로 내부 DB 변경시마다 로컬로 DB파일 꺼내는 작업을 반복해야 하므로, 파일 꺼내는 배치파일 or 명령을 이클립스 외부프로그램(External Tools)으로 등록시켜서 실행하면 편리합니다.



참고 : SQLite 데이터베이스의 DDL 쿼리 덤프 뜨기
sqlite에서 제공하는 .dump 명령어를 사용합니다. (table/index/trigger CREATE 문과 data INSERT 문이 출력됨)

$ sqlite3  C:/contacts.db  .dump > C:/contacts.ddl
Posted by 에코지오
,

안드로이드 시스템 분석에 사용할만한 shell 명령을 알아보자.

시스템 기본 정보: 하드웨어, 커널 등

cat /proc/version : 커널 버전
cat /proc/cpuinfo : 프로세서 정보. CPU 타입, 모델, 제조사 등
cat /proc/meminfo : 메모리 정보. 실제 메모리 및 가상 메모리
cat /proc/devices : 현재 커널에 설정되어 있는 장치 목록
mount : 마운트된 모든 장치 정보
df : 하드디스크 사용량
cat /proc/filesystems : 커널에 설정되어 있는 파일시스템 목록
cat /proc/swaps : 스왑 파티션의 크기와 사용량
cat /proc/interrupts : 장치가 사용중인 인터럽트(IRQ) 목록 표시
cat /proc/ioports : 현재 사용중인 Input/Output 포트
cat /proc/loadavg : 시스템의 평균부하량
cat /proc/partitions : 파티션 정보
cat /proc/uptime : 시스템이 얼마나 살아있었는지.
cat /proc/stat : 시스템 상태에 관한 다양한 정보. CPU 사용통계, 부팅이후 page fault 발생횟수 등
cat /proc/zoneinfo : ZONEINFO ?
dmesg : 시스템 부팅시 나왔던 메시지
ps : 실행중인 프로세스 정보
ps -p -t : 프로세스와 쓰레드 목록
set 또는 printenv : 환경설정값 출력

시스템 리소스 사용 현황

vmstat : 시스템의 리소스 상황 모니터링. CPU, I/O, 메모리 등
cat /proc/diskstats : 디스크 utilization과 throuthput. 즉 디스크 IO 현황
top : 시스템의 프로세스 상황 모니터링. 프로세스별 CPU 사용량, 메모리와 스왑 사용량 등
procrank : 프로세스별 메모리(VSS,RSS,USS, PSS)
dumpsys meminfo [PID] : 해당 프로세스의 메모리 상세 정보
cat /proc/[PID]/stat : 해당 프로세스에 대한 정보. 시작시간, 상태, CPU 사용량 등
cat /proc/[PID]/maps : 해당 프로세스의 메모리 맵 정보
cat /proc/vmstat : 버추얼 메모리 통계?
librank : 라이브러리별 메모리 사용량?

네트워크 관련

cat /proc/net/netlink : 네트워크 정보
netcfg : 네트워크 인터페이스와 IP주소 목록
netstat : 네트워크 연결상태 확인
nc : 네트워크용 cat 명령어(netcat)
ifconfig : 네트워크 인터페이스 설정 정보. 장치명을 파라미터로 받음. IP 주소, 서브넷마스크 등
tcpdump : 실시간 패킷 모니터링
iftop : 네트워크를 위한 top
route : 해당 호스트까지 연결하는 중간 경로 정보인 라우팅 테이블 표시
ping : 원격 호스트와의 연결 테스트
cat /proc/net/route : Routes

안드로이드 제공

logcat : 로그캣 보기
pm : package manager의 약자. 패키지/permission/instrumentation/feature 목록, 패키지 설치/제거 등
am : activity manager의 약자. 액티비티 시작, Intent 브로드캐스팅, Instrumentation 시작, profiling 시작/중지 등
service : 안드로이드 서비스 목록 표시, 서비스에 명령 전달
monkey : 애플리케이션에 랜덤 이벤트 발생시킴. 사용자 이벤트, 시스템 이벤트의 무작위 발행
cat /data/anr/traces.txt : VM TRACES (쓰레드 덤프)
cat /proc/binder/proc/[PID] : 바인더 프로세스 상태
cat /proc/binder/xxx : 바인더 관련 정보(xxx는 transactions, transaction_log, failed_transaction_log, stats 등)
cat /data/system/packages.xml : 설치된 패키지 세팅 정보
setprop : system property 세팅
getprop : 세팅된 system property 목록 출력


종합 리포트
dumpsys [service]: app/service 상태정보 덤프. 서비스별로 추가 파라미터 받을 수 있음
dumpstate : device 상태정보 덤프(cpu,mem,ps 등). 상태정보를 추출하는 여러 명령어들의 조합으로 구성
dumpcrash : 애플리케이션이 crash될 때의 상태정보 덤프?
bugreport : logcat+dumpsys+dumpstate

그밖에...
그밖의 안드로이드 shell 명령어는 /system/bin 및 /system/xbin을 뒤져보면 많이 나온다. 이제 남은 일은 찾아낸 명령어의 사용법, 출력결과를 어떻게 해석할지, 어떤 상황에서 이들을 활용할지 사례조사, 그리고 직접 활용해보는 것일게다.


* 참조

http://www.cyworld.com/polox94ii/312644

http://tkhwang.pe.kr/archives/65

http://en.androidwiki.com/wiki/ADB_Shell_Command_Reference

http://elinux.org/Using_Bootchart_on_Android

http://elenoa.tistory.com/52



Posted by 에코지오
,

안드로이드에서 프로세스가 도대체 얼만큼의 메모리를 사용하고 있는지 분석해본다.

시스템 메모리 사용 현황

우선 전체 시스템의 메모리부터 파악하자. $> adb shell 로 접속한 후 /proc/meminfo를 열어본다.

# cat /proc/meminfo

MemTotal:          94172 kB
MemFree:            2136 kB
Buffers:              12 kB
Cached:            46380 kB
SwapCached:            0 kB
Active:            36868 kB
Inactive:          46140 kB
Active(anon):      18548 kB
Inactive(anon):    19584 kB
Active(file):      18320 kB
Inactive(file):    26556 kB
Unevictable:         264 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:         36892 kB
Mapped:            29344 kB
Slab:               2952 kB
SReclaimable:        740 kB
SUnreclaim:         2212 kB
PageTables:         3176 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:       47084 kB
Committed_AS:     914824 kB
VmallocTotal:     876544 kB
VmallocUsed:       11376 kB
VmallocChunk:     863236 kB

MemTotal, MemFree 같은 필드는 대충 감이 잡히는데, 모르는 항목이 많이 보인다. 넘어가자. -.-;;

VSS와 RSS
이론적으로(?) 프로세스가 차지하는 정확한 메모리의 크기를 알 수는 없다고 한다. 다만, 프로세스에 매핑되는 page 수를 해석하는 다양한 방법이 있는데, VSS, RSS, USS, PSS 등이 그것이다.

  • VSS(Virtual Set Size) : 프로세스와 관련된 버추얼 메모리(virtual memory) 크기. 메모리 맵(나도 자세한 건 모른다)이 1M이면 프로세스가 어떤 리소스도 사용하지 않아도 VSS는 1MB가 된다. 의미있는 수치라고 볼 수 없음.
  • RSS(Resident Set Size) : 프로세스와 관련된 물리적 페이지(physical pages) 수. 여러 프로세스 사이에서 공유된 페이지(shared pages) 수를 확인할 수 없어 별 의미없음. A프로세스의 RSS가 2MB, B프로세스의 RSS가 2MB일 때 실제 물리 페이지 수는 4MB이거나 2MB일 수 있다.

어쨌거나 프로세스별 VSS와 RSS 값은 top 명령어로 구할 수 있다.

# top

User 1%, System 6%, IOW 0%, IRQ 0%
User 6 + Nice 0 + Sys 20 + Idle 293 + IOW 0 + IRQ 0 + SIRQ 0 = 319

  PID CPU% S  #THR     VSS     RSS PCY UID      Name
  205   4% R     1    888K    368K  fg root     top
   54   1% S    40 149592K  31644K  fg system   system_server
   99   0% S    17 117416K  22028K  fg radio    com.android.phone
    4   0% S     1      0K      0K  fg root     events/0
    5   0% S     1      0K      0K  fg root     khelper
    6   0% S     1      0K      0K  fg root     suspend
    7   0% S     1      0K      0K  fg root     kblockd/0
    8   0% S     1      0K      0K  fg root     cqueue
    9   0% S     1      0K      0K  fg root     kseriod
   10   0% S     1      0K      0K  fg root     kmmcd
   11   0% S     1      0K      0K  fg root     pdflush
   12   0% S     1      0K      0K  fg root     pdflush
    1   0% S     1    296K    204K  fg root     /init
   14   0% S     1      0K      0K  fg root     aio/0
   21   0% S     1      0K      0K  fg root     mtdblockd
   22   0% S     1      0K      0K  fg root     hid_compat
   23   0% S     1      0K      0K  fg root     rpciod/0
   24   0% S     1      0K      0K  fg root     mmcqd
   25   0% S     1    728K    308K  fg root     /system/bin/sh
   26   0% S     1    796K    260K  fg system   /system/bin/servicemanager
   27   0% S     1    832K    384K  fg root     /system/bin/vold
   28   0% S     1    656K    248K  fg root     /system/bin/debuggerd
   29   0% S     4   5420K    728K  fg radio    /system/bin/rild
   30   0% S     1  81428K  25528K  fg root     zygote
   31   0% S     6  20944K   3332K  fg media    /system/bin/mediaserver
   32   0% S     1    784K    284K  fg root     /system/bin/installd
   33   0% S     1   1616K    404K  fg keystore /system/bin/keystore
   34   0% S     1    728K    324K  fg root     /system/bin/sh
   35   0% S     1    824K    336K  fg root     /system/bin/qemud
   37   0% S     4   3372K    184K  fg root     /sbin/adbd
   46   0% S     1    780K    308K  fg root     /system/bin/qemu-props
   94   0% S     6 109168K  18604K  bg system   com.android.settings
   97   0% S     7 107548K  18784K  fg app_4    com.android.inputmethod.latin
  102   0% S    12 133720K  28496K  fg app_4    android.process.acore
  132   0% S     6 102392K  18260K  bg app_6    com.android.alarmclock
  144   0% S     7 103136K  18908K  bg app_1    android.process.media
  165   0% S     6 114060K  19036K  bg app_12   com.android.mms
  183   0% S     7 105492K  19592K  bg app_21   com.android.email
  197   0% S     1    728K    324K  fg root     /system/bin/sh
   13   0% S     1      0K      0K  fg root     kswapd0
    2   0% S     1      0K      0K  fg root     kthreadd
    3   0% S     1      0K      0K  fg root     ksoftirqd/0

USS와 PSS

VSS나 RSS보다 조금 더 의미있는 수치는 USS와 PSS인데, procrank 명령어로 구할 수 있다.

  • USS(Unique Set Size) : 프로세스만의 고유한 페이지 수. 공유되지 않는 프로세스에 private한 메모리 크기이다.
  • PSS(Proportional Set Size) : USS + (공유 페이지 / 공유하는 프로세스 수). 즉, 프로세스 고유 메모리 사용량 + 하나의 프로세스가 차지하는 공유 메모리 비율이다. 만약 A프로세스가 6MB 메모리를 사용하고 그 중 2MB가 그 프로세스의 고유 영역이라면, 나머지 4MB는 공유 메모리이다. 4MB의 공유메모리를 4개의 프로세스가 공유하고 있다면 PSS는 2MB + (4MB/4) = 3MB가 된다.

PSS는 공유되는 페이지를 공유 프로세스의 수로 나누어서 좀더 정확한 메모리 사용량을 파악할 수 있게 해준다. 이게 프로세스가 사용하는 실제 메모리 크기에 가장 근접한 값이라고 볼 수 있다.

# procrank

  PID      Vss      Rss      Pss      Uss  cmdline
   54   32244K   31644K   14353K   10860K  system_server
  102   28496K   28496K   10835K    7164K  android.process.acore
   30   25528K   25528K    8585K    5636K  zygote
   99   22028K   22028K    6146K    3604K  com.android.phone
  183   19592K   19592K    4461K    2480K  com.android.email
  165   19036K   19036K    4122K    2136K  com.android.mms
  144   18908K   18908K    3953K    2148K  android.process.media
   94   18604K   18604K    3868K    1780K  com.android.settings
   97   18784K   18784K    3734K    1712K  com.android.inputmethod.latin
  132   18260K   18260K    3352K    1508K  com.android.alarmclock
   31    3332K    3332K    1002K     628K  /system/bin/mediaserver
  203     456K     456K     269K     260K  procrank
   29     728K     728K     258K     220K  /system/bin/rild
    1     204K     204K     185K     184K  /init
   37     184K     184K     169K     168K  /sbin/adbd
   27     384K     384K     156K     144K  /system/bin/vold
   35     336K     336K     134K     124K  /system/bin/qemud
   33     404K     404K     107K      88K  /system/bin/keystore
   34     324K     324K     102K      68K  /system/bin/sh
  197     324K     324K     102K      68K  /system/bin/sh
   25     308K     308K      96K      68K  /system/bin/sh
   46     308K     308K      95K      84K  /system/bin/qemu-props
   32     284K     284K      93K      84K  /system/bin/installd
   26     260K     260K      92K      84K  /system/bin/servicemanager
   28     248K     248K      84K      76K  /system/bin/debuggerd

프로세스별 PSS 수치는 DDMS의 Sysinfo 탭을 통해서도 볼 수 있다. 파이그래프로 비주얼하게 보여주므로, 메모리 사용 비율을 쉽게 파악할 수 있다.




조금 더 상세한 메모리 정보
top과 procrank를 통해 애플리케이션의 개략적인 메모리 사용량을 알수 있었다면, dumpsys meminfo <PID(프로세스 ID)> 명령어로 약간 더 상세한 메모리 사용정보를 구할 수 있다. dumpsys meminfo 명령어는 프로세스가 사용하는 native(C/C++)와 dalvik(JAVA) 영역을 구분하여 보여주므로 달빅 VM 만의 메모리 크기를 알 수 있다.

# dumpsys meminfo 183
Currently running services:
  meminfo
----------------------------------------------------------
DUMP OF SERVICE meminfo:
Applications Memory Usage (kB):
Uptime: 3793754 Realtime: 3793754

** MEMINFO in pid 183 [com.android.email] **
                    native   dalvik    other    total
            size:     3436     3139      N/A     6575
       allocated:     3425     2639      N/A     6064
            free:       10      500      N/A      510
           (Pss):      824     1893     1461     4178
  (shared dirty):     1888     4916      972     7776
    (priv dirty):      620      540      552     1712

 Objects
           Views:        0        ViewRoots:        0
     AppContexts:        2       Activities:        0
          Assets:        2    AssetManagers:        2
   Local Binders:        4    Proxy Binders:        8
Death Recipients:        0
 OpenSSL Sockets:        0

 SQL
            heap:       56          dbFiles:        0
       numPagers:        3   inactivePageKB:       13
    activePageKB:        0

 

  • Pss 필드 : procrank에 나온 PSS 값과 동일
  • shared dirty : 다른 프로세스와 공유하는 dirty pages(디스크로부터 페이징 불가능?)
  • private dirty : 프로세스 고유의 dirty pages

참고로 위의 Objects와 SQL 섹션에는 View/Context/Activity 등의 개수와 SQL 페이지 크기 등 중요한 정보를 제공하므로 활용가치가 높다.

dumpsys meminfo 출력 결과에 나온 dalvik 컬럼의 size, allocated, free 필드값은 DDMS의 VM Heap 탭을 통해서도 확인할 수 있다. 각각 VM Heap 탭의 Heap Size, Allocated, Free 컬럼에 해당한다. DDMS의 VM Heap 탭의 정보는 달빅 VM만의 메모리 정보일 뿐, 애플리케이션의 native 라이브러리가 사용하는 메모리 정보는 제외되었다.




힙 메모리 분석
애플리케이션 프로세스가 차지하는 메모리 크기는 이 정도면 파악되었고, 힙 메모리 안에서 자바 객체 레벨의 메모리 사용량을 분석하려면 힙덤프를 떠서 분석해야한다. 이건 다음 기회에... ^^;;


* 참조

http://tinysense.textcube.com/11

Posted by 에코지오
,
Doxygen 은 소스코드 문서 생성툴이다. java, c++, python 등 여러 언어를 지원한다. 안드로이드 PDK 사이트를 가보면 일부 네이티브 C/C++ 소스에 대한 javadoc 스타일의 API 문서를 볼 수 있다. 예를 들어 CameraHardwareInterface.h. 이 문서가 바로 doxygen이라는 툴로 생성된 것이다. 

나 처럼 주로 자바로만 먹고살아서 C/C++ 소스 분석에 애를 먹는 사람들한테는, 이 API 문서가 조금이나마 도움이 될 수가 있다. 이렇게 예쁘게 다이어그램도 그려주고 말이다.

android-doxygen


그러나 안드로이드는 아쉽게도 전체 C/C++ 소스에 대한 API 문서는 제공하지 않는다. 이유는 잘 모르겠다.
목마른 놈이 우물판다고 그래서 내가 직접 framework에 속한 native 소스에 대해 doxygen을 돌려봤다. 다행히 큰 문제 없이 문서가 잘 만들어진다. 생각보다 어렵지 않다.

  1. 먼저 안드로이드 git에서 소스를 다운받아 놓는다. mydroid에 다운받았다고 하자.
  2. doxygen 사이트에서 doxygen을 다운받아 설치한다. 윈도 버전 있다.
  3. Doxywizard를 실행한다.
  4. Wizard 탭, Expert 탭에서 여러가지 설정을 한다. 처음에는 Wizard탭, 숙달되면 Expert 탭 이용.
    • 소스 폴더 선택. 예를들어 mydroid/frameworks 선택. mydroid 전체를 선택하면 먹통될수도...
    • scan recursively 옵션 체크
    • 소스 종류는 .h,.c,.cpp 선택
    • html은 하위 폴더로 나누어서 생성되도록
    • 클래스 다이어그램 생성되도록
    • public이 아닌 함수도 나오도록 설정하면 소스 분석에 도움됨
    • 그외 적당히 옵션 설정
  5. 설정 마치고 Run 탭에서 run 실행한다.
Posted by 에코지오
,

안드로이드에서 모든 자바 애플리케이션 프로세스는 zygote 프로세스에 의해 fork된다. 자이고트 프로세스는 미리 초기화된 Dalvik 인스턴스이며, ZygoteInit, ZygoteConnection 신공을 펼쳐 새로운 자바 프로세스를 띄운다.

애플리케이션 프로세스의 쓰레드들
프로세스가 포크되고 ZygoteInit.main() -> ActivityThread.main()을 거치면서 애플리케이션의 "main" 쓰레드가 완성(?)되는데, DDMS를 통해 main 쓰레드 외에 기본적으로 생성되는 쓰레드 목록을 엿볼 수 있다.

  • "main" 쓰레드는 말 그대로 main 메소드를 실행하여 생성된 쓰레드이다. main 쓰레드는 액티비티가 존재할 경우 UI 쓰레드 역할도 담당한다.
  • ID 앞에 별표시가 붙은 쓰레드는 데몬(daemon) 쓰레드다. 데몬 쓰레드는 백그라운드에서 돌아가는 쓰레드로, 자바 프로세스는 데몬 쓰레드를 제외한 모든 쓰레드가 종료한 시점에 종료된다.
    HeapWorker 쓰레드는 가비지콜렉션(GC)을 수행하는 쓰레드인 듯하며, Signal Catcher 쓰레드는 뭐하는 놈인지 잘 모르겠다. JDWP 쓰레드는 애플리케이션 디버깅을 위한 쓰레드로 VM과 디버거를 연결해준다.
  • Binder Thread가 2개 있는데 다른 프로세스와의 통신(IPC) 작업을 빠르게 처리하기 위해 마치 DB커넥션풀처럼 미리 만들어둔 것으로 보인다.(확실치 않음)


main 쓰레드 생성 직후부터 Activity가 만들어지기까지의 과정

  1. zygote에 의해 자바 프로세스가 포크되면서 ZygoteInit.main() -> ActivityThread.main() 호출
  2. ActivityThread.main()은 먼저 Looper.prepareMainLooper()를 호출하여 main 쓰레드를 위한 메인 메시지 루프를 초기화함
  3. ActivityThread 인스턴스가 생성되고, ActivityThread는 ApplicationThread 인스턴스를 생성하는데, ApplicationThread는 system_server 프로세스의 ActivityManagerService 서비스와 상호작용하기 위한 IBinder 객체로서 작동
  4. 이 시점에 프로세스는 system_server 프로세스로부터의 IPC 콜을 기다릴 뿐 다른 일은 하지 않음. Application과 ApplicationContext 객체도 아직 생성되지 않음. 이들 객체는 애플리케이션 프로세스가 실제로 어떤 일을 할 때 생성됨(즉, 액티비티를 시작하거나 인텐트를 수신하거나 서비스를 시작하는 등의 작업)
  5. 이제 애플리케이션에서 실행해야할 액티비티가 있다면, ActivityManagerService가 RPC 호출을 통해 애플리케이션 프로세스의 ApplicationThread.scheduleLaunchActivity()를 실행
  6. ApplicationThread는 ActivityThread에 메시지를 전달하여 액티비티를 시작하도록 요청
  7. ActivityThread는 Application과 ApplicationContext 객체를 생성
  8. 그리고 Instrumentation을 이용하여 마침내 액티비티 자바 객체를 생성하고 액티비티의 onCreate()를 호출


main 쓰레드 스택트레이스 샘플 : NotePad 예제 애플리케이션의 NotesList 액티비티


Posted by 에코지오
,

안드로이드는 스윙과 마찬가지로 싱글 쓰레드 GUI 모델이 적용되어 있다.
즉 UI를 그리거나 갱신하는 쓰레드는 하나뿐이라는 것이다. 그 쓰레드는 바로 안드로이드의 주요 컴포넌트들이 실행되는 "main" 쓰레드이다. 모든 UI 관련 코드는 main 쓰레드에서 실행된다.

스윙에서 응답없음(unresponsive) 현상을 막기 위해 백그라운드에서 돌아가는 worker 쓰레드를 만든 것처럼, 안드로이드에서도 오래 걸리는 작업은 UI 쓰레드(= main 쓰레드)에서 처리하지 말고 별도의 쓰레드를 만들어 처리해야 한다. 그렇지 않으면 화면을 갱신하고자 하는 모든 코드는 block 당하여 ANR이 발생할 것이다.

오래 걸리는 작업에는 무엇이 있나?
- 파일 처리
- 네트워크 조회
- 다량의 DB 트랜잭션
- 복잡한 계산

그럼 백그라운드 쓰레드(Worker Thread)를 만드는 방법은?
- UI 쓰레드와 상호작용 없으면 그냥 Thread.start()에서 처리하면 된다.
- 그러나 작업후 결과를 UI에 반영(즉 UI 쓰레드와 통신)해야 한다면 Handler 등을 이용해야 한다.

다른 쓰레드에서 UI 쓰레드에 액세스하는 방법

  • Handler : 일반적인 쓰레드간 상호작용
  • Activity.runOnUiThread(Runnable) : UI 쓰레드의 메시지큐에 작업을 전달(post).
       activity 인스턴스에 액세스할 수 있는 경우 사용
  • View.post(Runnable) : UI 쓰레드의 메시지큐에 작업을 추가. 해당 view 객체에 접근가능한 경우 사용
  • View.postDelayed(Runnable, long) : 일정시간 후에 작업 처리
  • AsyncTask : 쓰레드나 Handler에 대한 지식없이 백그라운드 작업 후 UI 쓰레드에서 화면갱신 작업을
       처리할 수 있게 코드를 단순화해주는 유틸리티 클래스
  • AsyncQueryHandler : 비동기적으로 쿼리를 수행할 수 있게 해주는 도우미 클래스
  • NotifyingAsyncQueryHandler
  • WeakAsyncTask

  • Handler는 무엇인가?
    - 쓰레드간 상호작용을 위한 일반적인 목적의 클래스
    - 작업 쓰레드(=자식 쓰레드)에서 부모 쓰레드(=Handler 객체를 생성한 쓰레드)에 Message 및
      Runnable(부모 쓰레드에서 처리할 작업) 전달(send/post 메소드)
    - 자식 쓰레드에서 handler를 통해 전달되는 Message와 Runnable은 부모 쓰레드의 메시지큐에 들어감
    - 내부적으로 Runnable도 결국은 Message로 변환(Message.callback=runnable)되어 메시지큐에 들어감
    - Handler 객체를 생성한 쓰레드(부모 쓰레드)에서는 Looper를 통해 MessageQueue를 만들어 놓아야 함
    - UI쓰레드(= main쓰레드)는 ActivityThread.main()에 의해 생성되는데, 여기서 Looper를 통해
      UI 쓰레드용 메시지 큐가 이미 만들어져 있으므로 우리가 UI 쓰레드용 메시지큐를 만들 필요는 없음
    - HandlerThread 클래스는 Looper를 가진 쓰레드를 쉽게 만들기 위한 용도


    * 안드로이드 쓰레드에 대한 'i티거'님의 글 참조

    http://tigerwoods.tistory.com/26 Thread 구현하기1

    http://tigerwoods.tistory.com/28 Thread 구현하기2

    Posted by 에코지오
    ,
    안드로이드의 이벤트 처리 과정에 대한 글(http://blog.naver.com/osk1004?Redirect=Log&logNo=50069078782 )을 참조하여 나름대로 분석하여 메모한 결과를 적어본다.

    개략적인 이벤트 처리 과정
    1. 액티비티 생성시 액티비티의 윈도우를 WindowManagerService에 등록해둠
    2. 이벤트 발생시 네이티브 라이브러리(EventHub)를 통해 이벤트 읽음
    3. 이벤트 큐(KeyInputQueue)에 이벤트 쌓임
    4. 이벤트 디스패치 쓰레드(InputDispatcherThread)는 이벤트큐에서 이벤트를 꺼내어
      WindowManagerService의 디스패치 메소드 호출
    5. WindowManagerService는 등록된 애플리케이션의 윈도우에 이벤트를 전달
    6. 이벤트를 전달받은 윈도우는 하위 UI 컴포넌트 트리를 찾아가며 리스너 콜백함수 실행

    이벤트 전달을 위한 준비

    WindowManagerService는 system_server 프로세스에서 실행중인 서비스이다. WindowManagerService에서 감지된 이벤트를 애플리케이션 프로세스의 UI 컴포넌트에 전달하기 위해서 둘 사이에 연결고리가 미리 만들어져 있어야 한다. 통신은 AIDL을 통해서 이루어진다.

    애플리케이션의 윈도우를 WindowManagerService에 등록하는 과정

    1. ActivityManagerService는 ActivityThread를 호출하여 액티비티 런치.
      ActivityThread.performLaunchActivity()에서 Activity인스턴스 생성하고 activity.attach() 호출
    2. Activity는 attach()에서 PhoneWindow 객체 생성. 이 PhoneWindow는 액티비티내 뷰들의 root로서 DecorView 인스턴스 포함.

      mWindow = PolicyManager.makeNewWindow(this);
       
    3. ActivityManagerService는 ActivityThread를 호출하여 액티비티를 resume시킴.
      WindowManager 인스턴스가 생성되고 decorView가 WindowManager에 추가됨.

      ActivityThread.handleResumeActivity()
       
    4. WindowManager의 addView(decor)에서 ViewRoot 인스턴스를 생성하고 viewRoot.setView(decor) 호출
    5. viewroot.setView(decor)에서 IWindowSession을 통해 WindowManagerService에 IWindow인스턴스를 추가

      IWindowSession.add(window) 
       

    DecorView 클래스
    - FrameLayout을 상속받으며, PhoneWindow의 내부 클래스로 정의됨
    - 표준 윈도우 프레임 및 데코레이션을 포함하는 최상위 윈도우 뷰
    - 윈도우 매니저에 윈도우로서 추가됨

    ViewRoot 클래스
    - WindowManager와 View 사이의 protocol을 위한 구현 포함
    - Handler를 상속받음
    - IWindow 서비스 구현 클래스(W)를 내부 클래스로 포함 :  class W extends IWindow.Stub

    관련 AIDL
    IWindowSession.aidl : 애플리케이션 --> WindowManagerService

        int add(IWindow window, ... ...); // 윈도우를 WindowManagerService에 추가
        void remove(IWindow window);   

    IWindow.aidl : WindowManagerService --> 애플리케이션

        void dispatchKey(in KeyEvent event); //이벤트를 애플리케이션에 전달
        void dispatchPointer(in MotionEvent event, ...);
        void dispatchTrackball(in MotionEvent event, ...);

    KeyEvent.aidl, MotionEvent.aidl : 프로세스간 전달되는 이벤트 정보


    이벤트 감지 및 디스패치
    이벤트를 검출하고 애플리케이션으로 디스패치하는 로직은 WindowManagerService.java에 구현되어 있다. WindowManagerService는 InputDispatcherThread와 KeyInputQueue 구현 클래스를 이용하여 이벤트를 읽어들이고 적절한 윈도우에 전달하는 일을 한다.

    WindowManagerService 클래스
    - KeyInputQueue 구현 클래스(KeyQ) 및 InputDispatcherThread 클래스 포함
    - WindowManagerService 인스턴스 생성시 KeyInputQueue 생성 및 InputDispatcherThread 쓰레드 시작
    - InputDispatcherThread는 이벤트 타입에 따라 WindowManagerService의 디스패치 메소드 호출.

           dispatchKey(KeyEvent); // 예를들어 키보드 이벤트인 경우

    - 디스패치 메소드는 현재 포커스를 가진 윈도우를 찾아 이벤트 전달

         mKeyWaiter.waitForNextEventTarget(); // WindowState 찾음
         windowState.mClient.dispatchKey(event); // windowState.mClient는 IWindow 객체

    - IWindow 객체는 액티비티가 resume인 상태가 되면서 ViewRoot가 WindowServiceManager에 전달한 것

    KeyInputQueue 클래스
    - 안드로이드에서 진짜 이벤트 큐 역할
    - 인스턴스 생성시 새로운 쓰레드가 시작되면서 native boolean readEvent() 메소드를 무한루프 호출.
    - 리눅스 입력 디바이스로부터 실제 이벤트를 읽어들이는 로직은 네이티브 코드로 구현됨 : EventHub
    - 이벤트 읽는 과정 

       KeyInputQueue.java -> JNI -> com_android_server_KeyInputQueue.cpp -> EventHub.cpp -> Device

    InputDispatcherThread 클래스
    - Event-Dispatch Thread 구현 클래스(?)
    - 무한루프 돌면서 이벤트 큐에서 이벤트를 꺼내 WindowManagerService의 디스패치 메소드 호출

    이벤트 유형
    - 키보드 : RawInputEvent.CLASS_KEYBOARD
    - 트랙볼 : RawInputEvent.CLASS_TRACKBALL
    - 터치스크린 : RawInputEvent.CLASS_TOUCHSCREEN
    - 설정 변경 : RawInputEvent.CLASS_CONFIGURATION_CHANGED

    이벤트 정보를 담고 있는 핵심 클래스
    - 키보드 이벤트 : KeyEvent
    - 터치 or 트랙볼 이벤트 : MotionEvent


    애플리케이션에서 이벤트 처리 과정(키 이벤트 중심)

    이벤트를 전달받은 애플리케이션 윈도우는, 뷰 트리의 최상위부터 시작해서 실제 포커스를 가진 뷰까지 경로를 따라 이벤트를 디스패치한다.

    1. 이벤트가 발생하면 WindowManagerService는 이벤트 큐의 이벤트를 IWindow에 전달

      IWindow.dispatchKey(event);
       
    2. IWindow(ViewRoot.W 내부클래스가 구현)는 이벤트를 ViewRoot의 dispatchKey(event)에 다시 전달
    3. ViewRoot.dispatchKey()에서는 sendMessageAtTime(msg) 메소드를 통해 메시지 형태로 이벤트를 전달(왜 갑자기 여기서 Handler 메시지 형태로 이벤트를 전달하는가?)
    4. 보내진 이벤트 메시지는 Handler를 상속받은 ViewRoot의 handleMessage(Message)가 처리
    5. handleMessage()는 deliverKeyEventToViewHierarchy(event)를 호출
    6. deliverKeyEventToViewHierarchy()는 decor view의 dispatchKeyEvent(event) 호출

      mView.dispatchKeyEvent(event);
       
    7. decor view의 dispatchKeyEvent()에서는 현재 뷰에 리스너가 등록되어 있으면 현재 view의 리스너 콜백함수를 호출함(즉 드디어 이벤트가 처리됨)
    8. 등록된 리스너가 없으면 KeyEvent의 dispatch(callback) 호출 : callback은 view 자신
    9. KeyEvent.dispatch()는 다시 callback view의 onKeyDown() 호출 : 키 누름 이벤트인 경우
    10. view의 onKeyDown()은 setPressed() 호출 : setPressed() ->dispatchSetPressed()
    11. dispatchSetPressed()는 하위 View 클래스(예를들어 ViewGroup)에서 적절히 오버라이드됨
    12. ViewGroup의 dispatchSetPressed()에서는 자식 뷰들의 setPressed()를 호출
    13. 이런식으로 최종 타겟 UI 컴포넌트까지 이벤트가 디스패치됨


    View 클래스
    - KeyEvent.Callback 구현
    - 주요 디스패치 메소드 :

        dispatchKeyEvent(KeyEvent event);
        dispatchTouchEvent(MotionEvent event);
        dispatchTrackballEvent(MotionEvent event);

    ViewGroup 클래스
    - XXXLayout 들의 부모 클래스
    - dispatchSetPressed(boolean pressed) 메소드 코드 :

            final View[] children = mChildren;
            final int count = mChildrenCount;
            for (int i = 0; i < count; i++) {
                children[i].setPressed(pressed);
            }

    Posted by 에코지오
    ,

    [오디오 재생]

    * 패키지의 assets/audio/oh.mp3 플레이
    AssetFileDescriptor afd = getAssets().openFd("audio/oh.mp3");
    MediaPlayer  audio_play = new MediaPlayer();
    audio_play.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
    afd.close();
    audio_play.start();
     
    * 패키지의 res/raw/oh.mp3 플레이
    MediaPlayer  audio_play = MediaPlayer.create(context, R.raw.oh);
    audio_play.start();
     
    * 파일시스템의 /data/oh.mp3 플레이
    MediaPlayer  audio_play = new MediaPlayer();
    audio_play.setDataSource("/data/oh.mp3");
    audio_play.start();
     

    [비디오 재생]

    * 파일시스템의 /data/oh.mp4 플레이
    VideoView video = (VideoView) findViewById(R.id.video);
    MediaController nc = new MediaController(this);
    nc.setMediaPlayer(video);
    video.setVideoPath("/data/oh.mp4");
    video.start();

    * 패키지의 res/raw/oh.mp4 플레이

    VideoView에서는 패키지의 리소스 파일 재생 불가하며 직접 MediaPlayer를 이용해야함.
    Posted by 에코지오
    ,