WebView 사용시 안에 들어있는 이미지나 동영상 등의 크기를 폰의 해상도에 정확히 맞추어 보여주길 바랄 때가 많은데요 이를 위해서는 코드 하나만 추가해주면 됩니다.
 
webview.getSettings().setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);


위와 같이 WebView에 LayoutAlgorithm만 지정해주면 글자크기는 그대로 유지하면서 이미지나 동영상의 크기만 폰의 해상도에 딱맞게 조절되어 표시됩니다.


이것 땜에 며칠을 고생했는데 굉장히 단순한 방법이 있었네요..;; 한국어 웹페이지만 찾아봤던게 잘못이었어요..ㅠㅠ 앞으로는 영어페이지를 위주로 봐야할 거 같습니다. 안드로이드 개발 관련 국내 사이트들은 질문 올려도 답변도 잘 안달리고 태도들도 다들 까칠하고.. 정작 도움이 될 만한 팁도 별로 없는 거 같아요..-_-
신고
Posted by 플라스틱라디오
정확한 이유는 모르겠는데 최근에 안드로이드 프로젝트를 apk파일로 컴파일하려고 하니 string.xml 관련하여 Lint 에러가 발생하면서 컴파일이 되지 않더군요. 다국어지원 관련한 오류인 듯 한데 에러를 없앨 수가 없더라구요.. 개발하면서는 아무런 오류가 없었는데 말이죠..

이런 경우 아주 간단한 해결방법은 Lint 에러를 warning으로 바꾸어 주는 것입니다. Lint 관련 설정은 이클립스 메뉴의 Window - Preference - Android - Lint 에서 변경할 수 있는데요.. 여기서 MissingTranslation 항목을 찾아 Severity를 warning으로 변경하면 됩니다.
저작자 표시 비영리 변경 금지
신고
Posted by 플라스틱라디오
안드로이드 4.0 이후에서는 WebView에서 유튜브나 플래시 동영상의 전체화면 모드 사용시 NullPointerException이 발생합니다. 이를 해결하기 위해서는 WebView에 WebChromeClient 를 설정해주어야 합니다. 
하지만 단순히 기본 WebChromeClient 객체를 생성하여 WebView에 설정할 경우 오류는 발생하지 않지만 영상이 정상적으로 재생이 안되는 문제가 있습니다. 

전체화면 모드가 제대로 작동되게 하려면 아래와 같이 WebChromeClient 클래스를 상속받은 클래스를 만든 후 이를 WebView에 설정하면 됩니다.

 
public class FullscreenableChromeClient extends WebChromeClient {
    protected Activity mActivity = null;

    private View mCustomView;
    private WebChromeClient.CustomViewCallback mCustomViewCallback;
    private int mOriginalOrientation;

    private FrameLayout mContentView;
    private FrameLayout mFullscreenContainer;

    private static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

    public FullscreenableChromeClient(Activity activity) {
        this.mActivity = activity;
    }

    @Override
    public void onShowCustomView(View view, int requestedOrientation, WebChromeClient.CustomViewCallback callback) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            if (mCustomView != null) {
                callback.onCustomViewHidden();
                return;
            }

            mOriginalOrientation = mActivity.getRequestedOrientation();
            FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView();
            mFullscreenContainer = new FullscreenHolder(mActivity);
            mFullscreenContainer.addView(view, COVER_SCREEN_PARAMS);
            decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS);
            mCustomView = view;
            setFullscreen(true);
            mCustomViewCallback = callback;
            mActivity.setRequestedOrientation(requestedOrientation);
        }

        super.onShowCustomView(view, requestedOrientation, callback);
    }

    @Override
    public void onHideCustomView() {
        if (mCustomView == null) {
            return;
        }

        setFullscreen(false);
        FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView();
        decor.removeView(mFullscreenContainer);
        mFullscreenContainer = null;
        mCustomView = null;
        mCustomViewCallback.onCustomViewHidden();
        mActivity.setRequestedOrientation(mOriginalOrientation);
    }

    private void setFullscreen(boolean enabled) {
        Window win = mActivity.getWindow();
        WindowManager.LayoutParams winParams = win.getAttributes();
        final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN;
        if (enabled) {
            winParams.flags |= bits;
        } else {
            winParams.flags &= ~bits;
            if (mCustomView != null) {
                mCustomView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            } else {
                mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            }
        }
        win.setAttributes(winParams);
    }

    private static class FullscreenHolder extends FrameLayout {
        public FullscreenHolder(Context ctx) {
            super(ctx);
            setBackgroundColor(ctx.getResources().getColor(android.R.color.black));
        }

        @Override
        public boolean onTouchEvent(MotionEvent evt) {
            return true;
        }
    }
}




_mWebView.setWebChromeClient(new FullscreenableChromeClient(this));




출처 : http://stackoverflow.com/questions/12398565/android-ics-flash-webview-full-screen-throws-nullpointerexception
신고
Posted by 플라스틱라디오
안드로이드 기기는 다양한 해상도를 가지고 있기 때문에 안드로이드 앱 개발에 있어 해상도에 따른 레이아웃 구현은 상당히 까다로운 문제인데요, 여러 해상도에 대응하기 위해 레이아웃 작업 시 아래와 같은 기본 룰을 지켜는 것이 좋다고 하네요.


