Handler와 Message를 활용하여 콜백함수 구현하기

 안드로이드 프레임워크를 분석하던 도중 흥미로운 부분을 발견하게 되어 이를 소개하고자 합니다. 물론 이는 안드로이드의 Application 단계에서도 쉽게 구현될 수 있는 기능이기에 안드로이드 애프리케이션 제작에 어느 정도 경험이 있으신 분들이라면 쉽게 이해하실 수 있으시리라 생각합니다.


 이번에 다루고자 하는 핵심적인 개념은 바로 Callback 입니다. 그럼 여기서 Callback 이란 무엇인지 간단하게 설명하도록 하겠습니다.


Callback이란?

 일반적으로 우리들이 프로그래밍을 설계할 때 Method와 같은 함수를 구현합니다. 특히 API와 같이 원하는 기능이 미리 구현되어 있어 해당 함수를 호출하는 것으로  원하는 기능을 실행하기도 하지요. Callback 또한 일반적인 함수들과 비슷하게 구성되어 있습니다. Android의 경우 프로그래머가 구현하고자 하는 기능을 Listener Interface를 통해 Callback 기능을 등록해줍니다.


 Callback 함수가 일반 함수와 가장 큰 차이점으로 호출되는 시점에 있습니다. 일반적인 함수의 경우 프로세스가 해당 함수를 호출하면 호출되는 즉시 해당 기능을 수행하게 됩니다. 반면 Callback 함수의 경우 프로세서가 호출을 요청할 경우 일반 함수처럼 즉시 호출될 수도 있지만 프로세스의 동작과는 독립적으로 동작하는 것이 가능하여 해당 프로세스가 수행을 종료한 후에 Callback 함수를 실행시킬 수 있습니다.


 안드로이드 Framework에서 Runnable Interface를 통해 Callback 기능을 구현한 방법이 있어 해당 기능을 분석해 보았습니다. 특이하게도 안드로이드에서 지원하는 Looper와 Message를 활용해서 구현하였다는 점인데요 쉽게 설명을 드리자면 실행하고자 하는 함수를 Runnable Interface를 통해 구현한 후 이를 Mssage를 통해 해당 함수를 예약해 두었다가 이후 프로세스가 작업을 종료하게 되었을 때 해당 함수를 호출하는 방식입니다.


 자세한 구현 내용을 실제 소스코드를 통해 확인해보도록 합시다.



/framework/base/core/java/android/view/Choreographer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
public final class Choreographer {
 
    ....
    // Choreographer를 초기와 합니다. 인자로 Choreographer의 Looper를 넘겨줍니다.
    // Thread local storage for the choreographer.
    private static final ThreadLocal<Choreographer> sThreadInstance =
            new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            return new Choreographer(looper);
        }
    };
 
    ....
 
    private final Looper mLooper;
    private final FrameHandler mHandler;
 
    // The display event receiver can only be accessed by the looper thread to which
    // it is attached.  We take care to ensure that we post message to the looper
    // if appropriate when interacting with the display event receiver.
    private final FrameDisplayEventReceiver mDisplayEventReceiver;
 
    ....
 
    private Choreographer(Looper looper) {
    //Constructor를 통해 Looper를 받는다.
        mLooper = looper;
    //Constructor를 통하여 얻게 된 Looper를 FrameHadler에 등록한다.
        mHandler = new FrameHandler(looper);
        mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
 
        ....
 
        }
    }
 
    ....
 
}
 
 
//Callback 기능을 수행할 Hndler입니다.
//Callback 기능 구현시 handlerMessage(Message msg) 함수는 실행되지 않습니다.
private final class FrameHandler extends Handler {
        public FrameHandler(Looper looper) {
            super(looper);
        }
 
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DO_FRAME:
                    doFrame(System.nanoTime(), 0);
                    break;
                case MSG_DO_SCHEDULE_VSYNC:
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
}
 
//Callback 기능을 구현하고자 하는 부분입니다.
//Runnable Interface로 구현하고자 하는 Callback 함수를 run() 함수로 구성합니다.
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
 
        public FrameDisplayEventReceiver(Looper looper) {
            super(looper);
        }
 
        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
 
        ....
 
            mTimestampNanos = timestampNanos;
            mFrame = frame;
 
            //Message를 통해 Callback 기능 구현
 
            //Callback 함수를 Message에 등록합니다.
            //mHandler    : Message를 받을 Looper를 갖고 있는 Handler
        //this    : 등록하고자 하는 함수. 해당 기능은 Runnable Interface를 통하여 run() 함수로 구성됨
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            //Message를 Handler에 전송합니다. Handler는 Message를 받은 후 현재 프로세스가 작업을
            // 종료하면 이후 해당 Callback 기능을 실행합니다.
            mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
        }
 
        //실행하고자 하는 Callback 함수
        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
}
cs


 위에서 구현된 함수를 이미지로 나타내면 다음과 같습니다. 



