2012년 11월 19일 월요일

하드웨어 리셋 이후에 저장된 파일이 날아간다.


  • 증상하드웨어 리셋 이후에 저장된 파일이 날아간다.
    • 상세 내용: FileOutputStream을 write후 close해서 DDMS의 File Explorer에 해당 파일이 나타났는데도 하드웨어 리셋을 하면 파일 크기가 0이 되거나 파일이 없어진다.
  • 원인: 파일시스템은 버퍼를 활용하고 있다. 버퍼링된 내용이 물리 파일에 씌여지기 전에 하드웨어 리셋이 되기 때문이다.
  • 해결책: FileOutputStream객체의 getFD()를 통해 전달받는 FileDescriptor객체를 이용하여 sync() 를 호출하여, 물리파일에 씌여지는 시기를 강제화 한다.
  • sync()사용 시의 문제점
    • 전화 중과 같은 상황에 sync가 호출 되면 속도가 느려질 수 있다.
  • 참고 링크

Exception없이 Stack Trace를 출력하는 방법

일반적으로 stack trace를 출력하는 방법은 Exception을 이용하는 것이다.
하지만, 디버깅 중 Exception없이 stack trace를 확인하면 도움이 될 경우가 있다.

이럴 경우 아래 클래스를 이용하면 Exception없이도 stack trace를 출력할 수 있다.


public class StackTrace {

    public static String toStackTraceString() {
        StackTraceElement[] elements = Thread.currentThread().getStackTrace();
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < elements.length; i++) {
            buf.append(elements[i].toString()).append('\n');
        }
        return buf.toString();
    }
}


2012년 11월 9일 금요일

Using HierarchyViewer

HierarchyViewer는 현재 표현하고 있는 View들의 구조 및 특정 View의 상태(속성)를 확인하는데 도움을 준다.

  • debugging 중에 특정 View의 레퍼런스를 확인하고, 해당 레퍼런스가 어떤 View인지 찾는데 유용하다.
    • Filter by class를 통해 class단위의 Filtering을 제공하고 있다.
    • 현재 Filter by id는 동작하지 않는 듯하다. 따라서 class에 의해 filtering한 view들 중에 특정 id를 찾는것에 의해 쉽게 원하는 view를 찾을 수 있다.
  • filtering에 의해 특정 view를 찾았다면 해당 view의 각종 property를 확인할 수 있다.
    • Property 종류는 아래와 같다.
      • Drawing
      • Events
      • Focus
      • Layout
      • Measurement
      • Miscellaneous
      • Padding
      • Scrolling
      • Text

HierarcheyViewer 캡쳐


2012년 11월 8일 목요일

What is the most accurate memory usage information on adb?

I found the post about this question below and the answer is using 'adb shell procrank'.

The reply of below post is written by Dianne Hackborn the google developer.
https://groups.google.com/forum/?fromgroups=#!topic/android-developers/r9hl8-t0lO0


Q: How calculates memory usage of application

A: "adb shell procrank" is generally the most accurate memory usage information -- in particular uss is the amount of memory private to your app, and pss also includes the shared memory weighted across the number of processes it is shared in.  (Ignore the other numbers, they are pretty much useless.)

Q: Thanks for your information, this command is work on emulator, but not 
work on my HTC Magic, however it's very useful to me. 

A: Ah it may not be installed on a production device.  You could also do "adb shell dumpsys meminfo" which will spew a bunch of stuff about the memory in each process, or "adb shell dumpsys meminfo <processname>" for info about your process.



If you could use root account on adb, you could use 'adb shell procrank' but if not use .adb shell dumpsys meminfo'.

adb top command

시스템 프로세스 상황 모니터링/ 프로세스별 CPU사용량, 메모리와 스왑 사용량 등
top is a command to monitor usage of cpu, memory and swap each system process


Usage: top [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]
    -m num  Maximum number of processes to display.
    -n num  Updates to show before exiting.
    -d num  Seconds to wait between updates.
    -s col  Column to sort by (cpu,vss,rss,thr).
    -t      Show threads instead of processes.
    -h      Display this help screen.


*** THE START OF EXAMPLE ***
tnkim@tnkim-N53SV:~$ adb shell top -m 10

User 2%, System 2%, IOW 0%, IRQ 0%
User 17 + Nice 0 + Sys 16 + Idle 571 + IOW 1 + IRQ 0 + SIRQ 0 = 605

  PID PR CPU% S  #THR     VSS     RSS PCY UID      Name
 8057  1   2% R     1    988K    392K  fg shell    top
 1275  0   0% S    65 352552K  45472K  fg system   system_server
  306  0   0% S     1      0K      0K  fg root     spi1
 1643  1   0% S    17 290620K  34152K  bg app_31   tw.dmb.service
 5971  0   0% S     1      0K      0K  fg root     kworker/0:2
 1833  1   0% S     9 276952K  28092K  bg app_30   com.thinkware.timesignal
 2026  0   0% S     5   4476K    220K  fg shell    /sbin/adbd
 1527  0   0% S    25 307048K  44920K  fg app_20   com.thinkware.dixon.nip
 1179  1   0% S    16  44680K   5408K  fg system   /system/bin/surfaceflinger
 1813  1   0% S    14 282368K  28656K  bg app_28   tw.cardiaryplus.deamon