1. 레이아웃 디자인시에는 HVGA 기본 스크린 사이즈를 중심으로 DIP 단위만을 사용해서 디자인합니다. px과 dip가 1:1 이라서 화면 크기에 대응하여 사이즈 결정하기가 좋습니다. 320 * 480 화면 기준으로 들어갈 이미지나 UI 요소들 각각의 가로 사이즈를 px로 계산한 후 코딩시에는 그 값의 단위를 dip 로만 입력하 면 됩니다 . 

2. AbsoluteLayout 을 사용하지 않습니다. 즉 , 화면의 절대 좌표 보다는 상대 좌표를 사용해야 합니다. 이거 사용해서 디자인하면 ..나중에 감당 안됩니다. (LinearLayout이나 RelativeLayout을 사용) 

3. Bitmap 은 HDPI 기준으로 만듭니다. 그래야 자동으로 크기가 조정 되더라도 보기에 좋습니다. 즉 HVGA 를 기준으로 계산했을 때 100 * 100 이미지가 필요하다면 HDPI 기준으로 크기의 1.5 배인 150 *150 크기로 실 이미지를 제작하시면 됩니다. 만일 여력이 된다면 hdpi 및 mdpi 기준으로 각각 만드는 것도 나쁘지는 않습니다. 위에서 언급했듯이 성능에 유리합니다. 

4. 각 장치별로 레이아웃 및 이미지를 모두 별도로 만들어서 세밀하게 조정할 수도 있지만 그런 경우 관리가 힘듭니다 . 가급적 레이아웃과 이미지를 적게 사용해서 통일된 UI를 구성하는 것이 좋습니다.

5. layout xml에는 wrap_content, fill_parent, dp만을 사용합니다. 글자의 크기는 sp로 합니다.



출처 : S2 & NAMU 블로그,  돼지왕왕돼지의 놀이터 블로그




저작자 표시 비영리 변경 금지
신고
Posted by 플라스틱라디오
해상도에 따라 참고할 Layout, Drawable 폴더를 다르게 하기 위해서는 res 폴더내에 아래와 같은 이름으로 폴더를 생성하시면 됩니다.

drawable-1280x720
layout-1280x720



위와 같은 폴더를 res 폴더에 생성했을 경우 해상도가 1280x720인 폰에서는 위의 폴더에 있는 layout xml파일과 이미지 파일을 참고하여 화면을 구현하게 됩니다. 


출처 :  안드로이드펍, 보리 & 마고 블로그



 
저작자 표시 비영리 변경 금지
신고
Posted by 플라스틱라디오
Target은 사용할 컴파일러의 버전을 의미하고 minSdkVersion은 앱이 지원할 최소의 SDK 버전을 의미합니다.

Target은 가장 최신버전을 사용하는 것이 좋으며, 만일 특정 SDK 버전 이상에서만 작동되는 코드가 사용된 경우 이하의 SDK 버전에서는 오류가 발생하지 않도록 예외처리를 통해 분기 설정을 해주어야 합니다.

minSdkVersion은 앱이 지원하고자 하는  최소한의 버전을 지정해주시면 되구요.
저작자 표시 비영리 변경 금지
신고
Posted by 플라스틱라디오

Proguard는 소스를 난독화하여 디컴파일에 의해 소스코드가 노출되는 것을 방지하기 위해 사용됩니다. Proguard를 적용하면 보안 향상 뿐 아니라 약간의 최적화가 이루어진다고 하니 앱 개발을 완료한 후 꼭 적용하는 것이 좋을 거 같습니다.



최신 ADT에서는 Progaurd를 기본 지원하여 Proguard 적용이 보다 간편해 졌습니다. 최신 ADT가 설치된 Eclipse에서 안드로이드 프로젝트를 생성하면 프로젝트의 루트 폴더에 proguard.cfg와 project.properties 파일이 생성되어있는 것을 확인할 수 있습니다. Progaurd 적용을 위해서는 project.properties 파일안에 아래의 코드 한줄만 추가하면 됩니다. 

proguard.config=proguard.cfg 



Proguard의 적용은 일반 컴파일에서는 안되고 Export signed Application Package를 실행해 서명된 apk 파일을 생성할 때만 적용됩니다. Proguard가 정상적으로 적용되게 되면 apk 생성 후 프로젝트안에 proguard 폴더가 만들어지며 그 안에 dump.txt, mapping.txt, seeds.txt, usage.txt 파일들이 생성됩니다.

- dump.txt : 애플리케이션에서 사용중인 클래스들의 내부 구조에 대한 대략적인 정보
- mapping.txt : 난독화 과정에서 기존 클래스 혹은 메서드가 어떤 새로운 난독화된 이름으로 매핑되었는지 그 목록. 난독화된 애플리케이션에 발생하는 로그나, 스택 트레이스 들을 분석하기 위해 필요
- seeds.txt : 난독화되지 않은 클래스와 멤버들의 목록
- usage.txt : 사용되지 않아 apk 파일에서 제거된 코드들의 목록 (proguard.cfg 설정 내용에 따라 적용되지 않는 경우 있음)