300x250

안드로이드 프레임워크 프로그래밍(21) [System Service란?]

안드로이드/프레임워크 2015. 8. 23. 18:36

 지금까지 블로그를 통해 안드로이드 프레임워크에 대해 꾸준이 다루어 왔습니다. 그러다가 문득 이런 생각을 해보게 되었습니다.


"지금까지 프레임워크에 대한 이론을 정확히 다루었던 포스팅이 있었던가?"


 프로그래밍을 공부하면서 문제의 솔루션을 찾는 것만 생각을 하다보니 막상 솔루션을 설명하는 것에 대한 공부를 소흘히 해왔던 듯한 생각이 들게 되었습니다. 지금까지 개념들에 대해 간단한 설명만 하고 소스코드를 확인하고 넘어가는 과정만 보였지 실질적으로 해당 개념에 대해 심도있는 고찰을 하는 경우가 드물었음을 알게 되었습니다. 비록 지금 시점에서 포스팅하기엔 다소 늦은 감이 있습니다만 이후에 안드로이드 프레임워크에 대해 연구하실 분들을 위해 조금씩 생각나는 중요한 내용들을 정리해볼까 합니다. 그 첫번째로 안드로이드 프레임워크의 가장 기초적인 상식이라 할 수 있는 System Service에 대해 알아보고자 합니다.


Android System Service

 System Srevice란 안드로이드에서 제공하는 Framework API와 Hardware 사이를 연결해주는 기능을 담당하는 서비스입니다. System Service는 아래 빨간 네모로 강조한 부분을 일컫는 말입니다.


출저 : Android Open Source Project


  실제로 System Service의 일부 기능들은 안드로이드 Application과 같이 순수 Java로 구성되어 프로그램을 관리하는 기능을 하는가 하면 Camera와 같이 하드웨어를 사용하여 JNI를 통해 C++혹은 C와 상호작용 할 수 있도록 설계되어 있는 경우가 있습니다. 안드로이드 Application과는 달리 System Service는 Application이 종료된 후에도 계속 동작하여 이후 다른 Application이 실행되었을 때에도 계속 동작할 수 있도록 구성되어 있습니다.


 System Service는 위의 그림에서 보시는 바와 같이 특정한 기능에 집중되어 있습니다. 이러한 System Service의 기능은 Media Server와 System Server로 나누어서 볼 수 있습니다. 전체적인 기능을 보았을 때 Media Server는 Hardware Abstraction Library(HAL)에 의존적이다 보니 C와 C++과 같은 Native Library를 좀 더 적극적으로 사용하고 있다고 보셔도 될 듯 합니다.


 System Service는 수십개의 기능들로 구성되어 있으며 애플리케이션 프로그래밍을 할 때 필요로 하는 기능들을 제공합니다. 각 서비스들은 Binder를 통해 통신을 수행합니다. Application 또한 Binder를 통해 기능들이 초기화 됩니다. Binder에 대한 자세한 내용은 아래 링크를 참조해 주시기 바랍니다.


http://d2.naver.com/helloworld/47656


 


300x250

[JAVA]JDWP(Java™ Debug Wire Protocol)

프로그래밍 팁 2015. 8. 21. 23:20


 JDWP(Java™ Debug Wire Protocol)이란 디버거와 디버그를 하고자 하는 자바 가상머신(JVM) 사이의 통신을 위해 사용되는 프로토콜 입니다. JDWP는 같은 디버거가 다음과 같은 작업을 하는 것을 할 수 있도록 해줍니다.


 - 같은 컴퓨터 상의 다른 프로세스 내부간의 디버깅

 - 원격 컴퓨터 상에서의 디버깅


 JDWP는 통신 뿐 아니라 형식과 레이아웃을 상세히 다룬다는 점에서  다른 프로토콜들과 차이점을 가지고 있습니다. JDWP는 간단한 하나의 API를 통하여 여러가지의 전송 방식들을 수용할 수 있도록 만들어져 있습니다. 특정한 전송방식은 각 디버거와 타겟 JVM의 조합이 반드시 지원되지는 않습니다.


 아직은 JDWP가 장래엔 더욱 발전할 것으로 보이지만, JDWP는 간단한 실행에 있어서 충분히 실행할 수 있도록 고안되어 있습니다.


 안드로이드의 기반인 Dalvik 가상머신 또한 JDWP를 지원합니다. 안드로이드 기기 내의 애플리케이션은 Dalvik 가상머신을 통해 실행되고 있으며 각 애플리케이션은 또한 DDMS를 사용하여 특정한 포트를 통하여 디버깅을 할 수 있습니다. 만약 여러개의 애플리케이션을 디버깅을 할 때, DDMS는 특정한 가상머신의 디버깅 포트를 통한 포트포워딩 기능을 지원합니다. DDMS의 선택 메뉴를 통해 자신이 디버깅 하고자 하는 애플리케이션을 자유롭게 변경할 수 있는 것이지요. DDMS는 포트 8700번을 통해 포트포워딩을 합니다.

 

 실제 안드로이드 기기를 통해 Traceview를 해보면 JDWP 스레드를 확인하실 수 있습니다 JDWP의 Trace까지 추적되는 것 또한 확인하실 수 있습니다.





