Android/정리2010. 5. 31. 14:39

Gestures

터치스크린은 모바일장치에서 애플리케이션과 연동하기 위한 대단한 방법이 있다. 터치스크린을 통해 사용자는 tab, drag, fling또는 slide 통해 애플리케이션을 빠르게 액션을 수행할 있다. 개발자를 위해서 안드로이는 framework swite같은 간단한 액션을 인식할 있는 방법을 제공한다. 하지만 복잡한 제스쳐는 다루기 어렵다 간혹 개발자는 이런 복잡한 처리를 위해 추가적으로 많은 코딩을 해야 수도 있다.

Android 1.6(Donut)에서부터 새로운 제스쳐API 제공한다. Android.gesture 패키지가 제공되며, 인식가능한 제스쳐들을 store, load, draw할수 있다. 문서는 패키지의 사용방법을 설명한다. 시작하기 전에 예제를 먼저 다운받아라(download the source code of the examples).

Creating a gestures library

Android 1.6이후 버전에는 Gestures Builder라는 어플이 설치된다. 어플로 미리정의된 당신만의 제스쳐를 만들 있다. 예제가 함께 제공되는데 제스쳐셋을 만들 있다. 아래 그림은 가지 제스쳐를 추가한 이후의 화면을 보여준다.

위에서 보는바와 같이 제스쳐는 연관된 이름을 갖는다. 이름은 앱에서 각각 제스쳐를 구분하는데 사용되므로 아주 중요하다. 이름이 유일할 필요는 없다. 동일명하에 여러가지 제스쳐가 사용될 있다. 제스쳐빌더를 이용하여 제스쳐를 추가하거나 수정할 있으며 SD Card gestures폴더에 저장된다. 파일에는 제스쳐에 대한 정보가 저장되어 있으며 앱에서 /res/raw폴더에 위치시켜서 사용할 있다.

Loading the gestures library

이제 미리정의된 제스쳐를 갖게 되었다. 이제 에서 반드시 로드하여 사용해야 한다. 몇가지 방법이 있으며 가장 간단한 방법은 GestureLibraries 클래스를 사용하는 방법이다.

mLibrary = GestureLibraries.fromRawResource(this, R.raw.spells);
if (!mLibrary.load()) {
    finish
();
}

예에서, /res/raw/spells파일로부터 제스쳐가 로드되었다. SD card같은 다른 소스로부터 로드할 수도 있으며 앱에서 파일을 저장할 수도 있으며 변경할수 없더록 읽기전용으로 로드할수도 있다. 아래는 하나의 라이브러리의 구조를 다이어그램으로 보여준다.

Recognizing gestures

To start recognizing gestures in your application, all you have to do is add a GestureOverlayView to your XML layout:

앱에서 제스쳐를 인식하기 위해서 해야 하는 일은 XML layout GestureOverlayView 추가하는 뿐이다.

<android.gesture.GestureOverlayView
   
android:id="@+id/gestures"
   
android:layout_width="fill_parent"
   
android:layout_height="0dip"
   
android:layout_weight="1.0" />

GestureOverlayView 일반적인 android.widget package 일부가 아니란걸 알아야 한다. 그러므로 full name 사용해야 한다. Gesture overlay 사용자가 제스쳐를 그리는 영역에서 동작한다. 몇가지 visual 속성들을 변경할 있다. 색상이나 stroke 두께, 그리고 액션이 진행동안의 사용되는 다양한 listener등을 변경할 있다. 가장 일반적으로 많이 사용되는 listener GestureOverlayView.OnGesturePerformedListener이다. gesture 그려질때마다 호출된다.

GestureOverlayView gestures = (GestureOverlayView) findViewById(R.id.gestures);
gestures
.addOnGesturePerformedListener(this);

Listener 호출될 GestureLibrary 제스쳐를 인식하기 위한 질의를 보낼 있다. 리턴값에서 예측된값들의 리스트와 score 얻게 될것이다. 리스트는 core 역순으로 정렬되어 있다. 가장 고득점에 해당하는 gesture 사용자가 그리길 원하는 것일것이다. 아래 코드는 첫번째 예측값의 이름을 검색하는 방법을 보여준다.

public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
   
ArrayList<prediction> predictions = mLibrary.recognize(gesture);

   
// We want at least one prediction
   
if (predictions.size() > 0) {
       
Prediction prediction = predictions.get(0);
       
// We want at least some confidence in the result
       
if (prediction.score > 1.0) {
           
// Show the spell
           
Toast.makeText(this, prediction.name, Toast.LENGTH_SHORT).show();
       
}
   
}
}

예제에서는 예측값이 1.0이상인경우만 고려하였다. 1.0이하는 낮은 일치율을 의미하며 코드가 앱에서 미리정의된 제스쳐를 인식하는 코드의 전부이다.

 

Gestures overlay

예제코드에서 GestureOverlayView LinearLayout 안에 들어간 일반적인 뷰로 사용되었다. 하지만 이름에서 느껴지는 처럼, 다른뷰들의 최상위에 overlay 사용될 수도 있다. 이는 게임이나 앱의 어느곳에서든 사용될 있는 유용한 방법이다. 두번째 데모인 GetstureListDemo에서는 Contacts list 최상위에 overlay 생성할것이다. 그리고 Getsture builder 새로운 제스쳐를 정의할 것이다.

And here is what the XML layout looks like:

<android.gesture.GestureOverlayView
   
xmlns:android="http://schemas.android.com/apk/res/android"
   
android:id="@+id/gestures"
   
android:layout_width="fill_parent"
   
android:layout_height="fill_parent"
   
   
android:gestureStrokeType="multiple"
   
android:eventsInterceptionEnabled="true"
   
android:orientation="vertical">

   
<ListView
       
android:id="@android:id/list"  
       
android:layout_width="fill_parent"
       
android:layout_height="fill_parent"  />

</android.gesture.GestureOverlayView>

앱에서 일반적인 ListView 최상위에 gesture overlay 있다. 그리고 overlay에는 전에 본적없는 속성들이 몇가지 있다.

·         gestureStrokeType: single stroke 사용할지 또는 multiple stroke 사용할 지정한다.

·         eventsInterceptionEnabled: true 설정하면 gesture 처리할 있는 child view 있는 경우 child 이벤트가 전달되도록 한다. Scroll 가능한 view 있는경우 유용하며 scroll이벤트와 충돌이 발생하지 않는다.

