Abstract
안드로이드에서 미디어가 담긴 파일시스템을 붙이거나, 시스템을 최초 부팅하면 저장소 내의 미디어 파일들을 스캔하여 라이브러리를 구축해 준다. 스캐닝 과정과 원리를 세부적으로 살펴보려 한다.
Overview
시작은 MediaScannerReceiver 에서 부터다. ACTION_BOOT_COMPLETED, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SCANNER_SCAN_FILE 의 인텐트에 대해 반응하고 처리를 시작한다. 각 인텐트는 다음과 같은 상황에서 발생한다.
- ACTION_BOOT_COMPLETED : 시스템이 부팅을 마치고 나면 딱 한번 브로드캐스트하는 것이다.
- ACTION_MEDIA_MOUNTED : external media 가 있고 마운트가 됐을 때 브로드캐스트 된다.
- ACTION_MEDIA_SCANNER_SCAN_FILE : 미디어 스캐너로 하여금 해당 파일을 스캔하고 데이터베이스에 추가하도록 한다.
이때 실질적인 처리는 MediaScannerService 에 맡기게 되는데, 그 이유는 스캐닝에 제법 긴 시간이 필요하기 때문이다.
MediaScannerService 는 MediaScanner 라는 객체를 만들어 실제적인 스캔작업이 이뤄지고, MediaScanner 는 MediaProvider 라는 미디어 데이터베이스를 이용한다.
다시 실제 스캐닝의 핵심 작업은 c++ 로 이루어진 라이브러리에서 이뤄지고, 이 인터페이스는 JNI 로 노출되어 있다.
MediaScannerReceiver
MediaScannerReceiver 는 아래 두 폴더에서 스캔을 진행한다.
- internal volume, $(ANDROID_ROOT)/media
- external volume, $(EXTERNAL_STROAGE)
public class MediaScannerReceiver extends BroadcastReceiver { ... private void scan(Context context, String volume) { Bundle args = new Bundle(); args.putString("volume", volume); context.startService( new Intent(context, MediaScannerService.class).putExtras(args)); } private void scanFile(Context context, String path) { Bundle args = new Bundle(); args.putString("filepath", path); context.startService( new Intent(context, MediaScannerService.class).putExtras(args)); } ... }
MediaScannerReceiver 내에 scan() 과 scanFile() 을 보면 결국 context 를 통해 startService() 를 호출하는 것을 알 수 있는데, 이때 호출되는 서비스는 위에서 얘기했다시피 MediaScannerService 이다.
MediaScannerService
MediaScannerService 에서 스캔 작업은 쓰레드를 생성하여 처리한다. 쓰레드가 생성되면 Runnable 인터페이스의 run() 을 호출하게 된다. ServiceHandler 라는 internal class 를 생성하여 서비스 요청을 처리하는데, 여기에서 다시 MediaScanner 라는 클래스의 인스턴스를 생성하여 스캔 작업을 시킨다.
MediaScanner 와 JNI 로 넘어가는 부분은 다시 새로운 포스팅으로 다룰 예정.