Problem: 

11-13 15:08:17.982: E/dalvikvm(6149): dlopen("/data/app-lib/com.kugistory-1/libkugistory.so") failed: dlopen failed: cannot locate symbol "rand" referenced by "libkugistory.so"...

11-13 15:08:17.982: W/dalvikvm(6149): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/kugistory/NativeImageProcessor;

11-13 15:08:17.982: D/AndroidRuntime(6149): Shutting down VM

11-13 15:08:17.982: W/dalvikvm(6149): threadid=1: thread exiting with uncaught exception (group=0x41b10ba8)

11-13 15:08:17.992: E/AndroidRuntime(6149): FATAL EXCEPTION: main

11-13 15:08:17.992: E/AndroidRuntime(6149): Process: com.kugistory, PID: 6149

11-13 15:08:17.992: E/AndroidRuntime(6149): java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "rand" referenced by "libkugistory.so"...

11-13 15:08:17.992: E/AndroidRuntime(6149): at java.lang.Runtime.loadLibrary(Runtime.java:364)

11-13 15:08:17.992: E/AndroidRuntime(6149): at java.lang.System.loadLibrary(System.java:526)

11-13 15:08:17.992: E/AndroidRuntime(6149): at com.kugistory.NativeImageProcessor.<clinit>(NativeImageProcessor.java:8)


안드로이드 네이티브(native) 라이브러리를 사용하려다가 위와같은 에러가 생겼다.


찾아보니 ndk-r10의 버그인 듯 하다.


다음과 같은 어떤 사람의 말로는 32-bit, 64-bit 빌드 타입과 관련된 문제인 듯 하다.

You need to use the 32-bit NDK (android-ndk32-r10b-darwin-x86_64) if building for 32-bit targets.

In the ARM headers in the 32-bit target NDK, rand() is declared as a static inline function (and as such does not need to exist as a symbol in libc.so on ARM devices).

In the ARM headers in the 64-bit target NDK, rand() is declared as a regular function, but since it does not exist in the libc.so on ARM devices, there is a runtime error.

Ergo, use the 32-bit target NDK when targeting 32-bit platforms. Or tell Google that their shit is broken.



Trials:

이것을 stl static 라이브러리에서 shared 라이브러리 이용으로 바꾸면 된다는 말을 지나가다가 보고는 다음과 같이 시도했으나 실패했다.







Solution:


해결 방법을 찾는것은 매우 힘들었지만, 해결 방법 자체는 간단했다.


다음과 같이 Application.mk에서 대상 OS들을 몇가지 더 추가하자 거짓말처럼 문제가 풀렸다.


APP_STL := gnustl_static

APP_CPPFLAGS := -frtti -fexceptions

APP_ABI := armeabi armeabi-v7a x86 mips # arm64-v8a x86_64 mips64

APP_PLATFORM := android-16

APP_OPTIM := release

NDK_TOOLCHAIN_VERSION := 4.8



참고로 Android.mk는 다음과 같이 설정해둔 상태였다.

OpenCV를 위한 설정 등 없어도 되는 줄들도 포함되어 있다.

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)


OPENCV_INSTALL_MODULES:= on

OPENCV_CAMERA_MODULES:= off

OPENCV_LIB_TYPE:= STATIC


include E:\OpenCV-2.4.9-android-sdk\sdk\native\jni\OpenCV.mk


LOCAL_MODULE    := Kugistory


FILE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp)

LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%) 

LOCAL_C_INCLUDES += $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/4.6/include

LOCAL_C_INCLUDES += $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include

LOCAL_C_INCLUDES += $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a-hard/include

LOCAL_LDLIBS += $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/libgnustl_static.a

LOCAL_LDLIBS +=  -llog -ldl -lz


include $(BUILD_SHARED_LIBRARY)


Posted by Kugi
,



안드로이드 프로젝트 테스트를 위해


새 프로젝트 만들기 메뉴의 [Android Project from Existing Code]로 안드로이드 프로젝트를 만드려고 하던 도중 다음과 같은 에러메시지가 떴다.


Error

Invalid project description


Reason:

Invalid project description.



[Details >>] 를 확인해보니 간단한 문제였다.