·         orientation: scroll orientation 지정한다. 여기서는 vertical 하였으며 이것은 가로제스쳐인 action_delete 바로 제스쳐로 인식될수 있음을 의미한다. 세로 제스쳐의 경우 반드시 가로제스쳐를 하나이상 가져야만 인식이 가능하게 된다. 다른 의미로 세로로 그려진 라인은 스크롤과 오인될 있으므로 이런 옵션이 필요하게 된다.

·          

Gesture library load하고 setup하는것과 overlay 설정하는 것은 위에서 설명한 코드와 동일하다. 오직 한가지 다른 점은 이제 예측의 이름값을 이용하여 무엇을 했는지 알아낸다는 것이다.

public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
   
ArrayList<Prediction> predictions = mLibrary.recognize(gesture);
   
if (predictions.size() > 0 && predictions.get(0).score > 1.0) {
       
String action = predictions.get(0).name;
       
if ("action_add".equals(action)) {
           
Toast.makeText(this, "Adding a contact", Toast.LENGTH_SHORT).show();
       
} else if ("action_delete".equals(action)) {
           
Toast.makeText(this, "Removing a contact", Toast.LENGTH_SHORT).show();
       
} else if ("action_refresh".equals(action)) {
           
Toast.makeText(this, "Reloading contacts", Toast.LENGTH_SHORT).show();
       
}
   
}
}

사용자는 이제 리스트의 위에 스크롤과 충돌없이 제스쳐의 입력이 가능해진다.

Overlay 세로스크로크만 입력했을 경우 반투명한 컬러로 표시하여 유효하지 않은 제스쳐임을 표시한다.

It's your turn

당신의 앱에 제스쳐를 추가하는 것은 쉬우면서도 가치있는 추가기능이 될 수 있다. 현재는 아주 쉬운 입력만 가능하나 활용하기에 따라 여러가지 모양을 인식하게 할수도 있다.. 알아서 잘 활용하길..

 

Posted by 삼스
Android/정리2010. 5. 19. 18:00

Graphics

Android 그래픽은 2D graphic library OpenGL 이용한 3D그래픽이 준비되어 있다. 2D graphic drawable package 공통으로 사용되는 API들이 정의되어 있으며, 3D graphic Khronos OpenGL ES package Android OpenGL utility들로 구성되어 있다.

Android graphics are powered by a custom 2D graphics library and OpenGL ES 1.0 for high performance 3D graphics. The most common 2D graphics APIs can be found in the drawable package. OpenGL APIs are available from the Khronos OpenGL ES package, plus some Android OpenGL utilities.

Canvas 사용하는 방법은 View 구조에서 어떻게 그려야 하는지에 대해 좋은 정보를 제공할것이다.

 

Consider your Options

2D 그래픽 그리기

1.     Layout View 객체에 graphic이나 animation 그린다. 그리기 작업은 시스템의 일반적인 View구조의 그리기 순서에 따라 그려지게 된다. 간단하게 View내부에 graphic 정의한다.

2.     Canvas 직접 그린다. 해당 클래스의 draw() 호출하거나 Canvas draw…() 호출하여 그림

 

1 View 그리기는  가장 편하고 좋은 선택이다. Simple Graphics Inside a View. <- 참조

2 Canvas 그리기는 정기적으로 다시 그리기가 필요한 경우에 좋은 선택이다. Video game들은 Canvas 그리기 방법을 사용한다.

·         UI Activity 동일한 스레드상에서 layout custom View component 생성되어 있는 경우 invalidate() 호출하면 onDraw()콜백이 동작한다.

·         또는 별도의 스레드의 경우 동일스레드에서 처리하는 만큼 Canvas 그리기를 수행하고 SurfaceView 다루는 것이 빠르다. ...Begin by reading Draw with a Canvas.

Simple Graphics Inside a View

단순한 그래픽(이미지, 모양, 색상, 미리만들어진 애니매이션등..) 그리고자 한다면 View 백그라운드를 그리고 layout안에 ImageView content영역에 그리기만 하면 된다. 그렇다면 문서를 건너뛰고 어떻게 그래픽과 애니매이션을 그리는지 배우길 바란다(2D Graphics 참조)

Draw with a Canvas

별도의 그리기를 구현하고자 한다면 Canvas 이용해서 그려야 한다.

onDraw()콜백메소드에 Canvas객체가 제공되며 이를 이용하여 그릴수 있다. SurfaceHolder.lockCanvas 통해 canvas 얻을수도 있으며 이는 SurfaceView객체로 다룰 가능하다.

Canvas 별도로 새로 생성하고자 할때는 반드시 실제로 그려지게될 Bitmap 정의해야 한다. Bitmap Canvas 항상 필요한 존재이다. 아래와 같이 새로운 Canvas 셋업할수 있다.

Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);

이렇게 생성된 canvas Canvas.drawBitmap(Bitmap, …)메소드중 하나를 이용하여 다른 canvas 복사할 있다. View.onDraw() SurfaceHolder.lockCanvas() 통해 얻어진 Canvas 통해서 그리는 것을 권장한다.

drawBitmap(…), drawRect(…), drawText(…) 많은 함수 지원. 다른 클래스들도 draw()메소드를 지원한다. Drawable클래스 같은 경우 자신의 draw()메소드를 지원하여 Canvas 인자로 사용되어 그려질 있다.

On a View

어플리케이션이 프로세싱이 많거나 frame-rate속도를 요하지 않는 경우(chess game같은 느린 애니매이션을 처리하는 게임등), View.onDraw()에서 Canvas 그리는 customView 만들어 사용할 있다. 이렇게 하면 안드로이드 프레임워크에서 제공하는 Canvas 이용하여 쉽게 구현가능하다.

1.     View class extend한다.

2.     onDraw() callback 정의한다. -> 메소드는 프레임워크에 의해 뷰가 그려져야 호출될것이다. draw관련 처리를 하는 지점이며 Canvas객체가 넘겨져 와서 객체를 다루면 된다.

안드로이드 프레임워크는 필요할때만 onDraw() 호출한다. 그려져야 하는 싯점이 되면 애플리케이션에서는 invalidate() 호출함으써 다시 그려지게 있다.

View component onDraw()에서는 Canvas객체를 사용하여 drawing 처리한다. Canvas draw…() canvas 넘겨져온 클래스의 draw()메소드가 호출되어 drawing 완성된다. 완성이 되면 프레임워크는 시스템에 의해 처리되는 비트맵을 그리기 위해 canvas 사용할것이다.

Note:  main activity thread 아니라 다른 thread에서 다시그리기를 수행하려면 postInvalidate() 호출해야 한다.

View class 확장에 대한 안내는 Building Custom Components  참조하라.

