ㅈㅅㄹ

안드로이드에선 패키지 내에 파일을 포함시켜서 어플리케이션에서 사용할 수 있도록 해 주는 기능이 있으며 이렇게 어플리케이션 패키지에 첨부된 파일을 Asset이라 부르고 있다. 아시는 분들은 다 아시겠지만 asset을 패키지에 포함시키는 방법은 쉽다. 일반적인 안드로이드 SDK 환경에서 개발하고 있다면, 그저 안드로이드 프로젝트 아래에 assets 폴더를 만들고 거기에 파일을 집어넣기만 하면 된다. 물론 assets 아래에 폴더를 더 만들어 집어 넣는 것도 가능하다.


이렇게 패키지에 포함시킨 파일은 안드로이드에서 제공하는 AssetManager를 통해 읽어와서 사용할 수 있다. 사용 방법도 간단하다. Context의 getAssets()를 통해 AssetManager를 가져와서 asset 경로를 지정해 주고 open() 하면 해당 asset에 대한 InputStream을 가져 올 수 있다.

AssetManager am = context.getAssets();
InputStream in = am.open("assetFile.txt");
뭐 여기까지는 많은 책자를 통해서 소개하고 있는 부분이지만, 안드로이드 asset은 사용하기에 어느정도 까다로운 구석이 있어 주의깊게 사용할 필요가 있다. 물론 이러한 부분들은 잘 문서화되어 있지도 않아[각주:1] 실제로 사용할 때 문제가 되기도 한다.

안드로이드 SDK에서 apk를 묶을 때, aapt는 몇 가지 확장자를 제외하고는 압축을 시도한다. 아래와 같은 확장자를 가지지 않은 파일의 경우 거의 대부분 해당 파일을 압축 해서 패키징하게 된다. 물론 압축을 했을 경우의 사이즈가 압축하기 전 보다 더 커지는 작은 사이즈의 파일의 경우도 제외되긴 한다만.

/* these formats are already compressed, or don't compress well */
static const char* kNoCompressExt[] = {
    ".jpg", ".jpeg", ".png", ".gif",
    ".wav", ".mp2", ".mp3", ".ogg", ".aac",
    ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
    ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
    ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
    ".amr", ".awb", ".wma", ".wmv"
};

내부적으로 압축을 하건 말건 그게 뭔 상관이냐 할 수도 있겠지만, asset으로 넣은 파일이 압축이 되면 여러가지 피곤한 상황을 맞닥뜨릴 수 있게 된다.

1. 우선적으로 파일 사이즈가 1MB 이상이 될 경우 asset의 InputStream을 가져오는 것이 불가능해 질 수 있다. 물론 단말이나 에뮬레이터 바이너리가 안드로이드 소스에서 Asset.h를 고쳐서[각주:2] 사이즈를 늘였을 경우엔 크게 상관이 없으나, 구글 공식 소스에서 1MB로 제한하고 있으니[각주:3] 어떤 단말에서도 실행되길 바란다면 1MB 이상의 asset 파일을 넣는게 모험수가 된다. 그래서 만약 1MB가 넘는 파일을 asset으로 넣어서 어플 구동시 사용하고자 한다면 (1)확장자를 위 목록에 있는 것으로 속여서 패키징 때 압축이 되는 것 자체를 막거나 (2)1MB 단위로 잘라서 넣고 나중에 런타임에 이를 붙어서 데이터 영역에 파일을 하나 만들어 쓰는 고육책을 써야 한다.

2. 파일 사이즈가 작다면 AssetManager.open()을 사용해서 InputStream을 성공적으로 가져올 수 있다. 다만 특별한 경우 FileDescriptor나 FileInputStream, FileOutputStream을 가져오기 위해 openFd()를 사용해야 할 필요가 있는데, 만약 압축된 asset에 이를 호출하게 되면 FileNotFoundException을 던져준다!!! 뭐 이 경우 패키징을 할 때 위 경우와 마찬가지로 (1)압축이 되지 않게 확장자를 위의 확장자 중 아무거나 하나로 바꿔서 넣어두던가, (2)혹은 open() 을 사용하여 읽어와서 어딘가에 카피본을 하나 만들어서 사용하는 수 밖에 없다.

위의 상황에서 내가 대안으로 제시한 고육지책들이 도무지 맘에 들지 않는 깔끔쟁이들이라면 http://www.xinotes.org/notes/note/1293/ 를 참조해서 위의 배열에 있는 확장자 외에 추가로 압축되지 않는 확장자를 지정할 수 있으니 참고하자. 물론 난 그 정도로 깔끔쟁이가 아니라서 마지막 방법은 해보질 않았다.... 하하
  1. 실제로 안드로이드 developer 사이트에서 AssetManager 항목을 보면 openFdI() 계열 함수는 아예 뭐하는 놈인지 설명도 없다. [본문으로]
  2. http://zeph1e.tistory.com/1 에서 마지막 부분 참고 [본문으로]
  3. 엄밀하게 말하면 제한이라기 보단 Asset 파일을 사용할 때, 실행중에 압축을 해제해서 메모리 버퍼에 올려두는데 이 버퍼 용량이 1MB이다 [본문으로]