안드로이드 애셋(Asset) 사용하기. (How to use the Android Asset)

2017. 4. 26. 20:46


1. 안드로이드 애셋(Asset)

안드로이드 앱에서 사용되는 "파일"은 여러 경로를 통해 제공될 수 있습니다. 앱의 설정 내용을 저장하기 위해 앱 내에서 직접 파일을 만들 수도 있고, 인터넷으로 연결된 웹서버로부터 HTTP 요청을 통해 파일을 가져올 수도 있습니다. 이 외에도 앱에서 사용되는 파일을 제공하는 방법은 여러가지가 있는데, 여기서는, 미리 만들어 둔 XML 파일을, 앱을 릴리즈할 때 APK(Android Package Kit) 파일에 포함시킨 다음, 앱에서 사용할 수 있게 만드는 방법에 대해 살펴보도록 하겠습니다.


파일을 APK(Android Package Kit)에 포함시켜 앱에서 사용할 수 있도록 만든 것을 안드로이드 애셋(Asset)이라고 합니다. 에셋(Asset)의 사전적 의미가 "자산"이라는 것을 떠올려보면, 앱이 릴리즈 되는 시점에, 앱 자신이 온전히 사용할 수 있는 파일들을 앱의 "자산", 즉, 애셋(Asset)이라고 할 수 있는 것이죠.


안드로이드 애셋(Asset)의 파일을 사용하는 과정은 크게 세 단계로 나뉩니다. 첫 번째는 프로젝트에 애셋(Asset) 폴더를 추가하는 것이고, 두 번째는 추가된 애셋(Asset) 폴더에 파일을 포함시키는 것입니다. 그런 다음 마지막으로, AssetManager 클래스를 사용하면, 앱 실행 시, 에셋 폴더에 포함된 파일을 읽을 수 있습니다.

안드로이드 애셋(Asset) 사용 과정


1.1 안드로이드 애셋(Asset) 폴더 만들기.

미리 만들어놓은 파일을 안드로이드 애셋(Asset)에 추가하려면, 안드로이드 애셋을 위한 폴더에 파일을 복사하기만 하면 됩니다. 그러면 추가적인 작업을 하지 않아도, 앱 빌드 시 애셋 폴더 전체가 자동으로 APK(Android Package Kit)에 포함됩니다.


기본적으로 안드로이드 애셋(Asset) 파일들이 저장되는 폴더의 경로는 "src/main/assets/"입니다. 그런데 안드로이드 스튜디오에서 생성된 프로젝트의 내용을 보면 있어야 할 "assets" 폴더가 보이지 않습니다. 안드로이드 스튜디오에서는 "assets" 폴더를 기본적으로 만들지 않기 때문인데요, 그래서 개발자가 "assets" 폴더를 직접 추가해야 합니다.


그렇다고 탐색기에서 직접 폴더를 생성해야 하는 것은 아니고, 안드로이드 스튜디오 메뉴를 통해 애셋 폴더를 추가할 수 있습니다. 애셋 폴더를 추가하려면 프로젝트 뷰 화면에서 마우스 오른쪽 버튼을 누른 다음, New - Folder - Assets Folder 메뉴를 선택하면 됩니다.

Assets Folder 추가 메뉴


애셋(Asset)이 저장될 폴더를 지정하는 화면이 나타나면, 기본 설정인 "main"을 선택한 상태로 "Finish" 버튼을 선택합니다.

Assets Folder 추가


그러면 앱 프로젝트에 "assets" 폴더가 추가된 것을 확인할 수 있습니다.

추가된 Assets 폴더



참고로, 에셋 폴더를 추가할 때 기본 경로인 "src/main/assets/"만 사용할 수 있는 것은 아닙니다. 즉, 애셋 폴더의 이름을 개발자가 직접 지정할 수 있죠. "애셋 폴더 추가 대화상자"에서 "Change Folder Location" 체크박스를 선택하면 됩니다. 그러면 아래와 같이 폴더 경로를 지정할 수 있는 화면으로 바뀝니다.

애셋 폴더 직접 입력


이렇게 애셋 폴더를 직접 지정할 수 있는 것은, 폴더 경로에 대한 정보가 하나의 고정된 값이 아니라 프로젝트 설정에서 관리되기 때문인데요, "build.gradle" 파일에 그 경로가 기록되어 있습니다.

build.gradle의 애셋 폴더 정보


1.2 안드로이드 애셋(Asset) 폴더에 파일 추가하기.

애셋(Asset) 폴더를 만들고나면, 다음 할 일은 애셋(Asset) 폴더에 파일을 추가하는 것입니다. 미리 만들어진 파일을 추가하는 방법은 프로젝트의 Drawable 리소스 폴더에 이미지 파일을 넣는 방법과 같습니다.
즉, 탐색기에서 복사할 파일을 선택하고 Ctrl + C를 누른 다음, 프로젝트의 "assets" 폴더 아이콘을 선택하고 Ctrl + V를 누르면 됩니다.