For a sample application, see the Snake game, in the SDK samples folder: <your-sdk-directory>/samples/Snake/.

On a SurfaceView

SurfaceView View구조내에서 surface 그리는 목적으로 고안된 특별한 subclass이다. 목적은 애플리케이션의 두번째 스레드에서 별도로 그려서 시스템의 View구조에서 그리기 타임까지 기다리지 않도록 하게 하기 위해 고안되었다. 두번째 thread에서 참조하는 SurfaceView 자신만의 Canvas 그리기를 수행한다.

1.     SurfaceView extend 새로운 클래스를 선언한다.

2.     SurfaceHolder.callback implement한다. 콜백 subclass 하부의 Surface 대한 정보와 함께 통지하는데 생성, 변경 또는 제거되었을 통지하게 된다. 이벤트를 통해서 언제 그리기를 시작해야 하는지 알수 있고, 새로운 surface속성에 맞게 조정하고, 언제 그리기를 종료하고 테스트를 죽일지등을 알수 있다. SurfaceView에서 두번째 Thread 정의하여 모든 drawing procedure 수행하는 것이 좋다.

3.     SurfaceHolder 생성한다. Surface 객체를 직접 처리하지 않고 SurfaceHolder 통해서 하는 것이 좋다. SurfaceView 초기화되면 getHolder() SurfaceHolder 얻는다.

4.     addCallback() 호출하여 SurfaceHolder.Callback으로부터 이벤트를 수신하도록 한다. SurfaceHolder.Callback SurfaceView클래스내에서 override하여 addCallback()한다.

5.     두번째 thread에서 Surface Canvas draw하려면 반드시 SurfaceHandler 전달해야 하며 lockCanvas() canvas 얻어야 한다. 이렇게 해야 SurfaceHolder 제공해준 Canvas 얻게 된다.

6.     Canvas drawing 한번 수행하면 canvas object 인자로 unlockCanvasAndPost() 호출해야 한다. lockCanvad() unlockCanvasAndPost() draw 반드시 쌍으로 호출되어야 한다.

 

Note: 한번 canvas 얻어져서 사용되면 canvas 이전상태를 유지한다. 따라서 적절하게 animate 하려면 surface전체를 re-paint해야 한다. 경우 drawColor() drawBitmap()등으로 background 갱신하면 된다. 그렇게 하지 않으면 이전 이미지가 표시될 것이다.

 

For a sample application, see the Lunar Landar game, in the SDK samples folder: <your-sdk-directory>/samples/LunarLander/. Or, browse the source in the Sample Code section.

 

Posted by 삼스
Android/정리2010. 4. 21. 11:52

효율적인 모바일앱의 작성은 항상 쉽지 않다. 특히, 안드로이드처럼 Dalvik garbage collector 의해 자동으로 메모리가 관리되는 경우 충분히 주의하지 않으면 성능이슈가 발생할 수밖에 없다

성능에 민감한 코드들(layout생성, view시스템의 그리기, 게임의 로직부분)은 필연적으로 비용을 초래한다. 너무 많은 메모리 할당후에는 당신의 앱은 메모리를 해재하도록 잠시 멈출수 있다. 대부분의 경우 사용자가 느끼지 못할정도로 gc는 이루어지는 반면에 리스트를 scroll하거나 게임에서 적을 물리치거나 하는 경우 심각한 성능과 응답성의 저하를 느낄수 있다. 100~200ms정도 gc시간이 걸리는 것은 일반적인 상황은 아니다. 부드러운 애니매이션의 경우 각 프레임이 16~33ms정도의 속도가 필요하다. 초당 10프레임정도로만 떨어져도 사용자는 문제를 느끼게 된다.

대개의 경우 gc는 tons of small, short-lived object, 일부 garbage collector때문에 발생한다. 그리고 그런 object의 collection을 최적화할 수 있으며 그렇게 해서 애플리케이션의 좀더 덜 방해받게 할 수 있다. 안드로이드의 garbage collector는 불행히도 이러한 성능최적화등을 할 수 없다. 

잦은 gc를 피해가도록 해주기 위해, 안드로이드 SDK는 allocation tracker라는 툴을 포함시켰다. DDMS의 일부기능으로 되어 있으며 디버깅목적으로 이미 사용하고 있다. 이 툴을 사용하려면 SDK에 포함되어 있는 DDMS를 먼저 실행한다. 이클립스버전의 DDMS에는 아직 지원되지 않는다.

DDMS가 실행되면 해당 애플리케이션 프로세스를 선택하고 Allocation Tracker탭을 클릭하라. 새로운 뷰에서 Start Tracking을 클릭하면 분석하고자 하는 애플리케이션을 선택한다. 준비가 되었다면 Get Allocations를 선택하가. 할당된 object들이 첫번째 테이블에 리스트될것이다. 보여지는 한 line을 선택하면 두번째 테이블에 애플리케이션에 할당된 stack이 보여진다. 어떤 타입의 object들이 할당되었는지는 물론이고 포함된 스래드, 클래스, 파일의 라인까지 표시된다. 아래 스크린샷은 Shelves앱의 listView를 스크롤하는 동안의 할당상태를 보여준다.

성능에 영향을 주는 부분을 모두 제거할 필요가 없다고 하더라도 코드에서 중요한 이슈를 확인하는데 도움이 될것이다.  예를 들면 내가 보아온 많은 애플리케이션에서 draw시마다 매번 Paint 객체를 새로 생성한다. 이 Paint객체를 하나의 인스탄스로 관리하는 것 만으로 성능이슈를 간단히 개선할 수 있다. 나는 Android source code를 살펴볼것을 강력하게 권고한다. 이유는 성능을 끌어올리기 위해 어떤 기술을 사용하였는지 볼수 있기 때문이다. 그리고 객체의 재사용에 대한 안드로이드가 제공하는 API들을 발견하게 될것이다.


Posted by 삼스
Android/정리2010. 4. 14. 16:52

Android2.0(API level5)부터 멀티어카운트와 다른 데이터소스로부터 다루고 통합하는 개선된 Contacts API 제공한다. 여러소스들로부터 오버랩되는 데이터를 다루기 위해 contacts content provider 유사한 contacts들을 모은다 그리고 사용자에게 하나의 엔티티로 제공한다. 문서는 contacts 다루기 위한 새로운 방법에 대해 기술한다.

새로운 Contacts API android.provider.ContactsContract 이와 관련된 클래스들로 정의된다. 예전 API 여전히 지원되나 사용이 권장되지는 않는다. 이전 API 작성된 애플리케이션을 가지고 있다면 Considerations for legacy apps문서를 참조하라.