import 할 소스코드들을 프로젝트 디렉토리에 옮겨두고 새로운 프로젝트를 만드려고 시도해서


동일한 이름의 폴더가 이미 존재하기때문에 생긴 에러였다.


원본 폴더를 다른 폴더로 옮겨두고 다시 시도하니 문제없이 새로운 프로젝트가 생성되었다.



Posted by Kugi
,



안드로이드 폰에서 카메라 동영상을 연속적으로 촬영할 수 있도록 하기 위해 여러 인터넷 소스코드들을 실험해보던 도중


안드로이드펍에서 찾은 어느 소스[각주:1]를 돌려보았더니 MediaRecorder의 start() 부분에서 start failed 에러가 생겼다.


start failed: -19


구글링을 통해 다양한 해결 방법을 시도하다가 찾아낸 성공적인 해결 방법[각주:2]


setVideoFrameRate(...) 부분을 제거하는 것이었다.


내가 사용한 코드에는 프레임레이트 설정을 16으로 지정하도록 되어있었는데 이 부분을 주석처리하니 제대로 동작하였다.









  1. http://www.androidpub.com/431977 [본문으로]
  2. http://stackoverflow.com/a/11281902 [본문으로]
Posted by Kugi
,



안드로이드에서 동영상 목록을 뿌려주려고 Cursor 객체를 받아오려는데


managedQuery(Uri, String[], String, String[], String) 함수를 사용하려고 했더니 다음과 같은 deprecated되었다는 경고메시지가 떴다.


The method managedQuery(Uri, String[], String, String[], String) from the type Activity is deprecated



그냥 사용 가능하지만 거슬리기때문에 수정할 필요가 있겠다.


(Activity를 상속한 클래스 내에서라면) 경고메시지를 없애기 위해 다음의 메소드로 변경해주면 된다.


물론 여기서, 메소드 호출할 때의 파라미터는 각자의 용법에 맞게 고쳐 쓰도록 한다.


getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null, null);



이 메소드의 원형은 다음과 같다.

Cursor android.content.ContentResolver.query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)





참고: http://stackoverflow.com/questions/12714701/deprecated-managedquery-issue

Posted by Kugi
,



안드로이드의 ViewPager 및 BitmapFactory를 이용해서 몇 종류의 이미지들을 모아 보여주는 기능을 구현하였는데

다음과 같이 OOM(Out Of Memory Error)를 맞닥뜨렸다.

 

07-08 00:21:48.309: E/dalvikvm-heap(28645): Out of memory on a 13498800-byte allocation.
07-08 00:21:48.309: I/dalvikvm(28645): "main" prio=5 tid=1 RUNNABLE
07-08 00:21:48.309: I/dalvikvm(28645):   | group="main" sCount=0 dsCount=0 obj=0x40cfc460 self=0x1969d88
07-08 00:21:48.309: I/dalvikvm(28645):   | sysTid=28645 nice=0 sched=0/0 cgrp=default handle=1075004776
07-08 00:21:48.309: I/dalvikvm(28645):   | schedstat=( 0 0 0 ) utm=789 stm=548 core=1
07-08 00:21:48.309: I/dalvikvm(28645):   at android.graphics.Bitmap.nativeCreate(Native Method)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:374)
07-08 00:21:48.319: I/dalvikvm(28645):   at

...

 

 

07-08 00:21:48.749: E/AndroidRuntime(28645): FATAL EXCEPTION: main
07-08 00:21:48.749: E/AndroidRuntime(28645): java.lang.OutOfMemoryError
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.Bitmap.nativeCreate(Native Method)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:374)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at

...

 

이 문제를 해결하기 위해서 이틀 정도를 고생했다.

 

원인은 예상치 못했던 곳에 있었다.

 

어느 정도 감이 있는 사람이라면 다음의 로그를 보고 알아차릴 수 있을 것이다.

 

이 로그는 어플리케이션을 실행해서 이미지들을 보여주는 기능을 수 차례 반복 수행 하면서 기록된 것이다.

 

 