여기서 mapping.txt 파일은 난독화된 애플리케이션에서 발생하는 로그, 스택트레이스 등을 분석하기 위해 반드시 필요하므로 잘 보관해 두어야 합니다. 버전 업 후 Proguard 재적용시 새로운 mapping.txt가 덮어쓰여지게 되므로 버전 별로 mapping.txt를 잘 보관해 두어야 할 듯 합니다. 

 
proguard.cfg에는 Proguard와 관련된 옵션을 설정해주는데 자세한 설정 방법은 Proguard 설명서를 참조하시면 됩니다. (사실 저도 잘 몰라요..^^;)



여기까지는 일반적인 Proguard 적용법이구요.. 외부 라이브러리를 사용한 경우에는 Proguard를 적용하는 과정에서 에러가 발생됩니다. 저도 이 에러 때문에 애를 좀 먹었는데 이러한 경우 에러를 해결하기 위해서는 외부라이브러리를 제외하는 옵션을 지정해 주어야 합니다. 구글에서 찾아본 결과 project.properties 파일에 -external.libs.dir=libs 를 추가하면 된다는 말도 있던데 저는 이렇게 해도 에러가 사라지지 않더군요. 그래서 저는 progaurd.cfg 파일 안에 -dontwarn 옵션을 추가하여 외부라이브러리를 난독화에서 제외하도록 했습니다. 그랬더니 에러없이 잘 되더군요.

-dontwarn 클래스명(같은 패키지에 속한 다수의 클래스를 지정하려면 패키지명.** 로 지정)

 
 
그리고 ADT에 포함되어 있는 Proguard는 구형 버전이라고 하더군요. 기왕이면 최신 Proguard를 다운로드 받아 설치하는 것이 좋겠죠.^^ 최신 Proguard 의 다운로드는 아래의 링크에서 하면 됩니다.


zip파일을 다운로드 후 압축을 풀어 나오는 폴더 중 lib, bin 폴더를 Android SDK가 설치된 폴더의 tools/proguard 경로에 덮어쓰기 하면 됩니다.


또한 Android SDK 나 프로젝트의 경로에 공백이 포함되어 있는 경우 Proguard 적용시 에러가 발생한다는 말도 있으니 혹시나 알 수 없는 에러가 계속 발생한다면 경로를 확인해 보는게 좋을거 같습니다.




참고 : 모든 것은 나의 뜻대로 - 안드로이드에 Proguard 적용 (이클립스)
         지똥이 - Android Proguard 설정법 libs 라이브러리 포함
         휴휴휴 - 안드로이드 이클립스에서 Proguard(프로가드) 사용하기
저작자 표시
신고
Posted by 플라스틱라디오
TAG Android

아이군의 블로그에 안드로이드 앱 개발에 있어서 상당히 유용한 글이 있어서 퍼왔습니다. 



마메렐핀님(이동훈님) - 2009.06.05

성능을 위한 설계

안드로이드 애플리케이션의 속도는 빨라야만 합니다. 음, 효율적이어야 한다고 말하는 쪽이 더 정확할 듯싶네요. 다시 말해, 제한된 컴퓨팅 파워와 데이터 저장소, 작은 화면, 갑갑한 배터리 수명 같은 모바일 장치 환경에서 가능한 한 효율적으로 실행되어야 한다는 것입니다.

애플리케이션을 개발할 때에는 이것을 명심하세요. 듀얼코어 개발 컴퓨터에서 실행하는 에뮬레이터에서는 충분히 잘 작동할지도 모르지만, 모바일 기기에서 실행할 때엔 그리 잘 되지 않을 것입니다. — 최고 성능의 모바일 기기라도 일반적인 데스크탑 시스템의 성능을 따라잡을 수는 없습니다. 그런 이유로, 다양한 모바일 기기들에게 최상의 성능을 보장하기 위해 여러분은 효율적인 코드를 작성하도록 열심히 노력하셔야 합니다.

일반적으로, 빠르거나 효율적인 코드라는 것은 메모리 할당을 최소화 하고, 꽉 짜인 코드를 작성하고, 특정 프로그래밍 언어나 잠재적으로 성능상 문제가 될만한 프로그래밍 어법들을 피하는 것을 말합니다. 객체지향 용어로 말하자면, 이러한 일이 가장 빈번히 일어나는 곳은 메소드 레벨이며, 이와 비슷하게 실제 코드 라인들과 반복문 등에서 발생합니다 .

이 문서는 다음 주제들을 다룹니다:

소개

자원 제한적 시스템에는 두 가지 기본 규칙이 있습니다:

  • 필요 없는 일은 하지 말 것
  • 메모리 할당을 피할 수 있다면 그렇게 할 것

아래의 모든 팁들은 이 두 가지 기본 주의를 따르고 있습니다.