새로운 API 예전 API 모두 사용하는 예제를 참조하고 싶다면 Business Card sample application 참조하라.

Data structure of Contacts

새로운 API에서 data 3개의 primary table 나뉜다. Contacts, raw contacts그리고 data 구성되며 이는 여러 개의 contacts소스로부터 정보를 다루고 저장하기 좋은 구조로 만들어져 있다.

Data raw contact 연관된 모든 종류의 데이터가 저장된 일반적인 테이블이다. row 해당종류의 데이터가 저장된다(ex: name, photo, email address, phone number, group membership). row 데이터의 종류를 파악하기 위한 MIME type으로 tag된다. Column 그것이 가지고 있는 타입이 row 저장되어있는 데이터의 종류에 따라  결정된다. ) 특정 row data종류가 Phone.CONTENT_ITEM_TYPE이라면, 첫번째 column 전화번호를 가지게 되고, data종류가 Email.CONTENT_ITEM_TYPE이라면 column에는 email주소가 저장된다.

ContactsContract.CommonDataKinds클랙스는 contacts data 해당하는 MIME type들에 대응되는 서브클래스들을 제공한다. 필요하다면 data row 해당하는 새로운 MIME type 정의하여 사용이 가능하다. 많은 정보를 원하면  android.provider.ContactsContract.Data 보라.

RawContacts table 하나의 row Data 집합과 인물에 대한 설명과 하나의 contacts source 연결된 정보를 표현한다. 예를 들면, 하나의 row Google이나 Exchange 계정 또는 facebook 친구와 연관된 정보를 정의한다. 많은 정보는 ContactsContract.RawContacts 살펴보라.

Contacts테이블내의 하나의 row 동일한 인물(또는 엔티티) 설명하는 하나이상의 합쳐진 RawContacts 표현한다.

위에서 언급한대로 Contact content provider 자동으로 Raw Contacts 합쳐서 하나의 Contact 엔트리에 넣는다. 취합로직이 Contacts row내의 엔트리들을 다루기 때문에 엔트리들은 읽을수 있지만 수정되어선 안된다. 아래 Aggregation of contacts 섹션을 보면 어떻게 취합이 이루어지는지에 대한 많은 정보를 얻을 있다.

것은 통합되어 제공되므로 사용자에게 contacts 보여질 하부의 다양한 소스로부터 contact들의 뷰가 취합되어진다. 이는 Contacts level에서 이루어진다.(When displaying contacts to users, applications should typically operate on the Contacts level, since it provides a unified, aggregated view of contacts from various underlying sources)

Example: Inserting a Phone Number

전화번호를 새로운 API 사용해서 추가하려면, 전화번호를 추가할 Raw Contact ID 필요할것이다. Data row 생성할 필요가 생긴다 :

 

import android.provider.ContactsContract.CommonDataKinds.Phone;
...
ContentValues values = new ContentValues();
values
.put(Phone.RAW_CONTACT_ID, rawContactId);
values
.put(Phone.NUMBER, phoneNumber);
values
.put(Phone.TYPE, Phone.TYPE_MOBILE);
Uri uri = getContentResolver().insert(Phone.CONTENT_URI, values);

Aggregation of contacts(contacts 취합)

사용자가 여러 개의 소스로부터 contacts 동기화할 , 일부 contact들은 동일인물이나 엔티티에 해당하는 경우가 있다. 예를 들어 “Bob Parr” 사용자의 동료이거나 친구일수 있다. 따라서 사용자는 그의 회사 이메일계정과 개인계정정보 두가지를 모두 contact 정보로 갖기를 원할것이다. 사용자에게 단순화된 뷰를 제공하려면 시스템은 겹쳐지는 contact들을 배치하고, 하나로 합치고 취합한다.

시스템은 디폴트로 자동으로 contact들을 취합한다. 하지만 필요한 경우 당신의 애플리케이션은 시스템이 취합하는 방법에 관여하거나 모두함께 취합되지 않도록 할수 있다.

Automatic aggregation

Raw contact 추가되거나 수정되면, 시스템은 취합되어질 매치되는(겹쳐지는)raw contact들을 찾는다. 하나도 매치되는 raw contact들을 찾지 못할 수도 있다. 때는 새로운 raw contact 생성될것이다. 하나가 검색되었다면 두개의 raw contact 갖는 하나의 Contact 새로 생성된다. 그리고 여러 개의 raw contact 검색되었다면 가장 매치가 잘되는 raw contact 선택하게 된다.

2개의 raw contact 아래의 요구 적어도 하나이상 일치하면 매치된것으로 간주된다.

  • 이름이 같은경우
  • 이름이 동일한 단어로 순서만 다른 경우(ex, “Bob Parr” “Parr, Bob”)
  • 공통된 short name 갖는 경우(ex, “Bob Parr” “Robert Parr”)
  • First name last name 하나만 갖고 있으며 다른 raw contact 동일한 경우, 룰은 느슨한편이다. 그래서 두개이 raw contact 폰번호, 이메일주소 또는 애칭을 공유하는 경우에만 적용된다.(ex, Hellen [“elastigirl]”=Helen Parr[“elastigirl”])
  • 둘중 하나가 name정보가 모두 없는 상태에서 전화번호, 이메일주소, 또는 애칭을 공유하고 있는 경우(ex, Bob Parr [incredible@android.com] = incredible@android.com)

 

이름을 비교할 시스템은 대소문자와 음성구분마크(diacritical mark)(Hélène=Helene) 구분하지 않는다. 전화번호를 구분할 “*”, “#”, “(“, “), “ “ 특수문자는 비교하지 않는다. 또한 두개의 번호가 국가코드만 다르고 나머지는 동일하다면 두가지를 동일한 번호로 인식한다(일본 국가코드만 제외됨)

자동 취합은 영속적이지 않으며 하나의 구성하는 raw contact 변경이 새로운 취합을 생성하기도하고 존재하는 취합을 중단시킬 있다.(Automatic aggregation is not permanent; any change of a constituent raw contact may create a new aggregate or break up an existing one.)

Explicit aggregation(명시적 취합)

어떤 경우에는 시스템의 자동 취합기능이 당신의 애플리케이션이나 sync adapter 요구사항에 부합되지 않는경우가 있을 있다. 그래서 명시적으로 취합을 제어할 있는 두가지 종류의 API set 있다. Aggregation mode(취합모드) 자동취합행위자체를 제어할수 있으며 aggregation exception 자동화된 취합자체를 오버라이드할수 있다.

