Storage Access Framework을 통한 이미지 가져오기

marojun
marojun’s Android
6 min readDec 5, 2013

--

안드로이드 킷캣(4.4)부터 파일관리는 Storage Access Framework을 통해 접근할 수 있도록 변경되었다. 물론 클라우드 및 내부 저장소까지 해당 프레임웍을 통해 같은 UI경험을 줄 수 있다는 것은 고무적이나 기존 파일에 접근하는 코드들은 4.4변경사항에 따른 이미지 불러오기 이슈와 같이 4.4에서는 제대로 동작하지 않을 수 있기에 분기문을 통해 코드를 적용해야 하는 번거로움이 있다.

여기서는 이에 4.4 이상에서 사용될 이미지를 가져오는 코드에 대해 알아보도록 하겠다.

Storage Access Framework에 대한 좀 더 자세한 사항은 스토리지 액세스 프레임웍을 통해 파일 마스터가 되자! 를 참조하자.

// 파일을 가져오기 위해 ACTION_OPEN_DOCUMENT을 사용한다.
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
// 이후 파일중 open가능한 것들로 카테고리를 묶기 위해 CATEGORY_OPENABLE을 사용한다.
intent.addCategory(Intent.CATEGORY_OPENABLE);

// 이제 Storage Access Framework에서 제공하는 UI에 노출될 MIME을 지정한다. 여기서는 이미지를 기준으로 작업하므로 image/라고 표기했지만
// 오디오를 가지고 오고 싶다면 audio/를 사용하며 오디오 파일형식 중에서도 ogg파일만을 보고 싶다면 audio/ogg라고 명시한다. 만약 모든 파일을 보고 싶다면 */*로 표기하면 된다.
intent.setType(“image/*”);

// 결과를 onActivityResult()로 전달받기 위해 startActivityForResult로 실행한다.
startActivityForResult(intent, READ_REQUEST_CODE);
파일에 접근할 수 있도록 도와주는 Picker

위 그림처럼 원하는 사진을 선택하면 onActivityResult통해 해당 이미지의 Uri를 전달받는다.

public void onActivityResult(int requestCode, int resultCode, Intent resultData) {

// ACTION_OPEN_DOCUMENT 인텐트로 호출된 경우 requestCode는 READ_REQUEST_CODE로 응답 받기 때문에 아래와 같이 if문에서 requestCode 코드를 처리해준다.
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
// resultData에 있는 uri값을 통해 이미지를 뷰에 노출 시키도록한다.
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
showImage(uri);
}
}
}
/*
전달받은 uri를 통해 Bitmap을 사용하도록 하는 작업을 진행한다. 해당작업을 쓰레드를 통해 작업하지 않으면 해당작업이 끝날때 까지 UI가 멈출수 있으므로 쓰레드를 통해 해당작업을 진행해야한다. 그러나 쓰레드 작업시 UI를 핸들링을 하게 되면(여기서는 이미지 뷰에 이미지를 활성화 하기위해 setImageBitmap()등을 사용하면) “Only the original thread that created a view hierarchy can touch its views.”라는 에러가 발생하게된다. 이러한 패턴의 작업은 빈번히 사용되기에 안드로이드에서는 개발자가 번거롭지 않도록 AsyncTask를 통해 이런 작업을 손쉽게 할 수 있도록 하였다. 이를통해 doInBackground()에서 백그라운드에 작업들을 수행하고 onPostExecute()에서 doInBackground()에서 진행된 작업들의 결과를 바탕으로 UI작업들을 수행 하면된다.
*/
private void showImage(URI uri){
AsyncTask<Uri, Void, Bitmap> imageLoadAsyncTask = new AsyncTask<Uri, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(Uri… uris) {
return getBitmapFromUri(uris[0]);
}

@Override
protected void onPostExecute(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
}
};
imageLoadAsyncTask.execute(mUri);
}
/* Uri를 Bitmap으로 쓸 수 있게해준다. */
private Bitmap getBitmapFromUri(Uri uri) {
ParcelFileDescriptor parcelFileDescriptor = null;
try {
parcelFileDescriptor =
getActivity().getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
parcelFileDescriptor.close();
return image;
} catch (Exception e) {
Log.e(TAG, "Failed to load image.", e);
return null;
} finally {
try {
if (parcelFileDescriptor != null) {
parcelFileDescriptor.close();
}
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Error closing ParcelFile Descriptor");
}
}
}

--

--

marojun
marojun’s Android

전슬마로. KTH, SK Planet, NCSOFT 에서 iOS와 Android를 개발하고 있다. — 안드로이드 개발 그룹 https://www.facebook.com/groups/junsle/