안드로이드 시스템 분석에 사용할만한 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 에코지오
    ,
    안드로이드의 이벤트 처리 메커니즘과 Handler 개념을 이해하기 위해 먼저 스윙의 쓰레드에 대해 이해할 필요가 있다.

    * 스윙의 이벤트 처리 쓰레드 관련 자료
     
      
    Single 쓰레드 UI 모델
    - 모든 UI 관련 작업은 하나의 쓰레드에서만 처리되어야 함. 그렇지 않으면 비정상적인 결과가 생김.
      => 멀티쓰레드로 하면 안되는 이유 : http://weblogs.java.net/blog/kgh/archive/2004/10/multithreaded_t.html
    - 그 하나의 쓰레드를 흔히 Event-Dispatch Thread(EDT)라고 부름.
    - EDT는 UI의 무분별한 갱신을 막고 일관성있는 화면 갱신 메카니즘을 제공
    - EDT에서의 GUI 이벤트를 처리하는 작업은 빨리 끝나야 하며, 그렇지 않으면 화면은 unresponsive 상태가 됨. 
      기억하시라. 화면 갱신 작업은 하나의 쓰레드에서만 처리된다는 것을.
    - EDT는 사용자의 비동기적인 요구에 대응하기 위해 Event Queue를 유지하며
      사용자의 요구는 이벤트 큐에 일단 저장되었다가 적절한 시점에 일괄적으로 처리됨.
     
     
    * 스윙의 주요 쓰레드
    - Initial Thread : 초기 애플리케이션 실행. 여기서 UI를 만들지는 않음.
    - Event-Dispatch Thread : GUI 이벤트 처리(리스너에 이벤트 전달해서 콜백메소드 실행) 및
       스윙 컴포넌트 method(대부분 thread-safe하지 않음) 처리. 
       UI를 생성하고 UI와 상호작용하는 모든 코드는 EDT에서 실행됨.
    - Worker Thread(Background Thread) : 시간이 소요되는 작업은 EDT에서 처리하지 말고,
        백그라운드 쓰레드로 만들어서 처리후 결과만 UI에 반영
     
      
    * 스윙 주요 클래스
    - SwingWorker : 백그라운드 작업을 처리하면서 EDT와 상호작용할 수 있게 해줌
    - SwingUtilities.invokeLater(aRunnable) : EDT의 다른 작업이 다 끝난 후에 주어진 작업을 처리

    Posted by 에코지오
    ,
    안드로이드도 이벤트 디스패치 쓰레드 모델을 구현하고 있을까?

    [스윙 이벤트 디스패치 쓰레드 : 출처-javaworld]
    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 에코지오
    ,
    이벤트 리스너를 구현하는 클래스 유형

    • 익명 클래스로 구현 : 1회성 로직 구현. 대부분 이런식으로 구현하고 있음.
    • 내부 클래스 또는 액티비티 외부의 독립 클래스로 구현 : 재사용 가능함
    • 액티비티가 자체 구현 : 해당 이벤트가 이 액티비티에 한개만 존재하는 경우

    Posted by 에코지오
    ,