만약, 텍스트 파일을 직접 작성하여 추가하는 경우라면, "assets" 폴더에서 마우스 오른쪽 버튼을 누른 다음, New - File을 선택하여 파일을 추가하고 파일의 내용을 직접 작성할 수 있습니다.

Assets 폴더에 파일 추가하기


여기까지가 애셋(Asset) 폴더에 파일을 추가하는 과정이며, 추가된 파일은 앱을 빌드하면 자동으로 APK 파일에 포함됩니다.

1.3 안드로이드 애셋(Asset)으로부터 파일 읽기.

자, 그럼 이제 마지막 과정으로 프로젝트의 애셋(Asset) 폴더에 추가된 파일을 여는 방법을 설명하겠습니다.


일반적으로 안드로이드에서 파일을 다룰 때는 파일이 저장된 경로명을 사용하여 직접 파일에 접근합니다. [안드로이드 바이너리(binary) 파일 입출력 2]에서 설명했듯이, 파일의 절대 경로명("/data/user/0/[PACKAGE-NAME]/files/" + "FILENAME") 또는 Context의 getFilesDir() 함수와의 조합(getFilesDir() + "FILENAME")을 통해 파일 스트림을 열 수 있습니다.


하지만 애셋(Asset) 폴더에 들어 있는 파일은 경로를 통해 직접 접근할 수 없습니다. 대신, AssetManager 클래스를 통해서 폴더에 저장된 파일을 열 수 있습니다.


앱에서 AssetManager 클래스 객체 참조를 획득하는 방법은 Context의 getResources() 함수를 통해 Resources 클래스 객체의 참조를 획득한 다음, Resources의 getAssets() 함수를 호출하는 것입니다.

Context.getResource()와 Resources의 getAssets()


그리고 AssetManager 객체의 참조를 획득하고 나면, AssetManager의 open() 함수를 사용하여 파일 입력 스트림을 열 수 있습니다.


아래는 AssetManager 클래스 객체를 가져온 다음, 애셋 폴더에 저장된 "FILE"의 스트림을 여는 예제 코드입니다.

    AssetManager를 사용하여 애셋(Asset) 파일 열기.
    AssetManager am = getResources().getAssets() ;
    InputStream is = null ;

    try {
        is = am.open("FILE") ;

        // TODO : use is(InputStream).

    } catch (Exception e) {
        e.printStackTrace() ;
    }

    if (is != null) {
        try {
            is.close() ;
        } catch (Exception e) {
            e.printStackTrace() ;
        }
    }

2. 안드로이드 애셋(Asset) 사용하기

아주 간단한 앱 작성 예제를 통해 안드로이드 애셋(Asset)에 저장된 파일을 읽는 구체적인 방법을 알아보겠습니다.


예제를 통해 만들어지는 앱의 기능은 매우 간단합니다. 앱 시작 시, 애셋(Asset) 폴더에 저장된 텍스트 파일을 읽은 다음, 화면에 배치된 텍스트뷰에 표시하는 기능만 갖추고 있습니다.


2.1 안드로이드 애셋에 파일 추가

가장 먼저 할 일은 안드로이드 애셋에 텍스트 파일을 추가하는 것입니다. 앞서 설명한대로 애셋(Asset) 폴더를 추가하고, 텍스트 파일을 작성합니다.

[STEP-1] 안드로이드 애셋(Asset) 폴더 추가 및 텍스트 파일 작성.

안드로이드 애셋(Asset) 폴더 및 텍스트 파일 추가 과정


예제에서 사용되는 텍스트 파일(file.txt)의 내용은 아래와 같습니다.

This is a Example of the Android Asset.
the Asset folder can be accessed by AssetManager.
AssetManager allows you to open and read raw files.

2.2 MainActivity 레이아웃 작성

다음, MainActivity의 레이아웃 리소스 XML 파일을 작성합니다.

[STEP-2] "activity_main.xml" - MainActivity의 레이아웃 구성.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.ppottasoft.assetexample.MainActivity"
    tools:showIn="@layout/activity_main">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/text1"
        android:text=""
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

2.3 앱 시작 시, 애셋(Asset) 파일 읽어서 화면에 표시하기.

이제 MainActivity의 onCreate() 함수에서 애셋(Asset) 폴더에 저장된 파일을 여는 코드를 작성합니다. 그리고 파일의 내용은 화면의 텍스트뷰에 표시합니다.