출저: http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html


300x250

[Java] Error 혹은 Debug시 등장하는 method인 access$000

프로그래밍 팁 2015. 8. 20. 23:57

 안드로이드 프레임워크를 공부하면서 종종 신기한 경우들을 마주치곤 합니다. 그 중 하나가 바로 실제 코드상에는 존재하지 않던 method가 디버깅을 할 때 예상치 못하게 등장하기 때문입니다.




 분명 소스코드 상에서는 존재 자체가 없었던 method인 'access$000'가 이렇게 디버깅을 하던 도중 발견되는 경우가 종종 있습니다. 과연 이것의 정체는 무엇일까요?


 이 문제의 원인은 바로 Java 언어의 특징 중 하나인 Inner class에서 원인을 찾을 수 있습니다.


 Java를 공부하신 분들이라면 누구나 아시는 듯이 Inner class는 말 그대로 Class 안에 내포된 Class를 의미합니다. C언어에서 마치 Struct 구조체 안에 또다른 Struct를 품은 듯한 형태를 띄고 있는 구조라고 이해하시면 되겠습니다.


아래는 Inner class가 구현되어 있는 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Test {
 
  public void func() {
    System.out.println(new Inner().a);
    System.out.println(new Inner().getInt());
    System.out.println(new Inner2().b);
    System.out.println(new Inner2().getInt());
  }
 
  class Inner {
    private int a;
 
    public int getInt() {
      return a;
    }
  }
 
  class Inner2 {
    int b;
 
    public int getInt() {
      return b;
    }
  }
}
cs


 위의 예제는 Test 클래스 안에 두 개의 Inner class로 구성되어 있는 소스코드입니다. Test 클래스의 입장에서 Inner class는 private 인 경우에도 접근이 가능하여 위의 func() method에서 실행되는 모든 함수들이 제 기능을 하는 것을 확인하실 수 있습니다.


 그런데 여기서 문제가 발생하게 됩니다. Java의 소스코드를 컴파일한 결과물인 bytecode는 Inner class를 고려하지 않는다는 점입니다. 이로 인해 bytecode 상에서 func() method는 접근할수 없는 field 값은 Inner 클래스의 a의 값에 접근할 수 없게 됩니다.

 그렇기 때문에 실제 bytecode 상에서는 private로 설정된 내부클래스에 접근할 수 있도록 하기 위해 소스코드를 살짝 바꾸어서 기능을 똑같이 구현되게 하는데 실제 위의 설계된 소스코드를 디컴파일하게 되면 위에서 보았던 메소드인  access$000이 등장하는 것을 알 수 있습니다.


 아래의 예제는 Inner class가 적용되었을 경우를 가장한 예제입니다. 위의 소스코드와 비교하시면 자신이 설계한 소스코드에서 등장하는  access$000이 어느 시점에서 등장하게 되는지 어느 정도 감이 오실 것이라 생각됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Test {
  public void func() {
    System.out.println(Test$Inner.access$000(new Test$Inner(this)));
    System.out.println(new Test$Inner(this).getInt());
    System.out.println(new Test$Inner2(this).b);
    System.out.println(new Test$Inner2(this).getInt());
  }
}
 
class Test$Inner {
  final Test this$0;
 
  private int a;
 
  Test$Inner(Test test) {
    this$0 = test;
  }
 
  public int getInt() {
    return a;
  }
 
  static int access$000(Test$Inner inner) {
    return inner.a;
  }
 
}
 
class Test$Inner2 {
  final Test this$0;
 
  int b;
 
  Test$Inner2(Test test) {
    this$0 = test;
  }
 
  public int getInt() {
    return b;
  }
}
cs


 아래는 제가 안드로이드 프레임워크를 분석하던 도중  access$000가 뜨던 부분입니다. 보시는 대로 new Handler() 방식으로 내부클래스를 구성하고 있으며 이를  access$000 함수명을 통하여 updateWindow() 함수에 접근하고 있는 것을 확인할 수 있습니다.


