본문 바로가기
Android

[깡쌤의 안드로이드 프로그래밍 with 자바 - 2022 - 쌤즈] 정리 22 - 카메라 앱 연동, FileProvider

by 들풀민들레 2022. 5. 9.

책의 모든 내용을 저자 직강으로 진행한 강의는 ssamz.com 에서 들으실 수 있습니다.

 

 

본 글은 [깡쌤의 안드로이드 프로그래밍 with 자바 - 2022 - 쌤즈] 의 내용을 발췌한 것입니다.
좀더 자세한 내용은 책 혹은 인강(www.ssamz.com)을 통해 확인해 주세요.

 

12.2.2. 카메라 앱 연동


카메라 앱 또한 연동하여 유용하게 사용됩니다. 개발하는 앱에 사진을 찍거나 동영상을 촬영하는
기능이 포함되어야 할 때, 카메라 앱을 연동하면 편리합니다. 카메라 앱의 사진을 찍는 화면도 결국
액티비티이므로 인텐트를 발생시키는 것만으로도 쉽게 촬영 기능과 촬영된 데이터를 얻을 수 있습니다.
이때, 카메라 앱을 연동하여 촬영한 데이터를 획득하는 방법에는 섬네일로 받는 방법과 파일 공유
방법이 있습니다.


섬네일로 결과를 받는 방법을 사용하면, 연동한 카메라 앱으로 촬영한 결과 데이터만 앱에서
전달받습니다. 촬영된 데이터는 파일로 저장되지 않으며, 데이터 전달 방식이므로 사진 데이터의 크기가
작게 전달됩니다.


파일 공유 방식을 사용하면, 연동한 카메라 앱으로 촬영한 사진 데이터의 정보가 공유된 파일에
저장됩니다. 연동한 앱과 파일을 공유하고, 파일에 저장된 정보로 사진 데이터를 추출해야 하므로 다소
복잡한 코드로 구현됩니다. 하지만 촬영된 사진 파일을 그대로 전달받을 수 있습니다.


섬네일로 결과 받기

 

 

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraThumbnailLauncher.launch(intent);

Action 문자열을 MediaStore.ACTION_IMAGE_CAPTURE로 지정하고 인텐트를 발생시켜
간단하게 카메라 앱의 액티비티를 실행할 수 있습니다. 결괏값은 onActivityResult () 함수로 얻습니다.

 

ActivityResultLauncher<Intent> cameraThumbnailLauncher = registerForActivityResult(
	new ActivityResultContracts.StartActivityForResult(),
	new ActivityResultCallback<ActivityResult>(){
		@Override
		public void onActivityResult(ActivityResult result) {
			Bitmap bitmap = (Bitmap) result.getData().getExtras().get("data");
		}
	}
);

촬영한 결과는 result.data.getExtras ().get ("data")로 얻으며, 전달되는 타입은 Bitmap입니다.


파일 공유 방법


섬네일로 결과를 받는 방식으로 촬영된 사진 데이터를 받을 수 있지만, 문제는 이미지 데이터가
너무 작게 전달된다는 것입니다. 필자가 테스트한 스마트폰에서는 사진 데이터가 189x252 크기로
전달되었습니다. 스마트폰 카메라의 사양에 따라 차이는 있겠지만, 결국 섬네일 이미지 데이터만
전달되는 것은 마찬가지입니다.


크기를 줄이지 않고 촬영한 크기 그대로의 사진 데이터를 이용해야 하는 경우도 있습니다. 이를위해
파일 정보를 공유하는 방법을 이용해야 합니다. 파일 공유 방법은 개발한 앱에서 임의의 경로에 파일을
하나 만드는 것으로 시작합니다. 해당 파일의 경로를 카메라 앱에 전달하고, 카메라 앱에서 촬영 데이터
정보를 파일에 쓰고(write) 성공 여부를 반환합니다.


우선, 파일을 하나 만들고 이 파일 정보를 외부 앱, 즉 카메라 앱에 넘겨주어야 합니다. 파일 정보를
외부 앱에 전달하기 위해서는 content:// URI를 보내고, 이 URI에 대해 임시 액세스 권한을 부여해야
합니다. 이 권한을 쉽게 부여하려면 FileProvider 클래스를 이용합니다.


FileProvider 클래스는 androidx에서 제공하는 콘텐츠 프로바이더로, XML 설정을 기반으로 파일에
대한 Content URI를 생성해줍니다. FileProvider를 이용하려면 res/xml 폴더에 임의의 이름으로된
XML 파일을 만들어 아래의 내용을 작성해야 합니다.

 