누군가는 이 페이지상의 많은 조언이 "섣부른 최적화"나 마찬가지라고 비판할지도 모릅니다. 미시 최적화는 때로는 효율적인 데이터 구조와 알고리즘을 개발하는 것을 더 어렵게 만든다는 것은 사실입니다. 하지만, 핸드셋과 같은 임베디드 기기에서는 때로는 별다른 선택지가 없습니다. 예를 들어, 여러분이 데스크탑에서 개발할 때 생각하는 VM의 성능에 대한 가정을 안드로이드에도 적용한다면, 여러분은 시스템 메모리를 소진해버리는 코드를 꽤나 작성해 버리고 말 것입니다. 이것은 여러분의 애플리케이션이 바닥을 기도록 할 수 있습니다 — 시스템에서 동작하는 다른 프로그램들에게 무엇을 하는지 지켜보세요!

이것이 바로 이 가이드라인이 중요한 이유입니다. 안드로이드의 성공은 여러분의 애플리케이션이 제공하는 사용자 경험(UX)에 달렸고, 사용자 경험이란 것은 여러분의 코드가 빠르고 팔팔하게 반응하는지, 아니면 느리고 무거운지에 달렸습니다. 모든 우리의 애플리케이션들은 같은 장치에서 동작할 것이기 때문에, 어떤 의미로, 우리 모두 함께 이 것들을 지키도록 최선을 다해야 합니다. 이 문서를 운전면허를 딸 때 배워야만 하는 도로교통법이라고 생각하세요: 모든 이가 따르면 문제없이 원활하겠지만, 따르지 않는다면 사고가 날 것처럼 말입니다.

자세한 내용을 다루기 전에, 간단한 주의사항입니다: 아래 설명된 대부분의 이슈들은 VM이 JIT 컴파일러이든 아니든 효과적입니다. 같은 기능을 수행하는 두 메소드가 있고 interpret 방식에서 foo()의 실행속도가 bar()보다 빠르다면, 컴파일 된 버전에서도 아마 foo()가 bar()과 비슷하거나 더 빠른 속도를 보여줄 것입니다. 컴파일러가 여러분을 "구해줄"것이라던가 충분히 빠르게 만들어줄 것이라고 의존하는 건 현명하지 못하다는 것이죠.

객체 생성을 피하라

객체의 생성은 결코 공짜가 아닙니다. 임시 객체들을 위해 쓰레드-당(per-thread) 할당 풀을 사용하는 세대형(generational) GC는 더 낮은 비용으로 할당 할 수 있지만, 메모리를 할당한다는 것은 메모리를 할당하지 않는 것 보다 언제나 더 높은 비용이 듭니다.

만약 사용자 인터페이스 루프에서 객체를 할당한다면, 주기적으로 가비지 컬렉션을 강요하게 될 것이고 사용자 경험에 있어서 조그마한 "딸꾹질(거북함)"을 만들게 될 겁니다.

그러므로, 필요로 하지 않는 객체 생성을 피해야 합니다. 도움이 될 몇 가지 예제들이 있습니다.

  • 입력 데이터 셋에서 문자열을 추출할 때, 복사 생성된 것 대신 원본 데이터의 부분문자열을 받으십시오. 새로운 String 객체가 만들어졌더라도 원본 데이터의 char[]을 공유할 것입니다.
  • 문자열을 반환하는 메소드가 있고 그 결과가 언제나 StringBuffer에 더해지게 되는 경우에 있다면, 짧은 수명의 임시 객체를 생성하는 대신 직접적으로 더해지는 방식으로 식별자와 구현방식을 바꾸세요.

좀 더 급진적인 아이디어는 다차원 배열을 병렬의 단일 일 차원 배열로 잘라내는 것입니다:

  • int 배열은 Integer 배열보다 더 좋습니다만, 이것으로 또한 int형의 두 병렬 배열이 (int,int) 객체의 배열보다 더 많이 효과적이라는 사실을 추론할 수 있습니다.
  • 만약 (Foo,Bar) 튜플로 저장하는 컨테이너를 구현할 필요가 있다면, 직접 만든 (Foo,Bar) 객체의 단일 배열보다 두 개의 병렬 Foo[] 와 Bar[] 배열이 일반적으로 더욱 더 좋다는 것을 기억하십시오. (물론, 다른 코드들이 접근해야 하는 API를 설계할 때에는 예외가 있습니다; 이 경우 작은 속도 향상을 노리는 것 보다 좋은 API설계가 항상 좋습니다. 그러나 여러분의 내부 코드를 작성할 때에는 가능한 한 효율적인 코드가 되도록 해야 하겠습니다.)

일반적으로, 가능하다면 짧은 수명의 임시 객체 생성을 피하십시오. 더 적은 객체들을 만든다는 것은 사용자 경험에 직접적인 영향을 주는 가비지 컬렉션 줄여줌을 뜻합니다.

네이티브 메소드를 사용하라