/frameworks/base/core/java/android/view/SurfaceView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SurfaceView extends View {
 
....
 
    final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case KEEP_SCREEN_ON_MSG: {
                    setKeepScreenOn(msg.arg1 != 0);
                } break;
                case GET_NEW_SURFACE_MSG: {
                    handleGetNewSurface();
                } break;
                case UPDATE_WINDOW_MSG: {
                    updateWindow(falsefalse);
                } break;
            }
        }
    };
 
....
 
}
cs



출저 : http://www.javacodegeeks.com/2012/05/java-pitfalls-field-access-in-inner.html

300x250

Intel Management Engine BIOS Extention 설정시 암호 변경이 안될 때 해결방법

공대생의 팁 2015. 8. 19. 21:12

 Intel 사의 bios를 설정할 때에 사용되는Intel Management Engine BIOS Extention을 처음 사용할 때에 default 암호를 알 수가 없어 이를 어떻게 해야 헤결될 수 있는지 방법을 한참 찾다 설정 방법을 알게 되어 이렇게 포스팅을 하게 되었습니다. 혹시 Intel Management Engine BIOS Extention을 설정하시다가 저와 같이 암호 설정 때문에 애를 먹는 분이라면 본 포스팅이 도움이 되었으면 합니다.


 먼저, Intel Management Engine BIOS Extention에 진입하는 방법부터 알아보도록 하겠습니다. 아래 화면은 Dell사의 컴퓨터를 기준으로 F12 버튼을 누른 후 Boot Device Menu로 진입한 화면입니다.



 이렇게 Boot Device Menu에 진입하게 되면 위와 같은 메뉴들을 확인하실 수 있습니다. 이 중 Intel Management Engine BIOS Extention을 선택하여 진입하도록 합니다.



 그렇게 해서 진입을 하면 아래에서 보는 바와 같이 처음부터 암호를 입력하라는 뜬금없는 화면이 뜨게 됩니다. 우리는 이 암호를 설정한 적도 없음에도 이렇게 버젓이 뜨니 당혹스러울 수 밖에 없는 상황이지요...

 실은 해당 암호는 처음에는 Defalt로 기본 암호가 설정되어 있습니다. 해당 Default 암호는 다음과 같습니다.


admin


 위의 디폴트 암호를 입력하시면 아래와 같이 암호를 새로 설정하라는 안내가 나옵니다.



 그런데 이상하게도 암호를 설정하려 할 때마다 위와 같은 경고문이 뜨면서 변경된 암호를 받아들이지 않는 당혹스러운 상황이 벌어집니다.


Error - Intel(R) ME password change rejected


 위와 같은 일이 발생하는 이유는 자신이 설정한 암호가 해당 프로그램이 요구하는 암호의 기준에 맞지 않아 보안에 약한 암호이기 때문에 위와 같이 자신이 설정한 암호가 거절당하는 상황이 발생하게 됩니다.

 위와 같은 상황이 생기시는 분들이 계신다면 아래의 규칙대로 자신의 암호를 다시 설정하시면 암호를 설정하실 수 있을 것입니다.


 1. 8자 이상의 암호로 구성되어야 합니다.

 2. 1개 이상의 숫자가 암호에 포함되어야 합니다.(1,2,...,0)

 3. 1개 이상의 기호(!,@,#,$,...)가 포함되어야 합니다. 단, '_'는 암호로 설정할 수 있으나 기호로는 예외입니다.

 4. 알파벳 설정시 대문자와 소문자를 섞어서 사용해야 합니다.



 예를 들어 다음과 같이 암호를 설정하시면 확실하게 적용되는 것을 확인하실 수 있습니다.


eLecs20!5



300x250

SurfaceView가 다른 View 클래스와의 차이(updateWindow() method in SurfaceView class)

안드로이드/프레임워크 2015. 8. 17. 20:13

 안드로이드 UI를 설계할 때 주로 사용되는 View의 경우 화면에 한 번 표출되면 특별한 이펙트를 적용하지 않는 한 Activity에서 생성된 모습 그대로의 모습을 유지하는 경우가 대부분입니다.

 반면 SurfaceView의 경우 화면의 변화가 다른 View들에 비해 거의 실시간으로 화면에 변화를 주어야 하는 특성을 가지고 있기 때문에 이를 실시간으로 적용시킬 수 있는 기능이 필요로 하는데 그 중 하나가 updateWindow() 함수입니다.


 아래 간단한 예제를 통해 알아보도록 하겠습니다.


1
surfaceView.setVisibility(View.VISIBLE);
cs


 Application 단계에서 SurfaceView를 화면상에서 보이도록 설정하게 되는 순간 SurfaceView는 화면에 비추어지기 시작합니다.


/frameworks/base/core/java/android/view/SurfaceView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        mViewVisibility = visibility == VISIBLE;
        boolean newRequestedVisible = mWindowVisibility && mViewVisibility;
        if (newRequestedVisible != mRequestedVisible) {
            // our base class (View) invalidates the layout only when
            // we go from/to the GONE state. However, SurfaceView needs
            // to request a re-layout when the visibility changes at all.
            // This is needed because the transparent region is computed
            // as part of the layout phase, and it changes (obviously) when
            // the visibility changes.
            requestLayout();
        }
        mRequestedVisible = newRequestedVisible;
        updateWindow(falsefalse);
    }