Aggregation modes(취합모드)

당신을 raw contact 각각에 대해 aggregation mode 설정할 있다. 이렇게 하기 위해서 모드상수값을 RawContact row column으로 추가한다. 모드상수는 아래와 같이 정의되어 있다:

·         AGGREGATION_MODE_DEFAULT 일반모드, 자동취합기능 동작.

·         AGGREGATION_MODE_DISABLED 자동취합기능 꺼짐. raw contact 취합되지 않는다.

·         AGGREGATION_MODE_SUSPENDED 자동취합기능이 비활성화된다. 취합모드가 suspene 변경될 이미 취합중인 상태였다면, 취합중인 내용은 계속 유지된다.

 

Aggregation exceptions(취합 예외)

두개의 raw contact 무조건 합치거나 떨어뜨려 놓으려면, ContactsContract.AggregationException(contact2.db agg_exceptions테이블명으로 있슴)테이블에 row 하나 추가할 있다. 여기에 추가된 예외는 모든 자동 취합룰에 대해 오버라이드된다.

 

Loookup URI

새로운 Contacts API contact 검색하기 위해 lookup key 개념을 소개한다. 당신의 애플리케이션이 contact 참조를 관리할 필요가 있다면 전통적인 row id대신에 lookup key 사용해야 한다. 당신은 contact로부터 lookup key 가져올 있다. 이는 ContactsContract.Contacts 테이블에 하나의 칼럼(contact2.db 경우 “lookup”필드명으로 있슴)으로 있다. Lookup key 가져오면 아래의 방법으로 UIR 구성할 있다.

Uri lookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey)

그리고 아래와 같이 전통적인 방법처럼 URI 사용할 있다.

Cursor c = getContentResolver().query(lookupUri, new String[]{Contacts.DISPLAY_NAME}, ...);
try {
    c
.moveToFirst();
   
String displayName = c.getString(0);
} finally {
    c
.close();
}

이렇게 복잡해진 이유는 contact row ID 근본적으로 volatile하기 때문이다. 당신의 애플리케이션이 아주 ID 가진 contact 저장했다고 해보자, 그러면 사용자는 수동으로 해당 contact 다른 contact join한다. 이제 개로 사용되는 하나의 contact 있다. 그리고 ID 저장된 contact 아무데도 없다. <- 이게 먼소린지???

The reason for this complication is that regular contact row IDs are inherently volatile. Let's say your app stored a long ID of a contact. Then the user goes and manually joins the contact with some other contact. Now there is a single contact where there used to be two, and the stored long contact ID points nowhere.

Lookup key 이런 케이스의 해결책을 제시한다. 키값은 raw contacts들의 server-side id값들을 이어붙인 문자열이다. 당신의 애플리케이션에서는 임의 contact 찾기 위해 문자열을 사용할 있다. 이는 해당 raw contact 취합여부에 상관없이 사용이 가능하다.

만일 당신의 애플리케이션은 성능을 중시한다면 lookup long ID 모두 저장할 있으며 ID값을 벗어난 lookup URI값을 아래와 같이 만들어낼 있다.If performance is a concern for your application, you might want to store both the lookup and the long ID of a contact and construct a lookup URI out of both IDs, as shown here:

Uri lookupUri = getLookupUri(contactId, lookupKey)

URI 두개의 ID 모두 표현되어 있다면, 시스템은 long ID 먼저 사용한다. 그렇게 하면 아주 빠른 쿼리가 가능하다. Contact 검색되지 않거나 검색된 정보가 잘못된 lookup key 가지고 있으면, content provider lookup key 파싱하고 생성된 raw contact들을 추적(?)한다. 만일 애플리케이션이 contacts bulk-process 한다면, ID 모두 다루어야 하고 반대로 사용자 액션당 하나의 contact 대한 작업을 한다면, long ID 저장할 필요가 없을 것이다.

안드로이드는 스스로 contact 참조할 (shortcut, Quuick Contact, contact 또는 수정중인 경우) 생기면 lookup URI 사용한다.

contact 단순히 보고 있는중에 contact ID 변경 되는것인가? 그것은 백그라운드로 동기화가 진행중이거나 화면에 보여지는 중에 다른 항목과 취합될수 있기 때문이다.

 

정리하면 contact 참조할 필요가 생기면 무조건 lookup URI 사용할것을 추천하는 바이다.

 

 

Considerations for legacy applications(이전 애플리케이션에 대한 고려)

이전 API 사용하는 애플리케이션이 있다면 새로운 API 업그레이드를 고려해야 한다. 가지 방법이 있다:

·         내버려 두고 Contacts 호환성모드에 기대는 .

·         업그레이드를 진행하는 , 그리고 Android2.0 이전 플랫폼에 대한 지원을 중단하는 .

·         이전 버전을 계속 유지한 상태에서 버전을 작성하는

·         배포된 platform 맞는 올바른 API 사용한 어플을 만드는

·          

Using compatibility mode

호환성 모드는 가장 쉬운 방법이다. 그리고 public API만을 사용하는 안드로이드 2.0에서도 동작해야 한다. Non-public API 사용하는 예제는 내부 쿼리들에서 명시적인 테이블명을 사용하고 컬럼을 사용한다그것은 Contacts class public상수로 정의되어 있지 않다.

이런 애플리케이션이 현재도 사용중임에도 불구하고 이렇게 계속 내버려두고 싶지은 않을것이다. 왜냐하면 오직 하나의 계정에만 접근이 가능하기 때문이다. 그것도 처음 지정된 구글계정에만. 만일 사용자가 다른 계정을 열거나 다른 구글계정으로 변경한다면 이런 애플리케이션들은 계정에 접근할 수가 없게 된다.

 

Upgrading to the new API and dropping support for older platforms

애플리케이션이 Android 2.0보다 오래된 플랫폼을 이상 지원하지 않는다면 아래와 같은 방법으로 업그레이드 가능하다.

l  API Contacts 모든 사용처를 변경한다. 모두 마친 후에는 애플리케이션 빌드중에 발생하는 모든 사용되지 않음 경고는 무시한다. 애플리케이션은 다중계정과 Android 2.0 다른 특성들의 장점을 모두 지원하게 될것이다.

l  Manifest파일에 <uses-s> 엘리먼트에 android:minSdkVersion속성을 업데이트한다. API 사용하려면 API level ‘5’이상을 설정해야 한다.

 

Maintaining two applications

Android 2.0이전과 이후 버전을 모두 지원하는 애플리케이션을 만들고자 한다면, 다음과 같은 작업을 해야 한다.

l  기존 어플 복제