문자열을 처리할 때, String.indexOf(), String.lastIndexOf() 와 그 밖의 특별한 메소드를 사용하는 것을 주저하지 마십시오. 이 메소드들은 대체적으로, 자바 루프로 된 것 보다 대략 10-100배 빠른 C/C++ 코드로 구현이 되어있습니다.

이 조언의 반대적 측면은 네이티브 메소드를 호출하는 것이 interpret방식의 메소드 호출보다 더 비용이 높다는 것입니다. 피할 수 있다면, 사소한 계산에는 네이티브 메소드를 사용하지 마십시오.

인터페이스보다 가상 연결을 선호하라

여러분이 HashMap 객체를 하나 가지고 있다고 합시다. 여러분은 HashMap이나 제네릭 Map 으로 선언을 할 수 있습니다.

Map myMap1 = new HashMap();

HashMap myMap2 = new HashMap();

어떤것이 더 좋은가요?

전통적인 지혜에서는 Map을 사용해야 한다고 할 것입니다. Map 인터페이스를 구현한 어떤 것으로라도 구현체를 바꿀 수 있기 때문입니다. 전통적인 지혜는 전통적인 프로그래밍에는 맞습니다만, 임베디드 시스템에는 그다지 대단하지 않습니다. 인터페이스 참조를 통해 호출하는 것은 명확한 참조를 통한 가상 메소드 호출보다 2배 더 소요될 수 있습니다.

여러분이 하는 일에 적합하여 HashMap사용을 선택했다면 Map으로 호출하는 것은 거의 가치가 없습니다. 코드를 리팩터링 해 주는 IDE의 가능성을 고려해 보더라도, Map으로 호출하는 것은 큰 가치가 없습니다. 여러분이 코드의 방향을 확신하지 못한다 해도 말입니다. (다시금 이지만, 공용 API는 예외입니다: 작은 성능 고려보다 좋은 API가 언제나 으뜸입니다.)

가상 연결보다 정적 연결을 선호하라

만약 객체의 필드에 접근할 필요가 없다면, 여러분의 메소드를 정적(static)으로 만드세요. 가상 메소드 테이블을 필요로 하지 않기 때문에 그게 더 빠르게 불려집니다. 또한, 메소드 식별자를 보고 메소드 호출이 객체의 상태를 바꿀 수 없다고 말할 수 있으므로, 좋은 관습이 됩니다.

내부에서 Getter/Setter 사용을 피하라

C++와 같은 네이티브 언어에서 필드에 직접적으로 접근하는 것 (예. i = mCount) 보다 getter를 사용하는 것 (i = getCount())은 일반적인 관습입니다. 이 방법은 C++에서는 훌륭한 습관입니다. 왜냐하면 항상 접근을 inline화 할 수 있는 컴파일러를 사용하고 있고, 필드에 접근을 제한하거나 디버그 해야 한다면 언제나 코드를 추가할 수 있기 때문입니다.

안드로이드에서는 나쁜 생각입니다. 가상 메소드 호출은 인스턴스 필드 참조보다 더 비용이 높습니다. 일반적인 객체 지향 프로그래밍 관습에 따르거나, 공용 인터페이스에서 getter, setter을 가지는 것은 이치에 맞습니다. 그러나 클래스 내부에서는 언제나 직접적으로 필드 접근을 해야합니다.

필드 참조들을 캐시하라

객체의 필드에 접근하는 것은 지역 변수에 접근하는 것 보다 더 느립니다. 이렇게 작성하는 것 대신:

for (int i = 0; i < this.mCount; i++)

      dumpItem(this.mItems[i]);

이렇게 하십시오:

  int count = this.mCount;

  Item[] items = this.mItems;

 

  for (int i = 0; i < count; i++)

      dumpItems(items[i]);

(멤버 변수라는 것을 명확히 하기 위해 명시적인 "this"를 사용하고 있습니다.)

유사한 가이드라인은, 결코 "for"문의 두 번째 조건에서 메소드를 호출하지 말라는 것입니다. 예를 들어, 다음 코드는 간단하게 int 값으로 캐쉬 할 수 있는 경우임에도 큰 낭비가 되는 getCount()메소드를 매번 반복 마다 실행하게 됩니다:

for (int i = 0; i < this.getCount(); i++)

    dumpItems(this.getItem(i));

인스턴스 필드를 한번 이상 접근해야 한다면, 지역 변수를 만드는 것 또한 좋은 생각입니다. 예를 들어:

    protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) {

        if (isHorizontalScrollBarEnabled()) {

            int size = mScrollBar.getSize(false);

            if (size <= 0) {

                size = mScrollBarSize;

            }

            mScrollBar.setBounds(0, height - size, width, height);

            mScrollBar.setParams(

                    computeHorizontalScrollRange(),

                    computeHorizontalScrollOffset(),

                    computeHorizontalScrollExtent(), false);

            mScrollBar.draw(canvas);

        }

    }

mScrollBar 멤버 필드에 네 개의 분리된 참조가 있습니다. 지역 스택 변수로 mScrollBar를 캐싱 함으로써, 네 개의 멤버 필드 참조가 더욱 효율적인 네 개의 스택 변수 참조로 바뀌었습니다.