cs


 보시는 바와 같이 SurfaceView를 화면에 비추도록 설정하였을 때 함수 내에 updateWindow() 함수가 호출되고 있는 것을 보실 수 있습니다. 해당 함수는 SurfaceHolder에서 설정한 기능들을 수행하는 데에 사용되며 SurfaceHolder에 대한 자세한 사항은 아래 포스팅을 참조해 주시길 바랍니다.


http://elecs.tistory.com/78


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
    private void updateWindow(boolean force, boolean redrawNeeded) {
 
        if (!mHaveFrame) {
            return;
        }
        ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot != null) {
            mTranslator = viewRoot.mTranslator;
        }
 
        if (mTranslator != null) {
            mSurface.setCompatibilityTranslator(mTranslator);
        }
 
        int myWidth = mRequestedWidth;
        if (myWidth <= 0) myWidth = getWidth();
        int myHeight = mRequestedHeight;
        if (myHeight <= 0) myHeight = getHeight();
 
        getLocationInWindow(mLocation);
        final boolean creating = mWindow == null;
        final boolean formatChanged = mFormat != mRequestedFormat;
        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
        final boolean visibleChanged = mVisible != mRequestedVisible;
 
        if (force || creating || formatChanged || sizeChanged || visibleChanged
            || mLeft != mLocation[0|| mTop != mLocation[1]
            || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
 
            if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
                    + " format=" + formatChanged + " size=" + sizeChanged
                    + " visible=" + visibleChanged
                    + " left=" + (mLeft != mLocation[0])
                    + " top=" + (mTop != mLocation[1]));
 
            try {
                final boolean visible = mVisible = mRequestedVisible;
                mLeft = mLocation[0];
                mTop = mLocation[1];
                mWidth = myWidth;
                mHeight = myHeight;
                mFormat = mRequestedFormat;
 
                // Scaling/Translate window's layout here because mLayout is not used elsewhere.
 
                // Places the window relative
                mLayout.x = mLeft;
                mLayout.y = mTop;
                mLayout.width = getWidth();
                mLayout.height = getHeight();
                if (mTranslator != null) {
                    mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
                }
 
                mLayout.format = mRequestedFormat;
                mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                              | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                              | WindowManager.LayoutParams.FLAG_SCALED
                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                              ;
                if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
                    mLayout.privateFlags |=
                            WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
                }
                mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 
                if (mWindow == null) {
                    Display display = getDisplay();
                    mWindow = new MyWindow(this);
                    mLayout.type = mWindowType;
                    mLayout.gravity = Gravity.START|Gravity.TOP;
                    mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
                            mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets);
                }
 
                boolean realSizeChanged;
                boolean reportDrawNeeded;
 
                int relayoutResult;
 
                mSurfaceLock.lock();
                try {
                    mUpdateWindowNeeded = false;
                    reportDrawNeeded = mReportDrawNeeded;
                    mReportDrawNeeded = false;
                    mDrawingStopped = !visible;
 
                    if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
 
                    relayoutResult = mSession.relayout(
                        mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                            visible ? VISIBLE : GONE,
                            WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
                            mWinFrame, mOverscanInsets, mContentInsets,
                            mVisibleInsets, mConfiguration, mNewSurface);
                    if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                        mReportDrawNeeded = true;
                    }
 
                    if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
                            + ", vis=" + visible + ", frame=" + mWinFrame);
 
                    mSurfaceFrame.left = 0;
                    mSurfaceFrame.top = 0;
                    if (mTranslator == null) {
                        mSurfaceFrame.right = mWinFrame.width();
                        mSurfaceFrame.bottom = mWinFrame.height();
                    } else {
                        float appInvertedScale = mTranslator.applicationInvertedScale;
                        mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
                        mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
                    }
 
                    final int surfaceWidth = mSurfaceFrame.right;
                    final int surfaceHeight = mSurfaceFrame.bottom;
                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
                            || mLastSurfaceHeight != surfaceHeight;
                    mLastSurfaceWidth = surfaceWidth;
                    mLastSurfaceHeight = surfaceHeight;
                } finally {
                    mSurfaceLock.unlock();
                }
 
                try {
                    redrawNeeded |= creating | reportDrawNeeded;
 
                    SurfaceHolder.Callback callbacks[] = null;
 
                    final boolean surfaceChanged = (relayoutResult
                            & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                        mSurfaceCreated = false;
                        if (mSurface.isValid()) {
                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
                            callbacks = getSurfaceCallbacks();
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceDestroyed(mSurfaceHolder);
                            }
                        }
                    }
 
                    mSurface.transferFrom(mNewSurface);
 
                    if (visible && mSurface.isValid()) {
                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                            mSurfaceCreated = true;
                            mIsCreating = true;
                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceCreated(mSurfaceHolder);
                            }
                        }
                        if (creating || formatChanged || sizeChanged
                                || visibleChanged || realSizeChanged) {
                            if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
                                    + " w=" + myWidth + " h=" + myHeight);
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                            }
                        }
                        if (redrawNeeded) {
                            if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                if (c instanceof SurfaceHolder.Callback2) {
                                    ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
                                            mSurfaceHolder);
                                }
                            }
                        }
                    }
                } finally {
                    mIsCreating = false;
                    if (redrawNeeded) {
                        if (DEBUG) Log.i(TAG, "finishedDrawing");
                        mSession.finishDrawing(mWindow);
                    }
                    mSession.performDeferredDestroy(mWindow);
                }
            } catch (RemoteException ex) {
            }
            if (DEBUG) Log.v(
                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
                " w=" + mLayout.width + " h=" + mLayout.height +
                ", frame=" + mSurfaceFrame);
        }
    }