l  이전 어플 수정

n  어플 적재시 SDK버전을 확인해야 . 버전정보는 android.os.Build.VERSION.SDK. 값을 확인하면 .

n  버전이 맞지 않으면 마켓으로 새로운 어플을 설치할 있도록 팝업을 띄워주는 것이 좋다.(see Using Intents to Launch Market).

l  어플 수정

n  API 호출부분 모두 수정

n  AndroidManifest.xml수정

u  애플리케이션이름과 패키지이름을 새로 부여하라. 현재 안드로이드 마켓은 동일 이름/패키지로 두개의 어플리케이션 설치를 지원하지 않는다.

u  android:minSdkVersion 수정

l  두개의 어플을 모두 마켓에 발표한다. 하나는 업그레이드버전이고 하나는 새로운 버전이다. 간략하게 차이점에 대한 설명을 달아야 한다.

 

방법은 두가지 단점이 있다.

u  어플은 이전 어플의 데이터를 읽을수 없다. 어플의 데이터는 동일한 패키지에 해당하는 코드에서만 접근 가능하다.

u  업그레이드 과정이 아주 사용자에게 투박하다. 일부 사용자는 두개를 사용하거나 모두 삭제할수도 있다.

 

Supporting the old and new APIs in the same application

이것은 아주 사소한 트릭이지만 노력에 비해 결과는 훌룡하다. 하나의 패키지에 모든 플랫폼에서 동작하게 있다.

기존 어플의 모든 Contacts 사용하는 코드를 모아서 하나의 클래스로 만들어라. 예를 들면 아래와 같이 하면된다.

ContactAccessor라는 abstract class 만들고 이를 확장하여 old API 억세스하는 class new API 억세스하는 class 만든다. 그러고 SdkVersion 따라 적절한 class 생성하여 미리정의된 interface method 호출하여 사용하도록 한다.

기존 코드는 아래와 같다.

    protected void pickContact() {
        startActivityForResult
(new Intent(Intent.ACTION_PICK, People.CONTENT_URI), 0);
   
}

코드를 아래와 같이 변경하여 사용할 있도록 한다.

    private final ContactAccessorOldApi mContactAccessor = new ContactAccessorOldApi();

   
void pickContact() {
        startActivityForResult
(mContactAccessor.getContactPickerIntent(), 0);
   
}

ContactAccessorOldApi getContactPickerIntent()이다.

    public Intent getContactPickerIntent() {
       
return new Intent(Intent.ACTION_PICK, People.CONTENT_URI);
   
}

이제 ContactAccessor 정의하고 이를 확장하여 Old API용과 New API 클래스를 정의한다.

    public abstract class ContactAccessor {
       
public abstract Intent getContactPickerIntent();
       
...
   
}

 

    public class ContactAccessorNewApi extends ContactAccessor {    
       
@Override
       
public Intent getContactPickerIntent() {
           
return new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
       
}
       
...
   
}

ContactAccessor에서 SdkVersion 따라 분기하여 적절한 class 리턴하는 singletone 함수를 만든다.

    private static ContactAccessor sInstance;

   
public static ContactAccessor getInstance() {
       
if (sInstance == null) {
           
String className;
           
int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
           
if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
                className
= "ContactAccessorOldApi";
           
} else {
                className
= "ContactAccessorNewApi";
           
}
           
try {
               
Class<? extends ContactAccessor> clazz =
                       
Class.forName(ContactAccessor.class.getPackage() + "." + className)
                               
.asSubclass(ContactAccessor.class);
                sInstance
= clazz.newInstance();
           
} catch (Exception e) {
               
throw new IllegalStateException(e);
           
}
       
}
       
return sInstance;
   
}

이제 아래와 같이 ContactAccessor 생성하여 SdkVersion 상관없이 사용할 있는 객체를 완성하였다.

    private final ContactAccessor mContactAccessor = ContactAccessor.getInstance();

 

Posted by 삼스
Android/정리2010. 4. 13. 13:31

Window Backgrounds & UI Speed

일부 안드로이드 어플리케이션들은 UI toolkit 뛰어넘는 성능을 요구되며 그렇게 하기 위한 가지 방법이 있다. 문서에서 당신은 그리기와 액티비티가 화면에 보여지는 시간을 빠르게 하는 방법을 발견하게 될것이다. 두가지 기술은 모두 윈도우의 백그라운드 그리기(window’s background drawable) 특성에 기인한다.

Window background 다소 오해하게 하는 용어이다. 당신이 setContentView() Activity에서 호출하여 UI 설정하면 안드로이드는 Activity 윈도우에 당신의 뷰를 추가한다. 하지만 윈도우에는 당신이 추가한 뷰만 있는 것이 아니다. 여기서 가장 중요한 하나는 뷰구조의 가장 상위에는 DecorView 있다는 것이다.

A typical Android view hierarchy

DecorView 실제로 윈도우의 background drawable 가지고 있는 뷰이다. 당신의 액티비티에서 getWindow().setBackgroundDrawable() 호출함으로써 DecorView background drawable 변경하게 된다. 이는 현재 구현된 안드로이드에 아주 국한된 내용이며 향후 버전이나 디바이스에서 변경될수 있다.

당신이 표준안드로이드 테마를 사용한다면 디폴트 background drawable 당신의 액티비티에 셋팅된다. T-Mobile G1에서 현재 ColorDrawable 사용되고 있다. 대부분의 애플리케이션에서 이미지는 동작하고 남겨진다. 이는 반대로 당신의 어플리케이션에 그리기 성능에 영향을 줄수 있다. 항상 전체화면에 불투명한 그림을 그리는 예제가 있다.

An opaque user interface doesn't need a window background 

그림은 ImageView 의해 커버되는 윈도우의 배경이 없는 스크린샷이다. 애플리케이션은 최대한 빠르게 다시그리기를 하도록 구현되었다. 44 fps 속도로 그려지고 있다. 이와 같이 속도를 개선하기 위한 쉬운방법은 background drawable 제거하는것이다. UI 불투명하기 때문에 배경을 그리는 것은 낭비적이다. 따라서 배경의 제거는 성능향상에 효과적이다.

Remove the background for faster drawing

새버전의 애플리케이션은 51fps까지 향상되었다. 개선된 차이는 T-Mobile G1 메모리버스의 속도 향상으로 쉽게 설명된다.

커스텀테마를 사용하여 배경을 제거하는 것을 쉽게 있다. 먼저 res/values/themes.xml 만들고 아래와 같이 내용을 추가하라.

<resources>
   