07-08 00:18:30.756: I/_PracticePagerAdapterClass(28645): parent: onPageSelected()=4
07-08 00:18:31.357: I/_PracticePagerAdapterClass(28645): destroyItem: position=1/13, bitmapSize=8191152
07-08 00:18:31.527: I/_PracticePagerAdapterClass(28645): instantiateItem: position=6/13, bitmapSize=8191152
07-08 00:18:43.208: I/_PracticePagerAdapterClass(28645): parent: onPageSelected()=5
07-08 00:18:43.839: I/_PracticePagerAdapterClass(28645): destroyItem: position=2/13, bitmapSize=8992608
07-08 00:18:44.109: I/_PracticePagerAdapterClass(28645): instantiateItem: position=7/13, bitmapSize=18348016
07-08 00:19:15.260: I/_PracticePagerAdapterClass(28645): parent: onPageSelected()=4
07-08 00:19:16.191: I/_PracticePagerAdapterClass(28645): instantiateItem: position=2/13, bitmapSize=8992608
07-08 00:19:16.191: I/_PracticePagerAdapterClass(28645): destroyItem: position=7/13, bitmapSize=18348016
07-08 00:19:32.987: I/_PracticePagerAdapterClass(28645): parent: onDestroy()
07-08 00:19:40.224: I/_PracticePagerAdapterClass(28645): parent: onCreate()
07-08 00:19:41.075: I/_PracticePagerAdapterClass(28645): instantiateItem: position=0/13, bitmapSize=13498784
07-08 00:19:42.716: I/_PracticePagerAdapterClass(28645): instantiateItem: position=1/13, bitmapSize=8191152
07-08 00:19:42.897: I/_PracticePagerAdapterClass(28645): instantiateItem: position=2/13, bitmapSize=8992608
07-08 00:19:54.408: I/_PracticePagerAdapterClass(28645): parent: onDestroy()
07-08 00:20:00.544: I/_PracticePagerAdapterClass(28645): parent: onCreate()
07-08 00:20:01.435: I/_PracticePagerAdapterClass(28645): instantiateItem: position=0/13, bitmapSize=13498784
07-08 00:20:01.585: I/_PracticePagerAdapterClass(28645): instantiateItem: position=1/13, bitmapSize=8191152
07-08 00:20:01.825: I/_PracticePagerAdapterClass(28645): instantiateItem: position=2/13, bitmapSize=8992608
07-08 00:20:06.319: I/_PracticePagerAdapterClass(28645): parent: onDestroy()
07-08 00:21:47.618: I/_PracticePagerAdapterClass(28645): parent: onCreate()

...

 

다음의 이미지에서 확인할 수 있듯이, 페이지는 항상 Instantiate 되었지만 정작 Destroy 되는 순간은

 

현재 보고 있는 페이지가 이동하면서 준비된 페이지의 수가 설정된 최대 준비페이지 수(여기서는 5개)보다 많아졌을 때 뿐이었다.

 

따라서 각각의 Page에 연결되었던 Bitmap Drawable들은 해제되지 못한 채 가비지 컬렉터(GC)에서 처리되지 못 하고 마는 것이다.

 

 

 

 

 

그래서 나는 ViewPager를 포함한 parent에서 onDestory()가 호출될 때 pagerAdapter에 따로 만들어 둔 비트맵 리사이클 메서드를 호출하도록 하였다.

 

결과는 만족스러웠다. 몇 번 액티비티를 들락날락거리면 죽어버리던 앱이 몇 십번을 반복해도 이상이 없었다.

 

참고로, 어떤 식으로 해결했는지에 대해 말하자면

 

adapter 클래스에 전체 페이지 갯수만큼의 크기의 boolean 배열과 ImageView 배열을 만들어서

 

각각의 위치의 페이지들이 instantiate 되었는지 아니면 destory 되었는지 체크해 두었다가

 

parent Activity의 onDestroy()에서 해당 배열을 처리하는 메서드를 호출하는 방법을 사용하였다.

 

예를 들면 다음과 같은 소스코드이다.

 

 

 

 

 

덧붙여 말하자면, ViewPager를 좌, 우로 넘기기만 헀을 뿐인데도 메모리 에러가 생기는 경우는

 

destroyItem()에서 리소스의 뒷처리를 하지 않았기 때문일 가능성이 크다.

 

Posted by Kugi
,