cs


300x250

[Android Developers] SurfaceHolder

안드로이드/프레임워크 2015. 8. 13. 00:38

  SurfaceHolder란 말 그대로 하나의 display surface를 잡고 있는 추상 Interface를 말합니다.  SurfaceHolder는 개발자가 직접 surface의 사이즈나 형식을 조절할 수 있고 surface의 pixel을 수정할 수 있습니다. 그리고 surface의 변화를 실시간으로 확인할 수 있습니다. SurfaceHolder는 왠만해서는 SurfaceView 클래스를 통해 사용할 수 있습니다.


 Surface의 상태 변경에 대한 정보를 다루기 위해서는 SurfaceHolder.Callback 인터페이스를 사용합니다. SurfaceView에서 사용될 때 SurfaceHolder.Callback 내에 있는 surfaceCreated(), surfaceDistroyed() 함수를 설정하는 것으로 Surface를 통제할 수 있게 합니다. SurfaceHolder.Callback는 SurfaceHolder.addCallback() 함수를 통해 등록할 수 있습니다.


SurfaceHolder가 SurfaceView에 적용되는 과정은 아래 포스팅을 참조해 주시길 바랍니다.

http://elecs.tistory.com/78


출저 : http://developer.android.com/reference/android/view/SurfaceHolder.html

300x250

Ubuntu에서 특정 Unicode가 안보일 때

공대생의 팁 2015. 7. 22. 20:44

 우분투 운영체제로 웹사이트를 돌아다니다 보면 종종 네모 모양으로 깨져서 나오는 경우가 있습니다. 아래는 특정한 유니코드가 깨져 제대로 출력 되지 않고 있는 상황을 보실 수 있습니다.




 위와 같은 경우 몇몇 유니코드는 Terminal에서 다음과 같은 명령어를 입력하셨을 때 해당 유니코드가 출력 되는 경우가 있습니다.


$ sudo apt-get install ttf-ancient-fonts


 이후 컴퓨터를 리셋 하신 후 다시 확인하시면 유니코드가 제대로 출력 되는 것을 확인하실 수 있습니다.


 


300x250

실리콘벨리 탐방기록(8) - Hilton San Francisco Airport Baypoint[2015.07.03]

 실리콘벨리 탐방의 마지막날, 귀국을 하기 바로 전날 숙소를 찾던 도중 이름만 듣던 힐튼 호텔의 가격이 매우 저렴해서 당장 예약을 하게 되었습니다. 난생 처음 고급호텔을 써 보게 되었다는 생각에 들떠 사용해보니 역시 힐튼이라는 이름이 이토록 유명했는지 알 수 있었습니다.

 혹시 샌프란시스코 국제공항 인근에서 좋은 숙소를 구하고자 하시는 분이 계신다면 이 곳 Hilton San Francisco Airport Baypoint를 추천드리고 싶습니다!



입구에서 이 입간판을 확인하시면 쉽게 찾으실 수 있습니다.



로비에 들어가기 전 입구입니다.