[STEP-3] "MainActivity.java" - onCreate() 함수에서 애셋(Asset) 폴더의 파일 읽기
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 코드 계속 ...

        AssetManager am = getResources().getAssets() ;
        InputStream is = null ;
        byte buf[] = new byte[1024] ;
        String text = "" ;

        try {
            is = am.open("file.txt") ;

            if (is.read(buf) > 0) {
                text = new String(buf) ;
            }

            TextView textView = (TextView) findViewById(R.id.text1) ;
            textView.setText(text) ;

            is.close() ;
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (is != null) {
            try {
                is.close() ;
            } catch (Exception e) {
                e.printStackTrace() ;
            }
        }
    }
}

3. 예제 실행 화면

예제를 실행하면 아래 그림과 같이, 애셋(Asset) 폴더에 추가한 텍스트 파일의 내용이 화면에 표시된 것을 확인할 수 있습니다.

Asset 예제 실행 화면


4. 안드로이드 애셋(Asset) 사용 시 주의 사항.

4.1 애셋(Asset) 폴더는 읽기 전용.

안드로이드 애셋(Asset)에 추가된 파일은 읽기만 가능(Read Only)합니다. 그래서 애셋(Asset)에 포함된 파일을 기반으로 내용을 수정 또는 추가하려면, 해당 파일을 읽기/쓰기가 모두 가능한 폴더로 옮긴 다음 파일을 다루어야 합니다.

    AssetManager am = getResources().getAssets() ;
    InputStream is = null ;
    FileOutputStream fos = null ;
    byte buf[] = new byte[1024] ;

    try {
        is = am.open("txt/file.txt") ;
        fos = new FileOutputStream(getFilesDir() + "file.txt") ;

        while (is.read(buf) > 0) {
            fos.write(buf) ;
        }

        fos.close() ;
        is.close() ;

    } catch (Exception e) {
        e.printStackTrace();
    }

4.2 텍스트 파일 읽기.

앞에서 AssetManager 사용법을 통해 확인했듯이, 애셋(Asset)에 포함된 파일은 바이너리(binary) 스트림으로만 열 수 있습니다. open() 함수의 리턴 타입이 InputStream인 것을 보면 알 수 있죠. 그래서 예제에서는 읽어들인 바이너리(binary) 데이터를 텍스트(text) 데이터로 바꾸기 위해, 바이트(byte) 버퍼를 String 타입으로 직접 바꾸는 코드를 작성했습니다. 그런데 매번 String 타입으로의 변환없이, 스트림 자체를 BufferedReader로 처리하는 방법이 있습니다. 아래 코드처럼 말이죠.

        AssetManager am = getResources().getAssets() ;
        InputStream is = null ;
        byte buf[] = new byte[1024] ;
        String text = "" ;

        try {
            is = am.open("file.txt") ;
            
            BufferedReader bufrd = new BufferedReader(new InputStreamReader(is)) ;

            // TODO : use bufrd
        } ...

4.3 애셋(Asset) 폴더에 하위 디렉토리 추가하기.

안드로이드 애셋(Asset)에 파일을 넣을 때, 파일의 갯수가 적으면, 보통 모든 파일을 애셋(Asset)의 루트 디렉토리(src/main/assets/)에 넣어 사용합니다. 그리고 파일을 참조할 때, 추가된 파일의 이름만으로 open() 함수를 호출하죠.

Asset 루트에 저장된 파일 열기


그런데 애셋(Asset)에 포함시킬 파일의 갯수가 많아지고 파일의 종류가 다양해지면, 애셋(Asset) 루트 디렉토리에만 모든 파일을 저장하는 것이 파일 관리를 어렵게 만들 수 있습니다.


이 때, 애셋(Asset)의 루트 디렉토리에 하위 디렉토리를 추가하면 파일을 효율적으로 관리할 수 있습니다. 그리고 하위 디렉토리에 추가된 파일은 하위 디렉토리 이름을 포함한 경로를 사용하여 열 수 있습니다.

Asset 하위 디렉토리 파일 열기


하위 디렉토리를 추가하는 방법은, 디렉토리를 추가하고자 하는 곳에서 마우스 오른쪽 버튼을 누른 다음, 팝업 메뉴의 New - Directory를 선택하면 됩니다.

Asset 하위 디렉토리 추가 방법


5. 참고.

.END.