User 4%, System 3%, IOW 0%, IRQ 0%
User 26 + Nice 0 + Sys 21 + Idle 570 + IOW 0 + IRQ 0 + SIRQ 0 = 617

  PID PR CPU% S  #THR     VSS     RSS PCY UID      Name
 8057  1   2% R     1    992K    404K  fg shell    top
 1275  0   1% S    65 352552K  45472K  fg system   system_server
  306  0   0% D     1      0K      0K  fg root     spi1
 1333  1   0% S    18 306988K  40020K  fg system   com.android.systemui
 1643  0   0% S    17 290620K  34152K  bg app_31   tw.dmb.service
  802  0   0% S     1      0K      0K  fg root     tpeg_thread
 5971  0   0% S     1      0K      0K  fg root     kworker/0:2
 1527  0   0% S    25 307048K  44920K  fg app_20   com.thinkware.dixon.nip
 2026  0   0% S     5   4476K    220K  fg shell    /sbin/adbd
 1179  1   0% S    16  44680K   5408K  fg system   /system/bin/surfaceflinger
...
*** THE END OF EXAMPLE ***







2012년 11월 6일 화요일

Service process가 개발자 또는 플랫폼에 의해 강제로 kill된 이후에 다시 시작되는 시간

결론부터 말하면 서비스가 kill 될 때 플랫폼이 재시작 될 시간을 알려주고, 그 시간에 해당 서비스를 재시작합니다.

자세한 사항은 아래와 같습니다.
Service process가 개발자 또는 플랫폼에 의해 강제로 kill된 이후에 다시 시작되는 시간은 친절하게도 플랫폼이 아래와 같은 로그를 표시해 주고 있습니다.
10-23 22:24:51.625: I/ActivityManager(2461): Process com.xxx.yyy (pid 3977) has died.
10-23 22:24:51.630: W/ActivityManager(2461): Scheduling restart of crashed service com.xxx.yyy/.zzz in 20000ms

대략 20초(20000ms)이후에 해당 service가 시작되는 것이 로그로 확인되네요.
10-23 22:25:11.685: I/ActivityManager(2461): Start proc com.xxx.yyy for service com.xxx.yyy/.zzz: pid=4026 uid=10049 gids={}

참고로, Restart정책에 대한 ActivityManagerService.java의 코드는 아래와 같습니다.
소스만 한 번 훑고 지나갑니다~

private final boolean scheduleServiceRestartLocked(ServiceRecord r,
            boolean allowCancel) {
        boolean canceled = false;
        
        final long now = SystemClock.uptimeMillis();
        long minDuration = SERVICE_RESTART_DURATION;
        long resetTime = SERVICE_RESET_RUN_DURATION;
        
        if ((r.serviceInfo.applicationInfo.flags
                &ApplicationInfo.FLAG_PERSISTENT) != 0) {
            minDuration /= 4;
        }
        
        // Any delivered but not yet finished starts should be put back
        // on the pending list.
        final int N = r.deliveredStarts.size();
        if (N > 0) {
            for (int i=N-1; i>=0; i--) {
                ServiceRecord.StartItem si = r.deliveredStarts.get(i);
                si.removeUriPermissionsLocked();
                if (si.intent == null) {
                    // We'll generate this again if needed.
                } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
                        && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
                    r.pendingStarts.add(0, si);
                    long dur = SystemClock.uptimeMillis() - si.deliveredTime;
                    dur *= 2;
                    if (minDuration < dur) minDuration = dur;
                    if (resetTime < dur) resetTime = dur;
                } else {
                    Slog.w(TAG, "Canceling start item " + si.intent + " in service "
                            + r.name);
                    canceled = true;
                }
            }
            r.deliveredStarts.clear();
        }
        
        r.totalRestartCount++;
        if (r.restartDelay == 0) {
            r.restartCount++;
            r.restartDelay = minDuration;
        } else {
            // If it has been a "reasonably long time" since the service
            // was started, then reset our restart duration back to
            // the beginning, so we don't infinitely increase the duration
            // on a service that just occasionally gets killed (which is
            // a normal case, due to process being killed to reclaim memory).
            if (now > (r.restartTime+resetTime)) {
                r.restartCount = 1;
                r.restartDelay = minDuration;
            } else {
                if ((r.serviceInfo.applicationInfo.flags
                        &ApplicationInfo.FLAG_PERSISTENT) != 0) {
                    // Services in peristent processes will restart much more
                    // quickly, since they are pretty important.  (Think SystemUI).
                    r.restartDelay += minDuration/2;
                } else {
                    r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
                    if (r.restartDelay < minDuration) {
                        r.restartDelay = minDuration;
                    }
                }
            }
        }
        
        r.nextRestartTime = now + r.restartDelay;
        
        // Make sure that we don't end up restarting a bunch of services
        // all at the same time.
        boolean repeat;
        do {
            repeat = false;
            for (int i=mRestartingServices.size()-1; i>=0; i--) {
                ServiceRecord r2 = mRestartingServices.get(i);
                if (r2 != r && r.nextRestartTime
                        >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN)
                        && r.nextRestartTime
                        < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) {
                    r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN;
                    r.restartDelay = r.nextRestartTime - now;
                    repeat = true;
                    break;
                }
            }
        } while (repeat);
        
        if (!mRestartingServices.contains(r)) {
            mRestartingServices.add(r);
        }
        
        r.cancelNotification();
        
        mHandler.removeCallbacks(r.restarter);
        mHandler.postAtTime(r.restarter, r.nextRestartTime);
        r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
        Slog.w(TAG, "Scheduling restart of crashed service "
                + r.shortName + " in " + r.restartDelay + "ms");
        EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART,
                r.shortName, r.restartDelay);

        return canceled;
    }