본문 바로가기
실시간TV∴영화∴드라마∴예능

Android 의 Storage Path 및 코드 구현 기술

반응형

안드로이드 앱에서 스토리지에 파일을 쓰고 읽기 위해서는 메니페스트 (AndroidManifest.xml) 파일에 다음과 같은 권한을 기록해줘야 한다.

< uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
< uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />


# 외장 메모리 (SD CARD)의 절대 경로 알아내기

String sdcardPath = null;
String sdcardStat = Environment.getExternalStorageState();
if(sdcardStat.equals(Environment.MEDIA_MOUNTED))
{
sdcardPath = Environment.getExternalStorageDirectory().getAbsolutePath();
}

여기에서 생각해야 할 내용이 있다. 만약 안드로이드 기기에 외장 메모리가 한개 이상인 경우에는 어떠할까...
Android 에서 제공되는 Environment 클래스에서 getExternalStorageDirectory() 함수의 코드를 살펴보자.

    public static File getExternalStorageDirectory() {
        throwIfUserRequired();
        return sCurrentUser.getExternalDirsForApp()[0];
    }

이렇게 getExternalStorageDirectory 함수는 현재의 사용자 (기기에 로그온 한 사용자, 초기에는 이런 내용이 없었는데 모바일 기기를 개인용 혹은 업무용으로 병용으로 사용할 수 있게 하기 위해서 사용자 개념을 도입함) 의 해당 앱(App) 에 제공되는 외장 디렉토리 파일들의 핸들 첫번째를 가져오게 되어 있다.

현재 대부분의 안드로이드 기기는 외장 메모리를 1개 밖에 탑재하지 못한다. 그것은 기기의 크기 및 휴대성 등을 고려하고 또한 SD 카드의 단일 용량이 계속 증가하는 추세이기 때문에 굳이 앱에서 이를 고려할 필요는 없어 보인다. 하지만, 미래에는 어떤 기기가 나올지 모르기 때문에 대처하는 현명함이 필요할 것 같다.

참고로 getExternalStorageState() 함수도 첫번째 외장 스토리지 패스의 상태를 돌려준다.

    public static String getExternalStorageState() {
        final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
        return getExternalStorageState(externalDir);
    }



이제 sdcardPath 를 가져온 결과의 예를 살펴보자.

Android SDK: 19 (4.4.4)
/storage/emulated/0

Android SDK: 21 (5.0.2)
/storage/sdcard

Android SDK: 23 (6.0)
/storage/emulated/0

이와같이 안드로이드의 빌드 버전 혹은 디바이스의 제조사 등에 따라서 다를 수 있다는것에 유의를 해야 할 것이다.

그런데 여기서 또하나 주의해야 할 사항이 있다. 과연 getExternalStorageDirectory 가 우리가 생각하는 외장 SD 카드를 뜻할까? 그렇게 오해할 수 있다. 함수 이름에 ExternalStorage 라는 내용이 있기 때문에 당연히 그렇게 오해 할 수 있다.

하지만 실상은 그렇지 않다. 여기서 말하는 External Storage 는 디바이스 장치의 내장 스토리지의 패스이다.
안드로이드는 크게 2 가지의 (혹은 그 이상)의 스토리지 영역을 가진다.

- System Storage
- External Storage(s)

"/" 로 표현되는 루트 폴더가 System 영역이다. 또한 모든 Storage Path 는 System 영역에 특정 패스로 마운트 되어진다.




# 슬롯에 꽂은 외장 메모리의 절대 경로 알아내기

아쉽게도 지금까지의 안드로이드에서 제공하는 API 중에 슬롯에 꽂아져 있는 외장 메모리의 패스 (마운트 포인트) 를 알려주는 API 는 없는것 같다.
단, 다음과 같은 방법으로 해당 패스를 알아내는 방법이다.

String legacyPath = System.getenv("EXTERNAL_STORAGE");
String secondaryPath = System.getenv("SECONDARY_STORAGE");

각각의 출력은 다음과 같다. (제조사 마다 다를 수 있음)
/storage/emulated/legacy
/storage/extSdCard

여기서 우리는 두번째 스토리지의 뜻인 "SECONDARY_STORAGE" 값으로 가져온 결과를 사용하면 될 것이다. 아쉽게도 지원이 안되는 디바이스의 경우가 많다. 따라서, 이 방법으로 앱을 만든다면 원치않게 불량 앱이 될 수 있다.

결국 다음과 같은 클래스 함수를 만들어 사용하면 된다.

public class SDCard
{
public static String getExternalSDCardPath()
{
HashSet<String> hs = getExternalMounts();
for(String extSDCardPath : hs)
{
return extSDCardPath;
}
return null;
}

public static HashSet<String> getExternalMounts()
{
final HashSet<String> out = new HashSet<String>();
//String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String reg = "(?i).*media_rw.*(storage).*(sdcardfs).*rw.*";
String s = "";
try
{
final Process process = new ProcessBuilder().command("mount").redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1)
{
s = s + new String(buffer);
}
is.close();
}
catch (final Exception e)
{
e.printStackTrace();
}

// parse output
final String[] lines = s.split("\n");
for (String line : lines)
{
if (!line.toLowerCase(Locale.US).contains("asec"))
{
if (line.matches(reg))
{
String[] parts = line.split(" ");
for (String part : parts)
{
if (part.startsWith("/"))
{
if (!part.toLowerCase(Locale.US).contains("vold") && !part.toLowerCase(Locale.US).contains("/mnt/"))
{
out.add(part);
}
}
}
}
}
}

return out;
}
}

반응형

댓글


Copyright ⓒ SmartWeb All rights reserved.