ANDROID 프로그래밍/FILE , , , , , , ,

  1. Blog Icon
    js

    안녕하세요.
    그럼 assets 에 파일을 넣어 놓으면 앱 설치시 fiels 디렉토리로 넣어 놓은 파일이 복사되면서 설치 되는 것인가요? 그렇다면 그게 안되고 있다면 어디를 체크해 봐야 할까요?

  2. 네. 앱 패키지 파일에 포함되며, 앱 설치 시 같이 설치됩니다. asset 이라는 사전적 의미 그대로, 앱의 "자산"이 되는 것입니다.

    asset이 문제가 있다면, 먼저 asset 관련 설정이 제대로 되어 있는지 확인해보세요.

    "1.1 안드로이드 애셋 폴더 만들기"의 내용을 보시고, 제대로 설정되었는지 확인해보시기 바랍니다.

    그리고 assets 접근 경로도 확인해보세요. "1.3 안드로이드 애셋으로부터 파일 읽기"의 내용을 다시 한번 확인해보시기 바랍니다.

    감사합니다.

  3. Blog Icon
    뽀따팬2호

    ayncTask 읽고 file입출력에 대해 궁금해져 이사왔습니다ㅎㅎㅎ 필요한 내용이 다 있는 블로그라... 쉽지 않게 맘에 들어버려버렸습니다!!! 감사합니다 ㅎㅎ

  4. 찾으시는 내용들이 있어서 다행이네요.
    방문해 주셔서 감사합니다.

  5. Blog Icon
    코틀린초보

    FAB버튼을 누르면, inst의 값을 바꿔서, 다른 파일에 들어있는 텍스트들을 읽어오려고 했습니다. 그러나 아래 보시는 바와 같이 코딩 했을때, 버튼을 눌러도 다른 파일에 있는 텍스트 문자열들을 읽어오지 않았습니다. 제가 생각하기에는, 파일 경로를 inst 변수에 대입해주더라도, 값을 대입해주는 것만으로는 읽어온 텍스트를 화면에 갱신하여 출력해주는것 같지 않았습니다. 뭔가 잘못된것 같은데, 뭐가 잘못된 것인지 모르겠습니다. 뽀따님이 가르쳐주신대로 try문안에 작성한 것을 복사해 온것입니다. 극히 일부분만 보여드린 것이라, 나머지는 뽀따님께서 가르쳐주신 것과 같다고 생각하셔도 무방합니다. 클릭 이벤트로 다른 파일에 있는 텍스트 문자열을 읽어와서 화면을 갱신시켜 출력되게 하려고 했는데, 아무래도 갱신 개념의 코딩이 빠진게 아닌가 싶었습니다. 원래 저 클릭 이벤트 없었을 때에도 잘 작동했고, 클릭 이벤트 추가한후에도, ary1[0]에 해당하는 경로값이 잘 대입되어, 화면에 텍스트 문자열을 잘 출력해줬습니다. 그러나 클릭 이벤트 안에 있는 대입에 관한 코드들은 아예 작동하지를 않았습니다.


    inst = am.open(ary1[0])


    fabz.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
    inst = am.open(ary1[1])
    }
    })
    fabz2.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
    inst = am.open(ary1[0])
    }
    })

  6. 조금 더 기본기를 학습할 필요가 있을 것 같습니다.

    단순히 프로그래밍 언어에 대한 내용만 말씀드리는 게 아니라,
    안드로이드 앱에서 사용하는 기본 컴포넌트들에 대한 사용법을 다시 한번 살펴보시길 추천드려요.

    일단 질문 내용만으로는, 어떤 문제가 있는지 정확히 짚어드릴수는 없지만,
    아마도... 생각하신대로 데이터를 읽어온 다음, 읽어온 데이터를 화면에 반영하는 부분이 빠져있는 것 같네요.

    어떤 컴포넌트를 사용하셨는지 잘 몰라서 구체적인 방법을 알려드리긴 힘들고요.

    사용하시는 컴포넌트 기본 사용법을 먼저 파악하신 다음, 화면 갱신하는 코드 추가하시면 될 것 같습니다.

    감사합니다.

  7. 항상 좋은 글 감사합니다.
    제가 이번에는 bitmap 이미지 파일을 저장하고 불러오는 공부를 하려고 합니다.
    bitmap 이미지는 보통 asset,바이너리 파일 중에 보통 어느 것을 사용해서 저장 및 불러오기를
    하나요??

  8. 그건 이미지 사용 용도에 따라 결정되는 문제라서, 어떤 방법을 사용하라고 말씀드리기는 애매하네요.

    앱에서 사용하는 이미지를 받아오는 서버가 별도로 존재하는지, 사용자에 의해 변경되는지, 패키징 파일의 크기에 민감한지, 등등 고려해야 할 사항들이 몇 가지 있기 때문입니다.

    하지만, "공부"라고 하시는걸보니 asset이든 바이너리 파일이든 크게 중요하진 않을 거 같고요. 두 가지 모두 한번 만들어보세요. 크게 어려운 내용은 아니니까요.

    감사합니다.

  9. 항상 빠른 답글 해주시네요 감사합니다ㅎㅎ

  10. 관심 가져주셔서 감사합니다.