로비에서의 모습입니다.

이 곳에서 체크인을 하시면 숙소 출입 카드를 받으실 수 있습니다.



이번에 묵게 될 숙소 입구입니다.



방 내부 분위기가 상당히 좋아 보입니다.



TV는 무려 LG로군요!



침대 위로는 샌프란시스코 도심의 풍경이 펼쳐져 있습니다.



탁상 위의 시계가 상당히 깔끔해 보이네요.



간단하게 커피를 먹을 수 있는 자리도 마련되어 있습니다!



간단한 메모지도 준비되어 있습니다.



쇼파가 상당히 푹신푹신해 보입니다.

앉아보니 마음이 편안해지는군요 ㅎㅎ



샌프란시스코의 상징이기도 한 골든게이트브릿지의 그림이 걸려있군요.



욕실 내부를 살펴보도록 할까요?



욕실 내부는 여느 호텔이랑 크게 다르지는 않습니다.

다만 일반적인 호텔들과 비교할 때 비고젹 욕실이 넓은 편입니다.



세면대 아래에는 수건들이 비치되어 있습니다.

옆에는 물도 있는데 사용시 추가 요금을 내야 합니다.



간단한 세면도구들이 보시는 바와 같이 마련되어 있는 것을 보실 수 있습니다.



숙소 출입 카드입니다.

안에는 와이파이 사용 방법들이 친절하게 안내 되어 있습니다.



창밖에서 바라본 풍경입니다. 저 멀리 Baypoint가 보이네요.

자고 일어난 후 아침에 주변을 산책하는 것도 나쁘지 않을 듯 합니다!



이번에는 숙소 내부의 시설들을 살펴보도록 하겠습니다.



각 층에 마련된 자판기 코너에 가보면 얼음 공급기와 음료 자판기가 있는 것을 확인하실 수 있습니다.



음료는 코카콜라, 7UP, 스프라이트, 생수 등이 보입니다.



몇몇 층에서는 간단한 군것질을 즐길 수 있는 과자들이 마련되어 있습니다.

제가 가보았을 때는 고장이 나있군요..



엘리베이터를 타고 2층에 가면 편의시설들을 이용하실 수 있습니다.



여느 호텔들과 마찬가지로 헬스장이 마련되어 있습니다.

여행 도중 스트레스를 운동을 하면서 풀어낼 수 있다는 점이 매력적입니다.



여느 헬스장에서와 같이 왠만한 운동기구들은 모두 갖추어져 있는것으로 보입니다.



운동하는 곳이니 만큼 식음대도 마련되어 있습니다.



2층에는 수영장 또한 마련되어 있습니다.



한편에는 유아용 수영장과 간이 사워실이 마련되어 있습니다.



수영장이니 만큼 바닥의 물 때문에 미끄러지는 사고를 조심해야겠지요?



수영장 내부 전경의 모습입니다. 생각보다는 많은 사람들이 들어와서 즐길만한 공간입니다.



수영한 후 몸을 닦을 수 있는 수건들도 이렇게 비치 되어 있습니다.



수영장에서 바라본 바깥의 풍경입니다. 성조기가 이 곳이 미국임을 느껴지게 합니다.







공항과 호텔 사이를 이동하는 셔툴버스입니다.



셔틀버스 뒷공간에 짐을 실을 수 있습니다. 캐리어는 운전기사가 직접 실어 정리합니다.



호텔을 떠나기 직전의 모습입니다.



셔틀버스 내부의 모습입니다.



호텔을 떠나면서 찍은 모습입니다. 다음에 기회가 된다면 한 번 더 이 호텔에서 묵었으면 좋겠군요.




호텔명 : Hilton San Francisco Airport Baypoint

주소 : 600 Airport Blvd Burlingame, CA 94010

전화번호 :  (650) 340-8500

300x250

실리콘벨리 탐방기록(7) - San Bruno에 위치한 한식 식당(고향 순두부)[2015.07.03]

 해외 여행을 하다 보면 가끔씩은 한국 음식이 그리울 때가 종종 있지요. 그래서 긴 여행을 떠나시는 분들이라면 여행 가방 안에 신라면을 챙겨시는 분들도 계시리라 생각합니다. 저도 외국에서 신라면을 먹어보니 신라면 특유의 매운맛이 나름 내가 살던 고향을 떠올리게 하는 명약같은 묘한 기분이 들기도 하지요.


 이번 포스팅에서는 실리콘벨리를 탐방하고 귀국하기 전에 나름 해외에서 한식을 먹어보는 것에 도전해 보기로 했습니다. 그렇게 인터넷을 찾아보던 도중 San Bruno에 한인 식당이 있다는 말을 듣고 찾아가 보았습니다!!