<style name="Theme.NoBackground" parent="android:Theme">
       
<item name="android:windowBackground">@null</item>
   
</style>
</resources>

그러고 android:theme=”@style/Theme.NoBackground”속성을 당신의 <activity/> <application/>테그에 추가하면 된다. 크릭은 MapView, WebView fullscreen 사용하는 어플에서 많이 사용된다.

불투명 뷰와 안드로이드 : 최적화방식은 안드로이드 UI툴킷이 현재 불투명한 자식뷰들에 의해 숨겨진 뷰의 그리기에 대해 충분히 스마트하지 않기 때문에 필요하다. 이런 최적화가 구현되지 않은 주된 이유는 간단하다 안드로이드 애플리케이션내에서 불투명한 뷰는 아주 조금 사용되기 때문이다.

윈도우 배경을 변경하기 위해 테마를 사용하는 것은 액티비티의 시작을 인지하는 성능을 개선하기 위한 최고의 방법이다. 트릭은 커스텀 백그라운드(texture logo) 사용하는 액티비티에만 적용이 가능하다. Shelves애플리케이션이 좋은 예제가 된다.

Textured backgrounds are good candidates for window's background

애플리케이션시 나무느낌배경을 XML이나 onCreate에서 설정하면 사용자는 어두운 배경이나 디폴트테마를 가지고 시작될것이다. 나무느낌배경은 콘텐트뷰의 inflation이후에 표시될것이며, 첫번째 layout/drawing 수행될것이다. 이것은 부작용을 야기하고 애플리케이션이 로드되는데 시간이 걸린다는 느낌을 받게 될것이다.

반면에 애플리케이션이 테마에 나무느낌배경을 정의하면 애플리케이션이 시작되자마자 시스템에 의해 로드된다. 사용자는 디폴트테마를 절대로 볼수 없으며 애플리케이션이 로드중이라는 느낌도 받지 않을것이다. 메모리와 디스크의 사용을 제한하기 위해 res/drawable/background_shelf.xml 정의되어 연결된다:

<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
   
android:src="@drawable/shelf_panel"
   
android:tileMode="repeat" />

drawable 테마에서 참조된다.

<resources>
   
<style name="Theme.Shelves" parent="android:Theme">
       
<item name="android:windowBackground">@drawable/background_shelf</item>
       
<item name="android:windowNoTitle">true</item>
   
</style>
</resources>

동일한 트릭이 T-Mobile G1 탑재된 Google Map애플리케이션에도 사용되었다. 애플리케이션이 적재되면 MapView tile 로딩되는 것을 바로 볼수 있다.

때때로 최고의 트릭은 또한 최고로 단순한것이다. 따라서 당신이 불투명한 UI 커스텀배경을 사용하는 액티비티를 만들어야 윈도우배경을 변경해야 함을 기억하라.

Download the source code of the first example.

Download the source code of Shelves.

 

Posted by 삼스
Android/정리2010. 4. 10. 08:09

Avoiding Memory Leaks

Android app들은 적어도 T-Mobile G1에서 16MB heap size제약이 있다. 이것은 phone을 위해서는 많은 양이면서 개발자에게는 아주 작은양이다. 대부분의 메모리를 사용할 생각이 아니더라도 다른 app들을 죽이지 않을 정도로 가능한 아주 작은 양의 메모리를 사용해야 한다. 더 많은 app들이 메모리에 유지되고 그들의 app들간에 전환이 일어날것이다. 내작업의 일부로써 메모리누수이슈가 속출할것이며 대부분의 시간을 이런 실수(하나의 Context에 오랬동안 레퍼런스를 유지하는것)때문에 보내게 될것이다.

Android에서 Context는 resource를 load하고 access하는것을 제외한 많은 작업에 이용된다. 이것은 모든 widget
들이 생성자에서 Context 파라메터를 인자로 받는 이유이기도 하다. 일반적인 Android app에서 Activity와
Application이라는 2개의 Context를 가진다. 이것은 보통 class와 method에 전달하는 첫번째 파라메터이다.

@Override
protected void onCreate(Bundle state) {
 
super.onCreate(state);
 
 
TextView label= new TextView(this);
  label
.setText("Leaks are bad");
 
  setContentView
(label);
}

위코드는 view가 전체액티비티에 하나의 레퍼런스를 가지고 있고 액티비티의 모든게 그안에 유지된다는것을 의미한다(모든 뷰구조와 그 리소스들), 그러므로 만일 Context가 누수(누수는 레퍼런스를 하나 유지하여 GC를 방지하는것을 의미한다)가 발생하면 당신은 많은 양의 메모리를 잃게된다. 전체 액티비티를 읽는것은 당신이 주의하지 않으면 아주 쉽게 일어날수 있다.

디폴트로 화면이 전환되면 현재 액티비티는 제거되고 그 상태가 저장된 상태로 새로운 액티비티가 다시 생성된다. 이런 방식으로 안드로이드는 리소스들을 다시 로드하여 UI를 다시 구성한다. 당신이 로테이션시마다 다시 로드되지 않기를 원하는 아주 큰 비트맵을 사용하는 어플리케이션을 작성한다고 상상해보라.  아주 쉬운 방법은 static field를 사용하는 것이다.

private static Drawable sBackground;
 
@Override
protected void onCreate(Bundle state) {
 
super.onCreate(state);
 
 
TextView label= new TextView(this);
  label
.setText("Leaks are bad");
 
 
if (sBackground== null) {
    sBackground
= getDrawable(R.drawable.large_bitmap);
 
}
  label
.setBackgroundDrawable(sBackground);
 
  setContentView
(label);
}

이 코드는 아주 빠르지만 또한 아주 잘못된 코드이다. 처음 액티비티가 생성되어 처음 로테이션될때 leak이 발생된다. Drawable이 뷰에 첨부될때 view는 drawable에 callback으로 설정된다. 위 코드상에서 drawable은 TextView의 참조를 가지고 있다. TextView는 액티비티의 참조를 가지고 있다.

이 예제는 Context의 누수의 아주 단순한 케이스중 하나이며 Home screen소스상에서(unbindDrawables()메소드를 찾아보라) 저장된 drawable의 callback에서 액티비티가 제거될때 null로 초기화하는 것을 볼수 있다.  충분히 흥미진진하고 누수된 context의 연결고리를 생성할수 있는 여러 군데가 있으며 그것은 좋지 않은 방법이다. 그것들은 당신을 오히려 더 빠르게 메모리부족을 야기시킨다.