<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="myfiles" path="Android/data/com.example.test4_12/files/Pictures"/>
</paths>

<external-path> 태그는 외부 저장 공간의 파일을 공유하기 위해 사용되며, 내부 저장 공간에 대한
공유는 <file-path> 태그를 이용합니다. 이렇게 작성한 XML 파일을 AndroidManifest.xml에서
FileProvider를 등록할 때 설정해줍니다.

 

<provider android:name="androidx.core.content.FileProvider"
	android:authorities="com.example.test4_12.fileprovider"
	android:exported="false"
	android:grantUriPermissions="true">
	<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
		android:resource="@xml/file_paths"></meta-data>
</provider>

FileProvider는 개발자가 작성한 콘텐츠 프로바이더가 아닌 androidx 라이브러리에서 제공하는
클래스입니다. authorities 속성에 유일성이 확보된 식별자 문자열을 하나 선언해줍니다. 위의
코드에서는 앱의 패키지명을 이용하여 선언하였습니다. 그리고 <meta-data> 태그로 정의한 XML
파일의 정보를 설정합니다. 이렇게 FileProvider를 등록하고 자바 코드에서 공유하려는 파일 정보의
Uri 값을 가져옵니다.

 

Uri photoURI = FileProvider.getUriForFile(
	this,
	"com.example.test4_12.fileprovider",
	file
);

얻은 Uri 값을 카메라 앱 실행을 위한 인텐트의 Extra 데이터로 설정합니다.

 

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
cameraFileLauncher.launch(intent);

이렇게 하면 파일 공유 방식으로 카메라 앱을 이용할 수 있습니다.


이미지 로딩으로 인한 OutOfMemoryException 문제


카메라 앱 연동에 대해 다루다 보니 외부 앱 파일 공유 이야기도 나왔는데요. 이미지에 관해 조금 더
살펴보아야 할 것 같습니다. 여기서 다루는 이미지의 OutOfMemoryException 해결은 카메라 앱
연동에만 관련된 문제는 아니므로 잘 정리해두어야 합니다.


안드로이드를 개발하다 보면, 크기가 큰 데이터를 로딩하다가 앱의 메모리 부족으로 실행 도중에에러가
발생하는 경우가 많습니다. 서버로부터 내려받은 이미지나 카메라로 찍은 사진 이미지는 데이터의
크기가 클 수 있습니다. 이러한 이미지를 화면에 출력하기 위해 로딩하면 OutOfMemoryException이
자주 발생합니다.


결국, 이 문제를 피하려면 이미지의 크기를 줄여서 로딩해야 합니다. 이미지 크기를 줄이는 방법은
API에서 제공하므로 쉽게 작업할 수 있습니다.

 

Bitmap bitmap = BitmapFactory.decodeFile(filePath.getAbsolutePath());

Bitmap은 이미지를 표현하는 클래스로 BitmapFactory 클래스로 생성하고, BitmapFactory의
decodeXXX() 함수로 생성하여 이용합니다.

 

  • BitmapFactory.decodeByteArray(): byte[] 배열로 Bitmap 생성
  • BitmapFactory.decodeFile(): 파일 경로로 FileInputStream을 만들어서 decodeStream 이용
  • BitmapFactory.decodeResource(): Resource 폴더에 저장된 파일
  • BitmapFactory.decodeStream(): InputStream으로 Bitmap 생성

이때 옵션을 설정할 수 있습니다. 특히, Options 클래스의 inSampleSize 속성이 중요합니다. 이
속성값을 decodeXXX() 함수의 두 번째 매개변수에 option으로 지정하면, 이미지 크기를 자동으로
줄여서 로딩합니다.

 

BitmapFactory.Options imgOptions = new BitmapFactory.Options();
imgOptions.inSampleSize = 10;
Bitmap bitmap = BitmapFactory.decodeFile(filePath.getAbsolutePath(), imgOptions);

위의 코드에서는 이미지의 크기를 10으로 지정하였는데요. 이는 전달받은 이미지를 10분의 1로 줄여서
로딩하라는 의미입니다. 이렇게 간단하게 이미지 크기를 줄여서 OutOfMemoryException 문제를
해결할 수 있습니다.

 

 

책의 모든 내용을 저자 직강으로 진행한 강의는 ssamz.com 에서 들으실 수 있습니다.