키캣미만에서는 RunningTaksInfo를 이용하고, 키캣이상에서는 RunningAppProcessInfo 를 이용하는 방식이다. 그런데 이 방색을 사용할때는 GET_TASKS 권한을 AndroidManifest.xml에 추가해야 하는데, GET_TASKS 권한이 deprecated 되었다.
그래서 다른 방식으로 사용하는것이 Activity의 onStart(), onStop() 함수를 오버라이드(override) 해서 count 하는 방식이다. 대략적인 코드는 아래와 같은 모습이다.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
먼저 혼동하기 쉬운 얼굴인식과 얼굴검출에 대해서 간단하게 짚고 넘어가면..
얼굴검출(face detect)은 이미지에서 얼굴이 어느 영역에 있는지
찾는 것이고
얼굴인식(face recognize)은 A라는 얼굴과 B라는 얼굴이 유사한지 또는 같은 사람인지 판별하는 것이다.
안드로이드에서 face dectect을 하기 위해서는 2가지 방식이 있다.
첫번째는 , Image 소스에서 얼굴을 검출(dectect)하는 android.media.FaceDetector 를
이용하는 방법
두번째는, live stream 소스에서 얼굴을 검출하는 Camera.FaceDetectionListener 를 이용하는 방법
Android 에서 얼굴검출을 위해서 3rd party library를 사용하지 않는다면 어쩔수 없이 사용하는 FaceDetector는 API level 1 부터 사용가능하데, 그래서 그런지 결과나 성능에서 상당히 떨어진다. 그리고 FaceDetector를 사용할때는 한가지 주의 할점이 입력 Bitmap을 RGB_565 타입을 넘겨야 한다. 대략적인 코드는 아래와 같다.
android.media.FaceDetector.Face[]
scaledFaces = new android.media.FaceDetector.Face[1]; // 얼굴 1개만 인식 Bitmap inputBitmap
= buildScaledFace565Bitmap(imageBitmap); android.media.FaceDetector faceDetector
= new android.media.FaceDetector(inputBitmap.getWidth(), inputBitmap.getHeight(), 1); faceDetector.findFaces(inputBitmap, scaledFaces);
그리고 입력 bitmap의 크기가 커질수로 수행시간이 기하급수적으로
늘어난다. 그리고 detecting된 정보도 두 눈사이의
중점 Point, 중점 Point에서 눈사이의 거리 정보만
준다. 즉 얼굴영역(Rect)는 알려주지 않는다.
그래서 2015년 8월에 google에서 google-play-vision library를
발표했는데, 무척이나 좋다.
간단하게 media.FaceDetector와 비교해본 테스트 성능은 아래와 같다.
갤럭시 S4단말
사진크기
FaceDetector 시간
Vision 시간
460 * 560
224ms
154ms
960 * 960
705ms
305ms
1920 * 1200
2380ms
350ms
성능도 좋지만 결과 품질도 상당히 좋다. 주요한 장점을 설명하면 아래와
같다.
1.얼굴영역(left/right 정보, width, height)를 알려준다.
·getPosition() - Returns the top left coordinates of the area where a face was
detected
·getWidth() - Returns the width of the area where a face was detected
·getHeight() - Returns the height of the area where a face was detected
2.얼굴이 기울어저 있어도 검출된다.
위그림처럼 y축 기준으로 60도 회전된 얼굴과, xy평면을 기준으로 45도 회전된 얼굴까지 검출이가능하다. 기존 android.media.FaceDetector에 비하면 비약적인
발전으로 보인다. ^^;
3.얼굴의 다양한 특징 포인트를 알려준다.
얼굴의 특징 포인트를 Landmark로 부르는데, 코드에 아래처럼 정의 되어 있다.
public static final int BOTTOM_MOUTH = 0;
public static final int LEFT_CHEEK = 1;
public static final int LEFT_EAR_TIP = 2;
public static final int LEFT_EAR = 3;
public static final int LEFT_EYE = 4;
public static final int LEFT_MOUTH = 5;
public static final int NOSE_BASE = 6;
public static final int RIGHT_CHEEK = 7;
public static final int RIGHT_EAR_TIP = 8;
public static final int RIGHT_EAR = 9;
public static final int RIGHT_EYE = 10;
public static final int RIGHT_MOUTH = 11;
두눈위치, 입위치, 볼위치, 코위치, 귀위치 등 얼굴에 대해서 상세한 정보를 줄 수 있는데, 물로 검출에 성공한 경우에만 Landmark 위치를 알 수 있다. 실제로 테스트 해보면 귀위치는 잘 안나온다.
Image에서 얼굴검출할 때 사용하면 상당히 유용할 듯 한다. 추가로 FaceTracking(live로 얼굴을 검출하고 얼굴이
이동하면 따라가면서 계속 검출하는)도 가능하다. 하지만 FaceTracking은 input 크기가 클 경우 FPS가 잘 안나온다.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
Android 3.0 ( 허니컴 : API level 11
) 부터 하드웨어 가속 (hardwareAccelerated ) 옵션으로 2D rendering
pipeline 의 사용여부를 설정 할 수 있도록 하였다. hardwareAccelerated 을
설정하면 CPU가 할 일을 GPU가 대신 처리해서 그런지
체감 속도가 많이 빨라진다.
설정하기
ApplicationManifest.xml에서 설정할수 있다.
앱 전체에 설정하려면 아래처럼 application tag에 설정하면
된다.
<application
android:hardwareAccelerated="true"
또는 Activity 단위로도 설정할 수 있다.
<activity
android:hardwareAccelerated="true"
코드를 이용해서 동적으로 Activity에 설정하려면 아래와 같이
하면 된다. ( 동적으로 application 전체를 설정하는
방법은 없다. 동적으로 하려면 모든 activity에 아래의
방법을 적용하면 된다. )
View 단위로도 설정할수 있는데,
아래의 방법으로 코드로 설정 할 수 있다. 생성자에서 호출이 되어야 한다.
if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_SOFTWARE,
null);
}
참고로, Applicationmanifest나 코드로 설정안하고, android의 settings에서 개발자 옵션에서 “GPU 렌더링 강제 실행” 옵션을 키면 항상 GPU로 렌더링하게 된다.
View 단위로 설정하는 코드에서 보듯이, SDK에서 View의
LayerType은 3가지로 제공한다.
lLAYER_TYPE_NONE
lLAYER_TYPE_SOFTWARE
lLAYER_TYPE_HARDWARE
Software-base drawing model
소프트웨어 기반의 그리기 모델에서는 다음 2가지 절차를 통해서 그리기를
수행한다.
1.Invalidate the hierarchy
2.Draw the hierarchy
예를들어서, TextView의 내용을 변경하고 invalidate() 함수를 호출하면 view의 hierarchy를 순회하면서 모든 view의 invalidate가 호하면서 다시 그려야할 영역을( the dirty
regin) 을 구하게 된다. 그리고 view의
hierarchy를 순회하면서 다시 그리게 된다. (View.invalidate 함수를 보면, ViewParent의 invalidateChild 함수를 호출하는
것을 확인할수 있다. )
Software로 그리는 방식은 간단하지만, 2개의 결점을 가지고 있다. 첫째는 변경되지 않은 view에 대해서도 draw 코드를 수행하게 되는것이고, 두번째는 앱에 버그가 존재할수 있다는 것이다.
Hardware-base drawing model
하드웨어기반의 그리기 모델에서는 소프트웨어기반의 그리기 모델의 단점을 개선하여서 변경된 view만 update를 하도록 개선된 모델이다.
1.Invalidate the hierarchy
2.Record and update display lists
3.Draw the display lists
디스플레이 리스트( diplay list)를 가지고 있어서 각각의 view hierarchy에서 update되는 것만 다시 그리는 방식이다.
전체적인 구조나 성능면에서 hardware 방식이 더 좋지만 hardware 방식의 문제점도 있다. 바로 모든 api 가 지원하는 것이 아니다.
아래의 표는 Harware 기반 그리기 모델에서 android OS 버전별로 지원되는 api와 지원안되는 api 리스트 이다. 아래의api를
사용할때는 성능 감소가 되더라도, LayerType을 반드시
software로 설정해야 정성 동작한다.
API level
< 16
16
17
18
Canvas
drawBitmapMesh()
(colors array)
✗
✗
✗
✓
drawPicture()
✗
✗
✗
✗
drawPosText()
✗
✓
✓
✓
drawTextOnPath()
✗
✓
✓
✓
drawVertices()
✗
✗
✗
✗
setDrawFilter()
✗
✓
✓
✓
clipPath()
✗
✗
✗
✓
clipRegion()
✗
✗
✗
✓
clipRect(Region.Op.XOR)
✗
✗
✗
✓
clipRect(Region.Op.Difference)
✗
✗
✗
✓
clipRect(Region.Op.ReverseDifference)
✗
✗
✗
✓
clipRect()
with rotation/perspective
✗
✗
✗
✓
Paint
setAntiAlias()
(for text)
✗
✗
✗
✓
setAntiAlias()
(for lines)
✗
✓
✓
✓
setFilterBitmap()
✗
✗
✓
✓
setLinearText()
✗
✗
✗
✗
setMaskFilter()
✗
✗
✗
✗
setPathEffect()
(for lines)
✗
✗
✗
✗
setRasterizer()
✗
✗
✗
✗
setShadowLayer()
(other than text)
✗
✗
✗
✗
setStrokeCap()
(for lines)
✗
✗
✗
✓
setStrokeCap()
(for points)
✗
✗
✗
✗
setSubpixelText()
✗
✗
✗
✗
Xfermode
AvoidXfermode
✗
✗
✗
✗
PixelXorXfermode
✗
✗
✗
✗
PorterDuff.Mode.DARKEN
(framebuffer)
✗
✗
✗
✗
PorterDuff.Mode.LIGHTEN
(framebuffer)
✗
✗
✗
✗
PorterDuff.Mode.OVERLAY
(framebuffer)
✗
✗
✗
✗
Shader
ComposeShader
inside ComposeShader
✗
✗
✗
✗
Same
type shaders inside ComposeShader
✗
✗
✗
✗
Local
matrix on ComposeShader
✗
✗
✗
✓
참고 : http://developer.android.com/guide/topics/graphics/hardware-accel.html
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
안드로이드를 개발할 때 장점은 java로 되어 있어서 android의 소스를 쉽게 참조 할수 있는 점이다. 그런데, 안드로이드 소스외에 추가로 사용하는 library에 대해서는 소스를
볼 수 없어서 답답하곤 했다. 예를 들어서 android의
하위버전 호환 library인
android-support-v4.jar를 대부분 사용하는데, 개발하다가 ‘F3’ 키로 따라 들어가다 보면 아래와 같은 화면을 만나게 된다.
Android –support-v4 가 opensource라서 project를 다운받아서 봐도 되지만, 개발 환경과 연동이 안되어서 바로바로 소스보기가 불편하다. 그래서
검색해보니 있구나~
Google I/O 2012에서 그 방법을 소개하고 있다. ( 48분 부터 보면 된다. )
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
Android에서 native 라이브러리를
만들 때 보통은 armeabi / armeabi-v7a 용으로 컴파일 해서 프로젝트의 libs 폴더 밑에 넣어준다. 이는 대부분의 안드로이드 장치가 ARM 기반의 CPU로 만들어 졌기 때문이다. 사실 Android 에서 지원하는
cpu는 ARM 뿐만 아니라 Interl의 x86 도 지원하고 MIPS 또한 공식적으로 지원한다. 다만 시장에 아직 제품이 많지 않을 뿐이다.
참고 : CPU Architecuture 별 지원 Platforms
Native Code CPU Architecture Used
Compatible Android Platform(s)
ARM, ARM-NEON
Android 1.5 (API Level 3) and higher
x86
Android 2.3 (API Level 9) and higher
MIPS
Android 2.3 (API Level 9) and higher
이번에 Intel에서 Xolo-X900
이라는 Intel의 x86 기반의 안드로이드
폰을 출시했다. 이번이라고 한건 내가 지금 알았기 때문이고, 찾아보니
실체 출시된 날짜는 2012년 4월 쯤이었다. 와~ 완전 모르고 있었네 ㅎㅎ
So 파일을 로딩하기 위해서 보통은 System.loadLibrary() 함수를 호출한다. 그러면 해당
함수가 알아서 CPU를 확인해서 알맞은 so 모듈을 로딩해주는
것으로 알고 있었다. 우연히 xolo 디바이스를 구할 수
있어서 armeabi/armeabi-v7a 용 so 모듈만
있는 프로그램을 실행시켰는데, armeabi-v7a용 so 가
로딩 되는 것이다. 우와~ 그러나 실행하다가 죽는다. ㅠㅠ
그래서 loadLibrary를 연구해 보기 시작했다. 가장 먼저 developer의 문서를 찾아보면 아래와 같다.
Loads and
links the library with the specified name. The mapping of the specified library
name to the full path for loading the library is implementation-dependent.
즉 loadLibrary(“foo”) 이렇게 호출하면 이름(foo)과 일치하면서, device의 CPU타입에 따라서 적절한 모듈을 로딩해 준다. 즉 해당 device가
armeabi-v7a 를 지원하면 lib/armeabi-v7a 경로에 있는 foo.so를 로딩한고 device가 x86을 지원하면 lib/x86 경로이 있는 foo.so를 로딩하는 것이다. 그리고 full path를 사용하면 해당 so를 직접 로딩한다는 말이다.
그럼, loadlibrary(foo)를 호출 했을 때 Intel의 Xolo 단말기는 왜 lib/armeabi-v7a
경로에 있는 모듈을 로딩한걸까? ( lib/x86 폴더는 존재하지 않는 상태임 )
궁금증을 해결하기 위해서 일단 Java 레벨에서(NDK가 아닌) CPU 정보를 얻는 방법부터 찾아 보았다. 간단하게 Build 클래스에 정보가 있었다.
Build.CPU_ABI 를 이용하면 되는데, 보다보니 Build.CPU_ABI2가 존재 한다. 아~~ 느낌이 온다~
(참고로 ABI는 Application Binary Interface의 약자다. )
System.loadLibrary는디바이스의Build.CPU_ABI /
Build.CPU_ABI2의 순서로 so 모듈을 찾는 것이다. Android 의 loadLibrary 내부에서 mapLibraryName을 호출하는 부분이 아마도 이 동작을 하는 것으로 추측된다.
해결 하려면 so 파일을 x86용으로
개발해서 빌드한후 추가하면 된다. 또는 모든 기능이 x86을
지원하는 것이 아니라면 아래처럼 Java 코드로 간단하게 막을수 있다.
publicstaticvoid loadLibrary() {
if (isLoaded) {
return;
}
if (isLoaded == false) {
if (Build.CPU_ABI.equals(“x86”)) {
isLoaded = false;
return;
}
try {
System.loadLibrary("foo");
isLoaded = true;
} catch (Throwable e) {
isLoaded = false;
}
}
추가로 발견한 내용으로는 프로젝트의 lib/x86 폴더에 해당 모듈이
하나라도 있으면 google play 마켓에서 x86용 디바이스에
노출이 된다는 점이다.
따라서 프로젝트가 x86을 부분적으로 지원 할 수는 없고 모든 so 모듈들을 x86으로 지원하는 것이 좋다.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
android에서 animation은 크게 2가지로 구분된다.
Frame animation :
각 frame의 이미지를 여러장 준비해서 보여주는 것
장점 : 이미지 제작에 따라서 다양한 에니메이션이 가능하다
단점 : 이미지를 많이 쓰게되면, 저사양 단말에서 OutofMemory 발생 가능성이 높아진다.
Tween animation
시스템이 제공하는 방법으로 첫상태와 마지막 상태를 지정해서 중간의 frame은 계산해서 만드는방식
장점 : 이미지가 필요없어서 가볍고 빠르다.
단점 : 제공되는 animation만 가능하다. ( alpha, scale, translate 등등 )
이중에서 Frame Animaton을 이용할 때 AnimationDrawable을 사용하게 되는데 사용시 주의점에 대해서 몇가지 적어본다.
[ AnimationDrawable.start() 호출 시점 ]
사용자의 interaction 없이 Activity의 시작과 동시에 animation이 시작되야 할 때 onCreate에서 호출하면 동작을 않한다. 이유는 developer 사이트에서 다음과 같이 설명하고 있다.
It's important to note that the start() method called on the AnimationDrawable cannot be called during the onCreate() method of your Activity, because the AnimationDrawable is not yet fully attached to the window. If you want to play the animation immediately, without requiring interaction, then you might want to call it from the onWindowFocusChanged() method in your Activity, which will get called when Android brings your window into focus.
ImageView의 post()를 통해서 호출시점을 조금 뒤로 늦추는 방식인데, 이 방식보다는 developer 페이지에서 언급한 방식이 더 낳아 보인다.
[ 3.0 미만에서 OutofMemory 이슈 ]
AnimationDrawable로 몇가지 만든다음에 반복적으로 실행하다보면 memory가 적은 단말에서 OutofMemory가 발생한다. 검색해보니 3.0 미만에서는 명시적으로 이미지리소스를 해제해주어야 한다. 해제하는 방법은 아래와 같다.
ad.stop();
for (int i = 0; i < ad.getNumberOfFrames(); ++i){
Drawable frame = ad.getFrame(i);
if (frame instanceof BitmapDrawable) {
((BitmapDrawable)frame).getBitmap().recycle();
}
frame.setCallback(null);
}
ad.setCallback(null);
참고로 3.0 이상에서 AnimationDrawable의 리소스를 해제하게되면 같인 리소스의 Animation을 2번 실행하면 recycledBitmap을 사용했다면서 crash가 발생한다. 아마도 3.0 이상부터는 bitmap이 java 영역의 heap에 할당 되면서 캐시가 되고 있는것 같은 느낌이다. 그래서 강제로 recycle을 호출하면 안된다.
안드로이드 3.0만 되도 좋을 것 같은데.. 아직도 2.3의 사용비율이 전체에서 30%가 넘는구나..
[ 2013-10 에 추가 ]
위에처럼 명시적으로 해제 할 경우에 문제가 발생한다. 아래의 방법이 더 잘 동작한다. 리소스아이디 0을 넘김으로써 빈 drawable을 선택하게 하면 기존에 가지고 있던 리소스가 자동으로 해제되는 방식으로 안드로이드 버전에 따른 문제도 없다.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
1. NDK란
앱을 개발할 때 보통은 SDK를 이용하여 개발을 한다. 그러나 SDK 에서 지원하지 않거나, 성능상의 이슈를 해결하기 위해서 NDK를 이용한다. NDK로 할 수 있는 것들은 어떤 것이 있을까를 알아보기 위해서 NDK에서 제공하는 것들을 알아보자.
·libc (C library) headers
·libm (math library) headers
·JNI interface headers
·libz (Zlib compression) headers
·liblog (Android logging) header
·OpenGL ES 1.1 and OpenGL ES 2.0 (3D graphics libraries) headers
·libjnigraphics (Pixel buffer access) header (for Android 2.2 and above).
·A Minimal set of headers for C++ support
·OpenSL ES native audio libraries
·Android native application APIS
많구나~ ㅋㅋ.근데 이걸로 멀 할 수 있을까? 현실적인 주요 목적은 기존에 작성된c/c++ 코드로 구현된 코드를 (java로 처리가 느린 image processing 같은것이 좋은 예) 그대로 이식할 수 있게 된다. 물론 필요에 따라서 java 로직을 c/c++으로 개발 할 수도 있다.
참고로 .so 파일은 apache의 모듈로 자주 보던 형식이었는데 linux의 share object 파일으로 윈도우의dll 과 비슷한 것으로 생각하면 된다. Linex의 static library는 .a 파일이고, shared-library가 .so 파일이므로 .so 로 빌드해야 한다. Android는 .so 파일만 로딩할 수 있다.
수정 : 참고는 그냥 참고일뿐, library.mak 파일에서 link 만드는 부분 수정하면 됨. ( install-lib$(NAME)-shared 로 검색 ) 그리고 configure 파일에서 SLIBNAME_WITH_MAJOR 찾아서 수정하면됨. 삽질은 멈추지 않는다. ㅠ
[ 마치면서 ]
윈도우에서 하다가 막혀서 맥에서 해보니 맥이 android NDK 개발하는데 더 좋은 환경인 것 같습니다. 주변에서도 맥에서 android 개발하는 사람들이 꽤 되던데 저도 고려중에 있습니다.
drawableLeft, drawableRight, drawableTop, drawableBottom 등으로 component의 원하는 위치에 이미지를 설정할수 있다.drawablePadding 값은 컨트롤 내부에서 이미지의 간격을 조정하는 값이다. paddingLeft는 이미지와 텍스트사이의 간격을 설정한다.
drawableXXX 기능은 TextView에 있는 기능으로 TextView를 parent로 하는 Button 계열의 컨트롤에서는 모두 사용가능한 방법이다. CheckBox의 상속 구조는 아래와 같다.
CheckBox와 ToggleButton은 interface가 상당히 유사해서 대부분 서로 대체해서 사용할수 있으므로 checkBox의 에러가 android 4.2에서 수정되었으므로 image와 text를 표시해야 하는 부분에서checkbox 대신에 ToggleButton을 사용하길 권장한다.