2가지의 아주 쉬운 context와 연관된 메모리누수를 피하는 방법이 있다. 가장 명백한 방법의 하나는 그 Context자신의 영역의 외부로 빠져나가는것을 완전히 피하는것이다. 위 코드는 static을 사용하는 경우를 보여주었다. 하지만 내부 class나 그것의 내부참조를 외부의 클래스에 참조하도록 하는것은 똑같이 아주 위험하다.
또한가지 방법은 Application context를 사용하는것이다. 이는 액티비티 생명주기와 관계없이 당신의 application이 살아있는동안 항상 유지된다. 만일 아주 오랬동안 유지되어야 하는 객체를 사용해야 한다면 applicaiton객체를 기억하라. Application context는 Context.getApplicationContext()나 Activity.getApplication()을 통해 아주 쉽게 얻을수 있다.

Context연관 메모리누수를 피하는 방법을 정리하겠다. 아래 사항을 기억하라.

  • 오랬동안 유지되어야 하는 레퍼런스는 Activity context에 유지하지 말아라(그것들은 액티비티와 동일한 생명주기를 갖게 된다.) - Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)
  • Application context를 사용할것을 시도하라 - Try using the context-application instead of a context-activity
  • 액티비티의 생명주기를 관리하지 않는다면 액티비티내에 non-static inner class를 사용하는것을 피하라. static inner class를 사용해야 하며 액티비티내에서 weak reference를 사용하라.  Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance
  • GC는 메모리 누수에 대해 아무런 보장을 하지 않는다. - A garbage collector is not an insurance against memory leaks

Posted by 삼스
Android/정리2010. 1. 21. 12:01
Activity의 메인 윈도우에 어떻게 on-screen soft keyboard가 동작할것인지에 대한 방법을 기술한다.
이 설정은 두가지에 대해 영향을 준다.
 1. soft keyboard의 상태 - activity가 사용자의 포커스를 받을때 보여질지 숨겨질지
 2. activity의 메인윈도우를 조정할지 - soft keyboard의 크기를 줄이거나 soft keyboard에의 해 덮어씌워질때 윈도우 사이즈를 조정하거나 한다.

아래 나열한 값들 중에 하나이상을 나열할 수 있으며 "state.."값하나와 "adjust.."값 하나씩 사용할 수 있다.
각 그룹에 여러개의 값을 지정하는것은 정의되어 있지 않으며 각 값은 |로 분리하여 나열할 수 있다.

stateUnspecified : hidden 또는 visible을 지정하지 않음. 시스템이 알아서 선택하거나 theme의 값을 참조한다.
stateUnchanged : activity가 foreground로 올때 마지막 상태(visible, hidden)를 유지하고 있는다.
stateHidden : activity 실행중에 hidden이다. that is, when the user affirmatively navigates forward to the activity, rather than backs into it because of leaving another activity.
stateAlwaysHidden : activity가 focus를 가지고 있으면 무조건 softkeyboard는 hidden으로 함.
stateVisible : 일반적인 모드로 동작
stateAlwaysVisible : activity 실행중에 visible이다.  that is, when the user affirmatively navigates forward to the activity, rather than backs into it because of leaving another activity.

adjustUnspecified : resize나 pan하지 않는다. 메인윈도우의 디폴트셋팅이다. 시스템은 자동으로 하나의 모드를 선택하는데 자신의 콘텐츠를 스크롤할 수 있는 layout view를 윈도우가 가지고 있는지에 따라 결정된다. 만일 그런 view라면 윈도우는 resize되어 더 작은 영역에서 모든 윈도우의 콘텐츠가 스크롤되어 표시될수 있어야 한다.
adjustResize : Activity의 메인윈도우가 softkeyboard에 따라 항상 resize된다. 
adjustPan : Activity의 메인윈도우가 resize되지 않고 pan된다. 따라서 keyboard에 의해 현재 포커스가 가려지지 않아서 사용자는 항상 타이핑결과를 볼수가 있다. resizing옵션보다 권장되지 않는데 사용자는 윈도우상에 보이지 않는 부분을 보기 위해 softkeyboard를 닫아야 하기 때문이다. 


화면의 하단에 EditText가 하나 있다고 가정후 두 옵션의 차이점은 아래와 같다.
 1. adjustResize : 메인윈도우 사이즈가 줄어든다, 하지만 EditText는 보이지 않는다. 스크롤이 지원되면 스크롤하여 볼수는 있다.
 2. adjustPan : 메인윈도우 사이즈가 줄어들지 전체적으로 않고 EditText가 보이도록 위로 올라간다. EditText는 보이나 다른 UI가 보이지 않는다.







Posted by 삼스
Android/정리2010. 1. 14. 13:00
1. ENG모드로 빌드하면 됨.
2. initramfs의 default.prop파일의 secure값이랑 adb값, debuggable값을 각각 0,1,1로 setting한 걸로 바꾸면 됨.
Posted by 삼스
Android/정리2010. 1. 13. 17:11
PowerManager는 Screen을 On/Off할때 WindowManager에 통지를 하고 이는 KeyguardViewMediator를 통해 LockScreen에도 전달된다.

PowerManagerService.mNotificationTask
    -> WindowManagerPolicy.screenTurnedOn()
        -> KeyguardViewMediator.onScreenTurnedOn();
    -> WindowManagerPolicy.screenTurnedOff()
        -> KeyguardViewMediator.onScreenTurnedOff();

Posted by 삼스
Android/정리2010. 1. 13. 16:24
Android concept이다.
Keyguard가 표시중이거나 Hidden일경우 PowerManager.enableUserActivity(true)에 의해 mUserActivityAllowed변수가 set되면서 PowerManager.userActivity(..)에서 setPowerState가 정상적으로 호출될 수 있다.
PowerManager.userActivity는 WindowManagerService.InputDispatcherThread에서 이벤트큐의 이벤트를 처리할때마다 호출된다. 따라서 PowerManager.enableUserActivity()로 screen timeout을 ON/OFF할 수 있다.

Keyguard에서 이를 이용해 LockScreen이 표시중에는 사용자 입력이 있더라도 무조건 5초후에 꺼지도록 되어있다.
KeyguardViewMediator.java
private void adjustUserActivityLocked() {
        // disable user activity if we are shown and not hidden
        if (DEBUG) Log.d(TAG, "adjustUserActivityLocked mShowing: " + mShowing + " mHidden: " + mHidden);
        boolean enabled = !mShowing || mHidden;
        mRealPowerManager.enableUserActivity(enabled);
        if (!enabled && mScreenOn) {
            // reinstate our short screen timeout policy
            pokeWakelock();
        }
    }


Posted by 삼스