덧붙여 말하자면, 메소드 인자들은 지역 변수와 같은 성능 특성을 가집니다.

상수를 Final로 선언하라

클래스의 상단에 있는 다음 선언을 고려해 봅시다:

static int intVal = 42;

static String strVal = "Hello, world!";

컴파일러는 클래스가 처음 사용될 때 실행하게 되는 <clinit>라 불리는 '클래스 초기화 메소드'를 생성합니다. 이 메소드가 intVal에 42 값을 저장하고, strVal에는 클래스파일 문자 상수 테이블로부터 참조를 추출하여 저장합니다. 나중에 참조될 때 이 값들은 필드 참조로 접근됩니다.

이를 "final" 키워드로 향상시킬 수 있습니다:

static final int intVal = 42;

static final String strVal = "Hello, world!";

클래스는 더이상 <clinit> 메소드를 필요로 하지 않습니다. 왜냐하면 상수들은 VM에 의해 직접적으로 다루어 지는 '클래스파일 정적 필드 초기자'에 들어가기 때문입니다.intVal의 코드 접근은 직접적으로 정수 값 42를 사용할 것이고, strVal로의 접근은 필드 참조보다 상대적으로 저렴한 "문자열 상수" 명령을 사용하게 될 것입니다.

"final"으로 메소드나 클래스의 선언을 하는 것은 즉각적인 성능 이득을 주지는 못하지만, 특정한 최적화를 가능하게 합니다. 예를 들어, 컴파일러가 서브클래스에 의해 오버라이드될 수 없는 "getter"메소드를 알고 있다면, 메소드 호출을 inline화 할 수 있습니다.

여러분은 또한 지역 변수를 final로 선언할 수 있습니다. 하지만 이것은 결정적인 성능 이득은 없습니다. 지역 변수에는 오직 코드를 명확히 하기 위해서 "final"을 사용합니다 (또는 예를 들어 익명 내부 클래스를 사용해야 한다면 가능).

주의 깊게 향상된 반복문(Enhanced For Loop)을 사용하라

향상된 반복문(때로 "for-each"로 알려진 반복문)은 Iterable 인터페이스를 구현한 컬렉션들을 위해 사용될 수 있습니다. 이러한 객체들로, 반복자는 hasNext() 와 next()을 호출하는 인터페이스를 만들기 위해 할당됩니다. ArrayList의 경우 여러분이 직접 탐색하는 것이 좋을 수 있습니다만, 다른 컬렉션들에서는 향상된 반복문 구문이 명시적인 반복자의 사용과 동등한 성능을 보여줍니다.

그럼에도, 다음 코드로 향상된 반복문의 만족스러운 사용법을 볼 수 있습니다:

public class Foo {

    int mSplat;

    static Foo mArray[] = new Foo[27];



    public static void zero() {

        int sum = 0;

        for (int i = 0; i < mArray.length; i++) {

            sum += mArray[i].mSplat;

        }

    }



    public static void one() {

        int sum = 0;

        Foo[] localArray = mArray;

        int len = localArray.length;



        for (int i = 0; i < len; i++) {

            sum += localArray[i].mSplat;

        }

    }



    public static void two() {

        int sum = 0;

        for (Foo a: mArray) {

            sum += a.mSplat;

        }

    }

}

zero() 는 반복되는 매 주기마다 정적 필드를 두 번 부르고 배열의 길이를 한번 얻습니다.

one() 은 참조를 피하기 위해 지역 변수로 모든 것을 끌어냈습니다.

two() 는 자바 언어의 1.5버전에서 소개된 향상된 반복문 구문을 사용합니다. 컴파일러에 의해 생성된 코드는 배열 참조와 배열의 길이를 지역 변수로 복사해주어, 배열의 모든 원소를 탐색하는데 좋은 선택이 될 수 있습니다. 주 루프 내에 추가적인 지역 읽기/저장이 만들어지고(명백하게 "a"에 저장), one()보다 쪼금 느리고 4 바이트 길어지게 하긴 합니다.

좀 더 명확하게 모든 것을 종합하자면: 향상된 반복문 구문은 배열과 잘 동작하지만, 추가적인 객체 생성이 있게 되는 Iterable 객체와 함께 사용할 때엔 조심해야 합니다.

열거형(Enum)을 피하라

열거형은 매우 편리합니다, 그러나 불운하게도 크기와 속도 측면에서 고통스러울 수 있습니다. 예를들어, 다음의 코드는:

public class Foo {

   public enum Shrubbery { GROUND, CRAWLING, HANGING }

}

900 바이트의 클래스 파일 (Foo$Shrubbery.class) 로 변환됩니다. 처음 사용할 때, 클래스 초기자는 각각의 열거화된 값들을 표기화 하는 객체상의 <init>메소드를 호출합니다. 각 객체는 정적 필드를 가지게 되고 총 셋은 배열("$VALUES"라 불리는 정적 필드)에 저장됩니다. 단지 세 개의 정수를 위해 많은 코드와 데이터를 필요로 하게 됩니다.

다음 코드:

