지금 화면에 배경으로 이미지를 깔아주는데
여러분들이 댓글로 답변 주셔서 bitmap 사용해서 어느정도;; 성공은 했습니다.
전에는 100% 죽었는데 이제 100% 죽지는 않네요
bitmap으로 이미지 받아오고
bitmapDrawable로 비트맵 이미지를 Drawable로 변환 시켜서 백그라운드에 적용 시켰는데요
이렇게 하니까 가로 세로 전환시 잘 됩니다.
근데 지금... 예를 들자면
A화면 가로 세로와 B화면 가로 세로 이렇게 있는데
A화면은 이미지 파일 크기가 2MB 정도 되는고 안 죽는것 같고
B화면은 이미지 파일 크기가 3MB 정도 되는데 이 화면에서만 가로세로 전환할때 죽어버리네요...
갤탭 8.9 인데... 이정도 이미지 크기면 이미지가 엄청 큰 건가요?
drawable-hdpi 이런 디렉토리일건데...
이미지를 dpi 잘 맞춰서 넣으셔야 합니다. 디자인 자체가 나오는 기준이 있을것인데
그 기준에 맞춰야 합니다.
보통 "기기 해상도 기준"이라 하면 hdpi겠지만
그 기기가 뭐냐에 따라서 xhdpi나 mdpi가 될수도 있겠죠... 그런 기준에 맞춰져 있는가가 문제라는겁니다.
모자란 지식이지만... 파일 자체의 용량은 중요하지 않다는 것에 보충 설명을 하자면...
디코딩방식이 ARGB8888 이라면 픽셀당 8bit씩 4개 데이터 즉 4byte를 먹습니다.
가로pixel * 세로pixel * 4byte = 필요한 메모리
(640*480 크기만 해도 일단 메모리를1,228,800 Bytes 먹고 들어갑니다...)
가 나오는데요 네이티브 힙이 수용가능한 크기라면 일단 표시가 됩니다.
(가장 좋은 방법은 표시 해상도 이내에서 처리하는것입니다... Bitmap option을 이용하세용...)
그런데 일단은 화면에 보이는데... 가로 세로 전환시 죽는다는것은 제가 볼때는 네이티브 힙에 있는 Bitmap이 recycle되지 않아서 생기는 현상같습니다.
(물론 3MB의 이미지를 화면에 그냥 표시하려는 것 부터 잘못된거지만... 대충 어림 잡아도 2400*2400 이상은 나오는거 같네요...)
안드로이드 생명주기상 가로세로 전환때 onDestroy를 타고 onCreate를 다시 타서 처음부터 액티비티를 다시 생성하게 됩니다.
이때 VM은 Bitmap의 참조가 0일때 비트맵을 정리를 해야합니다.
구글에 안드로이드 플랫폼 개발자가 말하기를 자동으로 정리가 된다고 하는데 구라도 이런 구라가 없습니다.
참조한 컨텍스트가 액티비티일 경우 뷰 구조등 액티비티의 모든 정보를 포함한 컨택스트가 되어버리는데요.
이때 이 컨텍스트를 이용해서 리소스를 구한다거나 하면 분명 destroy단계에서 액티비티의 모든 정보가 파기되고 그때 bitmap의 메모리는 VM이 자연스럽게 해제해주어야하는데 안되고 있습니다. ㅡㅡ;
그리고 recycle을 한다고 해도 이게 즉시 GC되는게 아니라 대상에 올라가기만 때문에 VM의GC만 믿었다가는 간헐적으로 같은현상이 발생하고 말지요...
그래서 코드가 좀 더러워지지만... 수동으로 해제해주면 해결 가능할 것 같습니다...
왜 개발자가 플랫폼 내부 처리 방식까지 알아야하는지는 모르겠으나 수동으로 비트맵을 recycle처리하고 이미지 뷰에 이미지가 표시될때 사용된 drawable에 set된 callback도 해제해주고 GC도 호출하는 것입니다...
특히 마지막의 GC호출은 지저분하기 짝이 없네요...
onDestroy에서 사용하세요. onPause등에서 사용되면 뷰가 invalidate될때 비트맵이 참조되서 recycle된 비트맵 참조했다고 Exception뜹니다.
onPause에서 꼭 사용하시려면 recycle호출전에 뷰의 이미지를 null로 셋팅하세요.
대신 갑자기 그림이 사라지는 현상을 눈으로 목격할수 있습니다.
----------------------
try {
// 이미지 뷰의 Drawable을 참조
Drawable d = iv.getDrawable();
// Drawable이 BitmapDrawable일때 Bitmap을 얻은후에 reCycle한다
if (d instanceof BitmapDrawable) {
Bitmap b = ((BitmapDrawable) d).getBitmap();
b.recycle();
}
// drawable이 생성될때 등록된 callback을 제거한다
d.setCallback(null);
}
catch (Exception e) {
e.getStackTrace();
}
Runtime.getRuntime().gc();
------------------
물론 이런 뷰들이 많이 있다면 이걸 메소드로 만들고...
최상위 뷰그룹부터 트리탐색으로 모든 뷰들의 이미지를 이렇게 지워주어야 겠죠...
칠리님 이글 정보게시판에 쓰시면 좋게네요 ㅎㅎ 구구절절이 옳은말
특히
구글에 안드로이드 플랫폼 개발자가 말하기를 자동으로 정리가 된다고 하는데 구라도 이런 구라가 없습니다.
참조한 컨텍스트가 액티비티일 경우 뷰 구조등 액티비티의 모든 정보를 포함한 컨택스트가 되어버리는데요.
이때 이 컨텍스트를 이용해서 리소스를 구한다거나 하면 분명 destroy단계에서 액티비티의 모든 정보가 파기되고 그때 bitmap의 메모리는 VM이 자연스럽게 해제해주어야하는데 안되고 있습니다. ㅡㅡ;
그리고 recycle을 한다고 해도 이게 즉시 GC되는게 아니라 대상에 올라가기만 때문에 VM의GC만 믿었다가는 간헐적으로 같은현상이 발생하고 말지요...
이부분에 공감 300%




몇메가가 중요한게 아니고 해상도 자체가 중요합니다.
이미지 열어보시고 화면 해상도의 몇배나 되나 한번 따져보세요.
화면 해상도 보다 실제 이미지의 해상도가 더 커야할 이유가 없습니다. (확대하지 않는다는 가정이 있다면요...)
만약에 그 이미지가 리소스의 이미지가 아니고 외부 파일이라면
해상도에 맞는 축소이미지를 캐시에 만들어두고 그 파일을 쓰시는것을 권장드립니다.