'분류 전체보기'에 해당되는 글 366건
- 2010.07.22 aidl과 원격 인터페이스 추가 정리 1
- 2010.07.16 WebView 6
- 2010.07.15 안드로이드 폰트 바꾸기 2
- 2010.07.06 Using WebView 3
- 2010.05.31 Gestures 2
- 2010.05.24 타이머 사용 시 주의사항 1
- 2010.05.19 Android Graphics #1 5
- 2010.05.17 WeakReference 와 SoftReference 좀 쉽게 정리
- 2010.05.17 Memory leak & Weak Reference 1
- 2010.05.04 app2sd 사용법 - 생각보다 간단함. 3
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
setContentView(webview);
// if there is an error loading this page (see below).
webview.loadUrl("http://slashdot.org/");
// OR, you can also load from an HTML string:
String summary = "<html><body>You scored <b>192</b> points.</body></html>";
webview.loadData(summary, "text/html", "utf-8");
// ... although note that there are restrictions on what this HTML can do.
// See the JavaDocs for
loadData()
and loadDataWithBaseURL()
for more info.// browser app does.
getWindow().requestFeature(Window.FEATURE_PROGRESS); // 타이틀바에 진행상태 아이콘표시모드로 변경한다.
webview .getSettings().setJavaScriptEnabled(true); // 자바스크립트를 활성화한다.
final Activity activity = this;
webview.setWebChromeClient(new WebChromeClient() { // WebChromeClient를 설정하고 진행상태 변동상태를 화면에 반영한다.
public void onProgressChanged(WebView view, int progress) {
// Activities and WebViews measure progress with different scales.
// The progress meter will automatically disappear when we reach 100%
activity.setProgress(progress * 1000);
}
});
webview.setWebViewClient(new WebViewClient() { // WebViewClient를 설정하여 에러발생시 토스트로 표시한다.
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
}
});
webview .loadUrl("http://slashdot.org/"); // 원하는 웹페이지를 연다.
createSnapshot() 현 페이지의 스크린샷 생성
getSettings() 설정을 조정하는 WebSettings 객체 반환
loadData() 브라우저에 주어진 문자열 데이터 로딩
loadDataWithBaseURL() 기준 URL을 사용해 주어진 데이터 로딩
loadUrl() 주어진 URL을 사용해 웹페이지 로딩
setDownloadListener() 사용자가 .zip이나 .apk 파일을 다운받는 경우 등의 다운로드 이벤트의 콜백 등록
setWebChromeClient() 제목이나 진행률 표시줄을 업데이트하거나 자바스크립트 대화상자를 여등 등, WebView 영역밖에서 실행되는 이벤트의 콜백을 등록
setWebViewClient() 리소스 로딩하기, 키 눌림, 인증 요청 등의 이벤트를 방해하도록 애플리케이션이 브라우저에 걸림돌을 설정함.
stopLoading() 현재 페이지 로딩 멈추기
개발Q&A 에 어떤분이 올리신 질문중에
"보낸이" 가 " 보냰이" 로 나온다고 하신 분이 계서서...
http://www.androidside.com/bbs/board.php?bo_table=B49&wr_id=6088&page=2
말이 나온김에 폰트에 관련된 이야기를 나누어 볼까 합니다.
먼저 폰트의 종류부터 알아보죠
비트맵 폰트
아주 옛날^^ 8비트 컴퓨터 시절 쓰이던 폰트죠
화면에 뿌려주는 처리속도는 매우 빠르지만 글씨의 크기가 변경 될때 이쁘게 보이질 않게 되죠
이미 크기가 정해져 비트맵으로 저장 되어 있기 때문인데요
c:\windows\fonts 에 보시면
이런 폰트들이 있으실텐데요 A 로 표시된 폰트가 비티맵 폰트입니다.
글씨가 커질수록 안 예쁘게 보여지는 단점이 있습니다.
외곽선 폰트
컴퓨팅 기술이 발전하면서 폰트도 이쁘게 표현할 필요가 생겼는데요
벡터 그래픽이나 베지어 곡선을 폰트에 적용하면서 외곽선 폰트 형태가 생겼습니다.
이 폰트는 크기에 상관 없이 일정한 품질의 출력을 하는 장점이 있습니다.
포스트 스크립트, 트루타입, 오픈타입 폰트등이 이에 속합니다.
포스트스크립트 (Postscript)
어도비사가 개발한 포스트스크립트 폰트는 Type1,2,3 등 다양한 포맷이 있는데요 Flash 에 적용되어
있던 벡터 개념을 폰트에도 적용하여 만든 것입니다 주로 프린터에 많이 사용됩니다.
TrueType 폰트
베지에 3차곡선을 사용하는 Type1과는 달리 베지에 2차곡선을 사용하지만 이에대한 폰트의
품질 향상은 미비하지만 속도는 빠른 장점 때문에 windows 에서 많이 사용되고 있습니다.
위 그림중 "T" 자로 되어 있는 폰트입니다.
오픈타입 폰트
어도비는 M$ 와 손잡고 오픈타입 폰트라는것을 제작 하게 되는데요.
true type 폰트와 postscript 폰트를 합친 새로운 형식입니다.
유니코드에 바탕을둔 truetype 에 opentype 공통의 헤더 정보를 더하여 확장시킨 포멧입니다.
유니코드이기 때문에 안드로이드에서 이 폰트를 사용하고 있습니다.
위 그림중 "O" 자로 되어 있는 폰트입니다.
서론이 너무 길었나요?
그럼 먼저 안드로이드 기본폰트에 대해 알아보죠
1. 기본폰트 사용하기
안드로이드 기본 폰트는 위 그림과 같이
Sans , Serif 두 종류가 있구요 mono 는 각 글자의 넓이가 일정한 폰트입니다.
layout 파일에
typeface="sans" 또는 "serif"
하시면 끝
2. 사용자폰트 사용하기
위에서 말씀드린바와 같이 windwos\fonts 에서 "O" 형태의 폰트를 고르신 후
assets\fonts\ 에 붙여넣기 합니다.
Activity onCreate 부분에
와 같이 하시면 됩니다.
실행 화면을 볼까요?
위 화면을 보신분 중 이러한 궁금증이 생기시는분이 분명 생기실껍니다.
"그럼 폰트를 일괄 적용은 못하나요?"
그래서 말씀 드리려구요.
3. Application 전체에 특정폰트 적용하기
res/values/styles.xml 에 위 내용을 입력합니다.
메니페스트 파일에 android:theme="@style/CustomTheme" 를 추가합니다.
끝
"그러면.. Application 전체에 커스텀폰트를 적용하려면 어떻게 하나요?"
제가 알고 있는바에 따르면 현재 버전에선 불가능 한것으로 알고 있습니다. ^^;
(완전 불가능한건 아니죠 안드로이드 core 소스를 구해서
특정폰트로 교체한 후 빌드 하면 될테니까요)
원문 : http://developer.android.com/resources/articles/using-webviews.html
링크 : http://silence2.tistory.com/entry/Using-WebViews
번역 : 이상훈 (calm1979@gmail.com)
2010년 6월 30일
WebViewDemo는 어플리케이션에 웹 컨텐츠를 붙일 수 있는 방법을 보여주는 간단한 어플리케이션이다. 이것은 apps-for-android 프로젝트에서 찾을 수 있다. 이 어플리케이션은 액티비티 내에 WebView를 붙일 수 있는 방법과 어플리케이션과 웹 컨텐츠 사이에 서로 통신할 수 있는 방법을 보여준다.
WebView는 브라우저와 동일한 렌더링 엔진과 자바 스크립트 엔진을 사용하지만, 당신의 어플리케이션의 제어하에 동작한다. WebView는 전체 화면으로 동작할 수도 있고, 다른 View들과 섞여서 동작할 수도 있다. WebView의 컨텐츠는 어디서든지 올 수 있다. WebView는 웹에서 컨텐츠를 다운로드 하거나, 당신의 assets 디렉토리 내에 저장된 로컬 파일에서 컨텐츠를 얻을 수도 있다. 심지어 어플리케이션 코드 상에서 동적으로 생성된 컨텐츠를 사용할 수도 있다. 이 예제에서는 demo.html 이라는 로컬 파일을 표시한다.
이 어플리케이션은 많은 것을 하지는 않는다: 당신이 안드로이드를 클릭하면 팔을 들어줄 뿐이다.
물론 이것은 간단한 자바 스크립트로 처리할 수 있다. 하지만, WebViewDemo는 WebView의 매우 강력한 두 가지 기능을 알려주기 위해 약간 복잡한 방법을 사용했다.
첫 째, WebView 내에 돌고 있는 자바 스크립트에서 액티비티의 코드를 호출할 수 있다. 당신은 이런 것을 자바 스크립트가 새로운 액티비티를 시작하는 액션을 발생시키거나, 데이터베이스나 ContentProvider에서 데이터를 얻어내는 경우 등에 사용할 수 있다. 이를 위한 API는 매우 간단하다: WebView의 addJavascriptInterface()만 호출하면 된다. 자바 스크립트에 노출시킬 메소드를 가진 객체와 자바 스크립트에서 호출할 때 사용할 이름을 넘겨주면 된다. 정확한 문법은 WebViewDemo.java에서 확인할 수 있다. 여기에서는 자바 스크립트에서 "window.demo"로 호출해서 사용될 DemoJavascriptInterface 객체를 만들었다.
둘 째, 액티비티에서 자바 스크립트 메소드를 호출할 수 있다. 이것은 모두 loadUrl 메소드를 통해 적절한 자바 스크립트를 호출 해야한다.
1 |
mWebView.loadUrl( "javascript:wave()" ); |
WebViewDemo 는 두 가지 기술을 사용하고 있다: 당신이 안드로이드를 클릭하면, 액티비티로 호출을 하고 다시 자바 스크립트를 호출한다. WebViews는 매우 강력하고, 어플리케이션을 만드는데 충분히 도움이될 만한 툴이다. - 특히 많은 양의 HTML 컨텐츠를 가지고 있는 경우에 도움이 될 것 이다. 마침, 우리는 우리가 만들고 있는 어플리케이션의 일부에서 정확히 이 방식을 사용했었다.
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
당신의 앱에 제스쳐를 추가하는 것은 쉬우면서도 가치있는 추가기능이 될 수 있다. 현재는 아주 쉬운 입력만 가능하나 활용하기에 따라 여러가지 모양을 인식하게 할수도 있다.. 알아서 잘 활용하길..
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.
}
final Launcher launcher = mLauncher.get(); // 사용시에는 get을 호출하여 null을 반드시 체크하여 사용
if (launcher != null) {
launcher.loadWallpaper();
}
}
확실한건 vm 이 OutOfMemoryError 를 발생시키기 전에는 Soft reference 만 남아있는 객체가 gc의 대상이 된다는 점 정도고, 그 이외에는 vm 의 구현에 따라 다를 수 있다.
출처 : http://skyswim42.egloos.com/3677368
참조 : http://www.ibm.com/developerworks/kr/library/j-jtp11225/#3.0
2005 년 11 월 22 일
자바 언어의 프로그램은 이론적으로는 "메모리 누수"에 대한 면역성이 있지만, 어떤 경우에는 프로그램의 논리적 상태의 일부가 더 이상 아닌데도 객체가 가비지 컬렉팅(garbage collected)이 되지 않은 상황이 있을 수 있다. 이번 달, Brian Goetz는 의도하지 않은 객체유지에 대해 설명한다.
가비지 컬렉터가 프로그램에서 더 이상 사용되지 않는 객체들을 처리하려면 객체의 논리적 수명(애플리케이션이 객체를 사용하는 시간)과 그 객체에 대한 레퍼런스의 실제 수명이 같아야 한다. 대부분의 경우 이 일은 자동으로 처리되기 때문에 우리가 객체의 수명 문제까지 신경 쓰지 않아도 된다. 하지만 가끔씩은 우리가 예상했던 것 보다 훨씬 긴 시간동안 메모리에 객체를 보유하고 있는 레퍼런스를 만들 때가 있다. 이 상황을 의도하지 않은 객체유지(unintentional object retention)라고 한다.
의도하지 않은 객체 유지의 가장 일반적인 원인은 Map
을 사용하여 메타데이터와 임시 객체들을 연결하는데 있다. 예를 들어, 클라이언트로부터의 소켓 연결 같은 중간 수명(메소드 호출 보다는 길지만 애플리케이션 보다는 짧은 수명)을 가진 객체가 있다고 가정해 보자. 몇 개의 메타데이터를 그 소켓과 연결해야 한다 그 당시에는 Socket
이 만들어진다는 사실을 모르고, Socket 클래스나 인스턴스를 제어할 수 없기 때문에 Socket
객체로 데이터를 추가할 수 없다. 이 경우, 전형적인 방식은 그와 같은 정보를 글로벌 Map
에 저장하는 것이다. Listing 1의SocketManager
클래스를 보자.
Listing 1. 글로벌 Map을 사용하여 메타데이터와 객체 제휴하기
public class SocketManager { private Map<Socket,User> m = new HashMap<Socket,User>(); public void setUser(Socket s, User u) { m.put(s, u); } public User getUser(Socket s) { return m.get(s); } public void removeUser(Socket s) { m.remove(s); } } SocketManager socketManager;socketManager.setUser(socket, user); |
이 방식의 문제점은 메타데이터의 수명은 소켓의 수명과 연관되어 있는데, 개발자가 정확히 언제 소켓이 더 이상 프로그램에서 필요하지 않은지를 알고, Map
에서 상응 매핑을 제거해야 한다는 것을 기억하지 못하면, Socket
과 User
객체들은 Map
에 영원히 머무르게 된다는 점이다. 이것 때문에 Socket
과 User
객체들이 가비지 컬렉팅이 되지 못한다. 바로 이것이 꽤 오랫동안 메모리에서 프로그램이 실행되는 원인이 된다. 프로그램에서 더 이상 Socket
이 필요하지 않을 때를 찾아내는 것은 수동적인 메모리 관리를 필요로 하므로 성가시면서도 에러를 일으킬 소지가 있는 기술이다.
프로그램이 메모리 누수 현상을 겪고 있다는 첫 번째 신호는 시스템이 OutOfMemoryError
를 던지거나 빈번한 가비지 컬렉션으로 인해서 퍼포먼스가 나빠지기 시작하는 것이다. 다행히도 가비지 컬렉터는 메모리 누수를 진단할 때 사용할 수 있는 많은 정보들을 공유하고 있다. -verbose:gc
또는 -Xloggc
옵션으로 JVM을 호출하면 GC(garbage collection)가 실행하면서 진단 메시지를 콘솔 또는 로그 파일에 프린트 한다. 걸리는 시간, 현재 힙 사용, 복구된 메모리 등을 프린트한다. GC 사용을 기록하는 것은 그렇게 매력적인 일은 아니기 때문에 메모리 문제를 분석하거나 가비지 컬렉터를 튜닝해야 할 경우 제품에서 기본적으로 GC 로깅을 실행하는 것이 바람직하다.
툴들은 GC 로그 아웃풋을 취해 이를 그래픽으로 디스플레이 한다. 그 툴 중 하나가 프리 JTune(참고자료)이다. GC 후에 힙 사이즈의 그래프를 보면 프로그램의 메모리 사용 내용을 볼 수 있다. 대부분의 프로그램의 경우 메모리 사용을 두 개의 컴포넌트 (기본(baseline) 사용과 현재 로드(current load) 사용)로 나눌 수 있다. 서버 애플리케이션의 경우, 기본(baseline) 사용은 어떤 부하에도 종속되지는 않지만 요청을 받아들일 준비가 될 때 애플리케이션이 기본적으로 사용하는 것이다. 현재 로드 사용은 요청을 처리하는 프로세스에 사용되지만 요청 프로세싱이 완료되면 사라지는 것이다. 부하가 일정하면 애플리케이션은 일정하게 메모리를 사용한다. 애플리케이션이 초기화를 완료하고 부하가 증가하지 않는데도 메모리 사용 추세가 계속 상승되면, 이 프로그램은 아마도 이전 요청을 처리하는 과정에서 만들어진 객체를 보유하고 있는 것이 분명하다.
Listing 2는 메모리 누수가 있는 프로그램이다. MapLeaker
는 쓰레드 풀에서 태스크룰 처리하고 각 태스크의 상태를 Map
에 기록한다. 안타깝게도 태스크가 완료될 때 엔트리를 삭제하지 않기 때문에 상태 엔트리와 태스크 객체가(내부 상태와 함께) 영원히 축적된다.
Listing 2. Map 기반 메모리 유출을 가진 프로그램
public class MapLeaker { public ExecutorService exec = Executors.newFixedThreadPool(5); public Map<Task, TaskStatus> taskStatus = Collections.synchronizedMap(new HashMap<Task, TaskStatus>()); private Random random = new Random(); private enum TaskStatus { NOT_STARTED, STARTED, FINISHED }; private class Task implements Runnable { private int[] numbers = new int[random.nextInt(200)]; public void run() { int[] temp = new int[random.nextInt(10000)]; taskStatus.put(this, TaskStatus.STARTED); doSomeWork(); taskStatus.put(this, TaskStatus.FINISHED); } } public Task newTask() { Task t = new Task(); taskStatus.put(t, TaskStatus.NOT_STARTED); exec.execute(t); return t; } } |
그림 1은 GC 후에 MapLeaker
에 대한 애플리케이션 힙 사이즈를 나타낸 그래프이다. 그래프가 계속 올라가고 있다는 것은 메모리 누수의 증거이다. (실제 애플리케이션에서, 이 슬로프는 이런 양상을 띄지는 않는다. 하지만 GC 데이터를 오랫동안 모으면 이런 모습이 된다.)
그림 1. 지속적으로 상승하는 메모리 사용
메모리 누수를 감지했다면 어떤 유형의 객체들이 문제를 일으키는지를 찾아낸다. 어떤 메모리 프로파일러로 객체 클래스로 나뉘어진 힙의 스냅샷을 만들 수 있다. 상용 힙 프로파일링 툴도 사용할 수 있지만 메모리 누수를 찾는데 돈까지 들일 필요는 없다. 빌트인 hprof
툴이 이 트릭을 수행한다. hprof
를 사용하여 메모리 사용을 트래킹하도록 하려면 -Xrunhprof:heap=sites
옵션으로 JVM을 호출한다.
Listing 3은 애플리케이션의 메모리 사용 부분을 나타내는 hprof
아웃풋이다. 애플리케이션이 종료한 후 또는 Windows상에서 Ctrl+Break을 누르거나 kill -3
으로 애플리케이션에 신호를 보낼 때 hprof
툴은 아웃풋을 만든다.
Listing 3보기. (Listing 3. Map.Entry
와 Task
객체에서 증가 추세를 보이는 hprof
아웃풋)
Listing 4는 hprof
아웃풋의 또 다른 부분을 보여준다. Map.Entry
객체용 할당 사이트용 콜 스택 정보를 제공한다. 이 아웃풋은 어떤 호출 체인이 Map.Entry
객체들을 생성하고 있는지를 말해준다. 프로그램 분석과 더불어 이것은 매우 쉽게 메모리 누수의 원인을 알아낼 수 있는 방법이다.
Listing 4. Map.Entry 객체용 할당 사이트를 보여주는 HPROF 아웃풋
TRACE 300446: java.util.HashMap$Entry.<init>(<Unknown Source>:Unknown line) java.util.HashMap.addEntry(<Unknown Source>:Unknown line) java.util.HashMap.put(<Unknown Source>:Unknown line) java.util.Collections$SynchronizedMap.put(<Unknown Source>:Unknown line) com.quiotix.dummy.MapLeaker.newTask(MapLeaker.java:48) com.quiotix.dummy.MapLeaker.main(MapLeaker.java:64) |
SocketManager
의 문제는 Socket
-User
매핑의 수명이 Socket
의 수명과 매치해야 하는데 이 규칙을 실행할 쉬운 방식을 제공하는 언어가 없다는 점이다. 프로그램은 수동 메모리 관리와 비슷한 기술에 의존하게 된다. 다행히도 JDK 1.2 부터, 가비지 컬렉터가 약한 참조(weak references)를 통해 이러한 유형의 메모리 누수를 방지하도록 한다.
약한 참조는 레퍼런트(referent)라고 하는 객체 레퍼런스를 위한 홀더(holder)이다. 약한 참조를 사용하여 레퍼런트에 대한 참조를 관리할 수 있다.(가비지 컬렉팅이 가능하다.) 가비지 컬렉터가 힙을 트레이스 할 때, 객체에 대한 대표적인 레퍼런스가 약한 참조라면 레퍼런트는 GC의 대상이 된다. (약한 참조에 의해 유일하게 참조되는 객체를 weakly reachable이라고 한다.)
약한 참조의 레퍼런트는 구현 시 설정되고 이 값이 제거되지 않았다면 이 값은 get()
으로 검색된다. (레퍼런트가 이미 가비지 컬렉팅 되었기 때문이거나 누군가가WeakReference.clear()
를 호출하여) 약한 참조가 제거되면 get()
은 null
을 리턴한다. 따라서 get()
이 비 null 값을 리턴하는지 언제나 검사해야 한다. 레퍼런트는 결국 가비지 컬렉팅 될 것이기 때문이다.
일반 참조(strong reference)를 사용하여 객체 레퍼런스를 복사할 때 레퍼런트의 수명이 최소 복사된 레퍼런스의 수명만큼 되도록 제한한다. 객체에 대해 약한 참조를 구현하면 레퍼런트의 수명을 확장하지 않는다. 그저 이것이 살아있는 동안 만 관리하면 된다.
약한 참조는 약한 컬렉션(weak collection)들을 구현할 때 가장 유용하다. 이것은 정확히 SocketManager
클래스가 해야 할 일이다. 일반적인 약한 참조 사용이기 때문에, 키에 약한 참조를 사용하는 WeakHashMap
도 역시 JDK 1.2에 있는 클래스 라이브러리에 추가되었다. 일반 HashMap
에 키로서 객체를 사용하면 그 객체는 Map
에서 매핑이 제거될 때 까지 컬렉팅 되지 않는다.WeakHashMap
을 사용하여 객체가 가비지 컬렉팅 되는 것을 방지하지 않고도 Map
키로서 객체를 사용할 수 있다. Listing 5는 WeakHashMap
에서의 get()
메소드의 가능한 구현이다. (약한 참조를 보여준다.)
Listing 5. WeakReference.get()의 구현
public class WeakHashMap<K,V> implements Map<K,V> { private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> { private V value; private final int hash; private Entry<K,V> next; ... } public V get(Object key) { int hash = getHash(key); Entry<K,V> e = getChain(hash); while (e != null) { K eKey= e.get(); if (e.hash == hash && (key == eKey || key.equals(eKey))) return e.value; e = e.next; } return null; } |
WeakReference.get()
이 호출되면(여전히 살아있다면) 레퍼런트에 대한 일반 참조를 리턴하기 때문에 while
루프의 바디에서 사라지는 매핑에 대해 걱정할 필요가 없다. 일반 참조는 가비지 컬렉팅 되는 것으로부터 이를 방지한다. WeakHashMap
의 구현은 약한 참조를 가진 일반 이디엄을 의미한다. 몇몇 내부 객체는 WeakReference
를 확장한다.
WeakHashMap
에 매핑을 추가하면 키가 가비지 컬렉팅 되기 때문에 나중에 이 매핑은 빠질 수 있다. 결과적으로 get()
은 null
을 리턴하면서 null
용 get()
의 리턴 값을 테스트하는 것이 더 중요해진다.
SocketManager
에서의 유출을 픽스하는 것은 쉽다. HashMap
을 WeakHashMap
으로 대체하면 된다.(Listing 6) (SocketManager
가 쓰레드 보안이 되어야 한다면 WeakHashMap
을Collections.synchronizedMap()
으로 래핑할 수 있다.) 매핑의 수명이 키의 수명과 연결되어야 할 때마다 이 방식을 사용할 수 있다. 하지만 이 기술을 남용하지 않도록 조심한다. 대부분의 경우에는 일반 HashMap
은 올바른 Map
구현으로서 사용하는 것이 옳기 마련이다.
Listing 6. SocketManager를 WeakHashMap으로 픽스하기
public class SocketManager { private Map<Socket,User> m = new WeakHashMap<Socket,User>(); public void setUser(Socket s, User u) { m.put(s, u); } public User getUser(Socket s) { return m.get(s); } } |
WeakHashMap
은 맵 키를 보유하기 위해 약한 참조를 사용한다. 따라서 키 객체들이 애플리케이션에서 더 이상 사용되지 않을 때 이것을 가비지 컬렉팅 되도록 하고, WeakReference.get()
이null
을 리턴하는지의 여부에 따라 get()
구현은 죽은 매핑과 살아있는 매핑을 구별한다. 하지만 이것은 get()
의 메모리 사용이 애플리케이션의 수명 동안 늘어나는 것을 방지하는데 필요한 것의 절반 정도에 지나지 않는다. 어떤 것은 키 객체가 컬렉팅 된 후 Map
에서 죽은 엔트리를 없애는데도 수행될 수 있다. Map
은 상응하는 죽은 키들에 대한 엔트리들로 채운다. 그리고 이것이 애플리케이션에 보이지 않는 동안 Map.Entry
와 값 객체가 컬렉팅되지 않도록 하기 때문에 메모리 밖에서 애플리케이션을 실행시키는 원인이 된다.
주기적으로 Map
을 스캐닝하고, 각각의 약한 참조에 get()
을 호출하고 get()
이 null
을 리턴하면 그 매핑을 제거하는 방식을 통해 죽은 매핑이 제거될 수 있다. 약한 참조의 레퍼런트가 가비지 컬렉팅될 때 공지가 될 수 있는 방법이 있다면 좋을 것이다. 바로 이 일을 레퍼런스 큐(reference queues)가 수행한다.
레퍼런스 큐는 정보를 객체의 라이프 사이클 정보를 애플리케이션에 피드백하는 가비지 컬렉터의 기본적인 수단이다. 약한 참조에는 두 개의 구조체가 있다. 하나는 인자로서 레퍼런트만 취하고 다른 하나는 레퍼런스 큐를 취한다. 약한 참조는 관련 레퍼런스 큐와 함께 생성되고 레퍼런트가 GC의 후보가 되면 레퍼런스 객체(레퍼런트가 아님)는 레퍼런스가 깨끗해진 후에 레퍼런스 큐에 인큐(enqueue) 된다. 이 애플리케이션은 레퍼런스 큐에서 레퍼런스를 검색하여 레퍼런트가 컬렉팅 되었다는 것을 알면 관련 클린업 액티비티를 수행할 수 있다. 약한 컬렉션에서 제거된 객체들의 엔트리를 삭제한다. (레퍼런스 큐는 BlockingQueue
와 같은 큐 해제 모드를 제공한다.)
WeakHashMap
은 대부분의 Map
연산 중에 호출되는 expungeStaleEntries()
이라고 하는 프라이빗 메소드를 갖고 있다. 이것은 종료된 레퍼런스용 레퍼런스 큐를 폴링하고 관련 매핑을 제거한다. Listing 7에 expungeStaleEntries()
의 구현이 나와있다. 키-값 매핑을 저장하는데 사용되는 Entry
유형은 WeakReference
를 확장하여 expungeStaleEntries()
가 그 다음의 종료된 약한 참조를 요청할 때 Entry
를 가져온다. 콘텐트를 주기적으로 트롤링 하는 대신 Map
을 청소하기 위해 레퍼런스 큐를 사용하는 것이 더 효율적이다. 살아있는 엔트리들이 클린업 프로세스에서 절대 건드리지 않기 때문이다. 실제로 인큐(enqueued) 레퍼런스가 있을 때만 수행된다.
Listing 7. WeakHashMap.expungeStaleEntries() 구현
private void expungeStaleEntries() { Entry<K,V> e; while ( (e = (Entry<K,V>) queue.poll()) != null) { int hash = e.hash; Entry<K,V> prev = getChain(hash); Entry<K,V> cur = prev; while (cur != null) { Entry<K,V> next = cur.next; if (cur == e) { if (prev == e) setChain(hash, next); else prev.next = next; break; } prev = cur; cur = next; } } } |
약한 참조와 약한 컬렉션은 힙 관리에 있어 강력한 툴이다. 애플리케이션이 일반 참조의 "모 아니면 도" 식의 접근이 아닌 보다 세련된 개념의 접근 방식을 사용할 수 있기 때문이다. 다음 달에는 소프트 레퍼런스(soft references)를 연구해 보자
출처 : http://www.androidpub.com/307179
1. SD 카드 파티션 나누기 (fat32 / ext2로 나눔)
ext2 파티션은 500메가 정도가 적당합니다.
윈도우에서 하실분은 파라곤 파티션 메니져 프로그램으로 가능합니다.
2. 루팅 및 busybox 설치
3. 마운트 후 어플을 SD 카드로 옮기기
ex) 깔려있는 모든 어플을 SD카드로 옮길때
busybox mount -t ext2 /dev/block/mmcblk0p2 /system/sd
busybox cp -a /data/app /system/sd
busybox rm -rf /data/app
busybox ln -s /system/sd/app /data/app
4. 이후 재 부팅시에는 마운트를 다시 해줘야함.
부팅 지연 시는 usb 연결 후 쉘에서 수동 마운트 하면됩니다.
=======================
수동마운트가 좀 걸리긴 하지만 한번 설정하고 계속 충전하면서 쓰면 별 문제 없을 듯 합니다.