Shrubbery shrub = Shrubbery.GROUND;

는 정적 필드 참조를 야기합니다. "GROUND"가 정적 final int 였더라면, 컴파일러는 알려진 상수로서 다루고, inline화 했을 수도 있습니다.

물론, 반대적 측면에서 열거형으로 더 좋은 API를 만들 수 있고 어떤 경우엔 컴파일-타임 값 검사를 할 수 있습니다. 그래서 통상의 교환조건(trade-off)이 적용됩니다: 반드시 공용 API에만 열거형을 사용하고, 성능문제가 중요할 때에는 사용을 피하십시오.

어떤 환경에서는 ordinal() 메소드를 통해 정수 값 열거를 갖는 것이 도움이 될 수 있습니다. 예를 들어, 다음 코드를:

for (int n = 0; n < list.size(); n++) {

    if (list.items[n].e == MyEnum.VAL_X)

       // do stuff 1

    else if (list.items[n].e == MyEnum.VAL_Y)

       // do stuff 2

}

다음 코드로 대신합니다:

   int valX = MyEnum.VAL_X.ordinal();

   int valY = MyEnum.VAL_Y.ordinal();

   int count = list.size();

   MyItem items = list.items();



   for (int  n = 0; n < count; n++)

   {

        int  valItem = items[n].e.ordinal();



        if (valItem == valX)

          // do stuff 1

        else if (valItem == valY)

          // do stuff 2

   }

때로는, 보장할 수 없습니다만, 이것이 더 빠를 수 있습니다.

내부 클래스와 함께 패키지 범위를 사용하라

다음 클래스 정의를 고려해 봅시다:

public class Foo {

    private int mValue;



    public void run() {

        Inner in = new Inner();

        mValue = 27;

        in.stuff();

    }



    private void doStuff(int value) {

        System.out.println("Value is " + value);

    }



    private class Inner {

        void stuff() {

            Foo.this.doStuff(Foo.this.mValue);

        }

    }

}

여기서 주목해야 할 중요한 것은, 외부 클래스의 private 메소드와 private 인스턴스 필드에 직접 접근하고 있는 내부 클래스(Foo$Inner)를 정의했다는 것입니다. 이것은 적법하고, 코드는 기대했던 대로 "Value is 27"을 출력합니다.

문제는 Foo$Inner는 기술적으로는 (비밀로써) 완전히 분리된, Foo의 private 멤버로 직접적인 접근을 하는 것은 적법하지 못한 클래스라는 것 입니다. 이 차이를 연결짓기 위해, 컴파일러는 두 개의 합성 메소드를 만듭니다:

/*package*/ static int Foo.access$100(Foo foo) {

    return foo.mValue;

}

/*package*/ static void Foo.access$200(Foo foo, int value) {

    foo.doStuff(value);

}

내부 클래스 코드는 외부 클래스에 있는 "mValue" 필드에 접근하거나 "doStuff" 메소드를 부르기 위해 이 정적 메소드를 부릅니다. 이것은 이 코드가 결국은 직접적인 방법 대신 접근자 메소드를 통해 멤버 필드에 접근하고 있다는 것을 뜻합니다. 이전에 우리는 어째서 접근자가 직접적인 필드 접근보다 느린지에 대해 이야기 했었는데, 이 문제로서 "보이지 않는" 성능 타격 측면에서 특정 언어의 어법이 야기하게 되는 문제에 대한 예제가 될 수 있겠습니다.

이 문제는 내부 클래스가 접근하는 필드와 메소드 선언에 private 범위가 아닌 package 범위를 가지도록 함으로써 피할 수 있습니다. 이로써 더욱 빠르게 동작하게 되고 자동 생성되는 메소드에 의한 오버헤드를 제거할 수 있습니다. (불운하게도 이 또한 직접적으로 같은 패키지 내의 다른 클래스들이 필드들에 접근할 수 있다는 것을 뜻하게 되며, 모든 필드들은 private로 해야 한다는 표준적인 OO 관습에 거스르게 됩니다. 다시 한번 더 말하자면, 공용 API를 설계하게 된다면 이 최적화를 사용하는 것을 조심스럽게 고민해야만 할 것입니다.)

Float를 피하라

펜티엄 CPU가 출시되기 전, 게임 제작자들에겐 정수 계산에 최선을 다하는 것이 일반적이었습니다. 펜티엄과 함께 부동소수점 계산 보조 프로세서는 일체형이 되었고, 정수와 부동소수점 연산을 넣음에 따라 순수하게 정수 계산만을 사용하는 것 보다 게임은 더 빠르게 되었습니다. 자유롭게 부동소수점을 사용하는 것은 데스크탑 시스템에서는 일반적입니다.

불운하게도, 임베디드 프로세서에게는 빈번하게 하드웨어적으로 부동소수점 계산이 제공되지 않고 있어, "float" 와 "double"의 모든 계산이 소프트웨어적으로 처리됩니다. 어떤 기초적인 부동소수점 계산은 완료까지 대략 일 밀리 초 정도 걸릴 수 있습니다.