식당 입구에서 부터 친근한 한글 간판이 눈에 들어옵니다!



한국에서 보던 흔한 식당 느낌이 들면서도 약간은 이국적인 느낌이 드는 간판입니다.



식당 내부의 분위기는 제법 괜찮은 편입니다. 상당히 깔끔하군요.



한식 식당이라서 인근에 사는 한국인들만 이 식당에 찾아올 줄 알았는데

종종 서양인 분들도 눈에 띄더라고요!

외국인이 한식을 먹는 광경이 약간은 신기했습니다.



메뉴판입니다.

생각보다 다양한 메뉴들이 준비되어 있어 부담 없이 주문하실 수 있습니다.



이국에서도 한국의 술을 마실 수 있군요!

다만 해외이다 보니 가격은 많이 비싸군요.

술은 한국에 귀국해서 먹는 것으로 하죠 ㅎㅎ



보글보글 끓고 있는 부대찌개의 모습입니다.

재료는 미국 현지의 것을 쓰지만 맛 만큼은 한국의 부대찌개와 같아 마치 한국에 온 듯한 느낌이 듭니다!



일명 코리안 그릴이라는 이름으로 나온 돼지불고기!

한식 중에 불고기는 외국인의 입맛에도 상당히 잘 맞아서 한식 홍보에도 자주 등장하는 친구이지요!



해외에서 맛있는 한식 식당을 찾아보고 싶으신 분이라면 이곳 샌부르노에 있는 한인 식당을 적극 추천드립니다!




업소명 : 고향순두부

주소 : 617 San Mateo Ave. San Bruno, CA 94066

전화번호 : (650)873-3030



300x250

실리콘벨리 탐방기록(6) - UC버클리에 위치한 파리바게트[2015.07.03]

 해외를 여행하다보면서 가장 반가운 것들 중 하나를 들자면 우리나라에서 흔히 보았던 것을 해외에서 보게될 때가 아닌가 싶습니다. 특히 미국 현지에서 달리고 있는 현대자동차를 보면 감회가 새롭게 느껴지기도 합니다. 


 이번 포스팅에서는 미국 캘리포니아의 컴퓨터공학도의 요람이라 할 수 있는 UC버클리 인근에 위치한 파리바게트를 다루어보겠습니다.

UC 버클리 인근에 있는 파리바게트는 BART Berkeley역에서 내리자마자 바로 만나보실 수 있습니다.



가게 내부에도 익숙한 에펠탑 모양의 간판이 눈에 띄는군요.








매장의 전체적인 분위기는 우리나라의 파리바게트와 많이 비슷해 보입니다.

그래도 우리나라에서 보는 파리바게트 매장과는 묘하게 다른 부분도 있는데

그러한 부분을 찾아보는 것도 재미 중 하나라 볼 수 있겠습니다.




주소 : 2150 Shattuck Ave #110, Berkeley, CA 94704

영업시간 : 매일 10:00-22:00, 금토 10:00 - 23:00

300x250

실리콘벨리 탐방기록(5) - 산호세에 위치한 시크교 사원[2015.07.01]

 실리콘벨리를 탐방하던 도중 산호세 내에 5대 종교중 하나인 시크교 사원이 있다고 하여 찾아 가보게 되었습니다. 제게 있어 시크교는 상당히 낮선 종교이기도 한데 새로운 종교를 체험한다는 것이 한 편으로는 상당히 재미있는 일이더군요.



산호세 언덕 인근에 위치한 시크교 사원의 모습입니다.

약간은 중동 풍의 이미지가 느껴집니다.



사원 입구에서 바라본 산호세의 전경입니다.



안에 들어가기 전 천으로 자신의 머리카락을 완전히 가려야 합니다.

시크교도들은 자신의 수염도 깎지 않는다고 하던데

이야기를 들어보니 칼을 소지한 채로 비행기를 탈 수 있는 종교라고도 하더군요.



입구 앞에서는 tv를 통해 무언가가 표출되어 있는데

마치 기독교의 성경과 같은 것을 띄어놓은 것 처럼 보입니다.



건물 안에 들어가기 전 주의사항들이 적혀 있습니다.

가장 중요한 천을 싸는 부분이 설명되어있군요.



사원 내부의 모습입니다.

양쪽에 대형 스크린이 걸려있고 중앙에는 의식이 시작되는 듯 한 모습입니다.





시크교에 대해 자세한 내용을 알고 싶으신 분이라면 아래 링크를 참조해주셨으면 합니다.

https://ko.wikipedia.org/wiki/%EC%8B%9C%ED%81%AC%EA%B5%90




주소 : Sikh Gurdwara Sahib 3636 Murillo Ave San Jose, CA 95148

300x250