또한, 정수에서도 어떤 칩들은 하드웨어 곱셈을 가지고 있지만 하드웨어 나눗셈이 없기도 합니다. 이러한 경우, 정수 나눗셈과 나머지 연산은 소프트웨어적으로 처리됩니다 — 만약 해시 테이블을 설계하거나 많은 계산이 필요하다면 생각해 보아야 할 것입니다.

성능 예시 숫자 몇 개

우리의 몇 가지 아이디어를 설명하기 위해, 약간의 기초적인 행동들에 대해 대략적인 실행시간을 나열한 테이블을 만들었습니다. 이 값들은 절대적인 숫자가 아니라는 것을 주목해 주십시오: CPU시간과 실제 구동 시간의 조합이고, 시스템의 성능 향상에 따라 변화할 수 있습니다. 그러나 이 값들 사이에 관계를 적용해 보는 것은 주목할 만한 가치가 있습니다 — 예를 들어, 멤버 변수를 더하는 것은 지역 변수를 더하는 것보다 대략 네 배가 걸립니다.

행동 시간
지역 변수 더하기 1
멤버 변수 더하기 4
String.length() 호출 5
빈 정적 네이티브 메소드 호출 5
빈 정적 메소드 호출 12
빈 가상 메소드 호출 12.5
빈 인터페이스 메소드 호출 15
HashMap의 Iterator:next() 호출 165
HashMap의 put() 호출 600
XML로부터 1 View 객체화(Inflate) 22,000
1 TextView를 담은 1 LinearLayout 객체화(Inflate) 25,000
6개의 View 객체를 담은 1 LinearLayout 객체화(Inflate) 100,000
6개의 TextView 객체를 담은 1 LinearLayout 객체화(Inflate) 135,000
빈 activity 시작 3,000,000

맺음 말

임베디드 시스템을 위해 좋고 효율적인 코드를 작성하는 최선의 방법은 여러분이 작성하는 코드가 실제로 무엇을 하는지 이해하는 것 입니다. 여러분이 정말로 반복자를 할당하기를 원한다면, List에 향상된 반복문을 반드시 사용하십시오; 부주의한 부작용이 아닌 신중한 선택을 통해서 말입니다.

유비무환입니다! 무엇을 하는지 알고 하세요! 좋아하는 좌우명을 여기에 넣으세요, 그러나 언제나 여러분의 코드가 무엇을 하는지 주의 깊게 생각하고, 속도를 높이는 방법을 찾도록 경계하십시오.

저작자 표시
신고
Posted by 플라스틱라디오

안드로이드 코딩을 하면서 빈번하게 사용되는 것이 Activity의 this 입니다. 이는 Context를 상속한 Activity의 Context 객체를 얻기 위한 변수인데요.. Context 변수를 필요로하는 메서드를 사용할 때 주로 액티비티명.this의 형태로 많이 사용합니다.
하지만 이렇게 Activity의 this를 직접 받아올 경우 메모리릭이 발생할 확률이 높다고 합니다. 따라서 Activity의 this를 통해 Context 객체를 가져오는 것보다 가급적  Context.getApplicationContext()나 Activity.getApplication() 메소드를 통해 Context 객체를 얻어내는 것이 좋다고 하는 군요.

보다 자세한 내용은 아래의 링크를 참조하세요.

휴휴휴 - [번역] 안드로이드 메모리 릭 회피하기 
저작자 표시
신고
Posted by 플라스틱라디오

Interpolation 수식을 이용하여 프레임 애니메이션을 구현하면 서서히 가속하거나 감속하는 애니메이션을 만들 수 있습니다. Interpolation 수식에는 여러 방식이 있습니다.




부동소수점 연산을 이용한 프레임 의존적인 감속 보간  

부동소수점 연산을 이용하는 경우 Euler 보간법과 달리 지수 함수를 사용하지 않기 때문에 CPU에 부담을 덜 줍니다. 따라서 감속 보간을 구현할 경우 Euler 보간법 보다 부동소수점 연산을 이용하는 것이 더 효율적입니다.

부동소수점 연산을 이용한 프레임 의존적인 감속 보간에 사용되는 수식은 아래와 같습니다.

F(x) = (x_0 * (weight - 1) + x_final) / weight

x_0 : 시작 좌표
x_final : 마지막 좌표
weight : 기울기의 곡선 처리를 부드럽게 적용하기 위한 가중치 상수




위의 수식을 이용해 소스 상에서 감속애니메이션을 구현하는 방법은 아래와 같습니다.
float weight = 8.0f; //가중치 상수
int n = 600;

x_0 = (x_0 * (weight - 1.0f) + x_final) / weight;
interpolation_values[interpolation_index++] = x_0;

for(int i=0; i<n; i++) {
   x_1 = (x_0 * (weight - 1.0f) + x_final) / weight;
   interpolation_values[interpolation_index++] = x_1 - x_0; //x의 변화량을 보간 값으로 저장
   x_0 = x_1;
}




출처 : 나의별
저작자 표시
신고
Posted by 플라스틱라디오


맨 위로

티스토리 툴바