[kotlin] 안드로이드 저장소

Leechaeyeon·2023년 7월 4일
0

코틀린 안드로이드

목록 보기
10/21

안드로이드 저장소

  • 안드로이드는 애플리케이션이 데이터를 저장할 수 있는 저장소를 두 가지로 제공하고 있다.
  • 내부 저장소 : 애플리케이션을 통해서만 접근이 가능하다.
  • 외부 저장소 : 단말기 내부의 공유 영역으로 모든 애플리케이션이 접근 가능하다.
    단말기를 컴퓨터에 연결하면 탐색기를 통해 접근할 수 있는 영역을 의미한다.

파일 입출력

  • 내부 저장소 : openFileOutput, openFileInput
  • 외부 저장소 : FileInputStream, FileOutStream

안드로이드 10이상의 외부 저장소

  • 외부 저장소에 저장된 파일은 모든 애플리케이션이 자유롭게 접근할 수 있어 보안에 문제가 되었다.
  • 이에 안드로이드 10에서는 외부 저장소에 제한을 두어 보안을 강화하게 되었다.
  • 안드로이드 10에서는 외부 저장소의 정책을 무력화 할 수 있는 옵션이 있었으니 11부터는 이 옵션이 제거되어 외부 저장소의 자유로운 접근은 불가능하게 되었다.
  • 이러한 외부 저장소를 Scoped Storage 정책이라 부른다.

예시 만들기

activity_main.xml

파일 입출력 -> 단말기 내부에 저장된다.

openFileInput 과 openFileOutput 사용

package com.test.android68_filestream

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.test.android68_filestream.databinding.ActivityMainBinding
import java.io.DataInputStream
import java.io.DataOutputStream

class MainActivity : AppCompatActivity() {
    lateinit var activityMainBinding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.run{
            // 내부 저장소
            // 파일을 저장한 애플리케이션만 사용이 가능하다.
            button.setOnClickListener {
                // MODE_PRIVATE : 덮어 씌우기
                // MODE_APPEND : 이어서 쓰기
                // 파일 내보내기
                val fos = openFileOutput("data1.dat", MODE_PRIVATE)
                val dos = DataOutputStream(fos)
                dos.writeInt(100)
                dos.writeDouble(11.11)
                dos.writeBoolean(true)
                dos.writeUTF("문자열")

                dos.flush()
                dos.close()

                textView.text = "내부 저장소 쓰기 완료 "
            }

            button2.setOnClickListener {
                val fis = openFileInput("data1.dat")
                val dis = DataInputStream(fis)

                val data1 = dis.readInt()
                val data2 = dis.readDouble()
                val data3 = dis.readBoolean()
                val data4 = dis.readUTF()

                dis.close()
                fis.close()

                textView.text = "data1 : ${data1}\n"
                textView.append("data2 : ${data2}\n")
                textView.append("data3 : ${data3}\n")
                textView.append("data4 : $data4")
            }
        }
    }
}

저장된 파일은 단말기 내에

외부 저장소에 저장(쓰기)

외부 저장소의 경로를 가져온다.
emulated/Android/data/패키지명/files
getExtrenalFilesDir 메서드의 매개변수에는 문자열을 넣어줄 수 있으며
files의 하위 폴더 이름을 넣어서 사용할 수 있다.
null을 넣으면 files까지의 경로가 된다.

// 외부 저장소
// Android/data 폴더에 저장된다.
// 이 곳에 저장된 파일은 다른 애플리케이션이 접근할 수 없으며
// 애플리케이션을 삭제하면 같이 삭제된다.
// 1. AndroidManfest.xml에 저장경로를 등록해준다.
// Activity 태그 안에 meta-data로 등록한다.
button3.setOnClickListener {
                // 외부 저정소의 경로를 가져온다.
                // emulated/Android/data/패키지명/files
                // getExternalFilesDir 메서드의 매개변수에는 문자열을 넣어줄 수 있으며
                // files의 하위 폴더 이름을 넣어서 사용할 수 있다.
                // null을 넣으면 files까지의 경로가 된다.
                val filePath = getExternalFilesDir(null).toString()

                val fos = FileOutputStream("${filePath}/data2.dat")
                val dos = DataOutputStream(fos)

                dos.writeInt(200)
                dos.writeDouble(22.22)
                dos.writeBoolean(false)
                dos.writeUTF("문자열2")

                dos.flush()
                dos.close()
                fos.close()

                textView.text = "외부 저장소 앱 데이터 폴더에 저장"
}


이렇게 저장된 것을 볼 수있다. \

외부 저장소 읽기

 // 메소드를 받아서 저장 -> 내부저장소
// 객체를 생성해서 저장 -> 외부저장소

button4.setOnClickListener {
                val filePath = getExternalFilesDir(null).toString()
                val fis = FileInputStream("${filePath}/data2.dat")
                val dis = DataInputStream(fis)

                val data1 = dis.readInt()
                val data2 = dis.readDouble()
                val data3 = dis.readBoolean()
                val data4 = dis.readUTF()

                dis.close()
                fis.close()

                textView.text = "data1 : ${data1}\n"
                textView.append("data2 : ${data2}\n")
                textView.append("data3 : ${data3}\n")
                textView.append("data4 : $data4")
}

이전에는 경로만 작성하면 개발자가 받아올수 있엇지만 보안 이슈 때문에 사용자가 직접 선택한 파일만 가져다가 쓸 수 있음

Scoped Storage 정책

  • Scoped Storage는 다음과 같은 형태로 구성
  • 앱 데이터 폴더 : 읽고 쓰는데 권한이 필요가 없으며 해당 애플리케이션만 접근이 가능, 애플리케이션 삭제 시 폴더도 같이 삭제
  • 미디어 파일 : 사진, 동영상, 음원파일들을 저장하는 장소
  • 공용 파일 : Downloads 폴더, 이 폴더에 저장된 파일은 모든 애플리케이션이 접근 가능
    단 코드를 통한 직접 접근은 불가능하고 단말기에 설치된 파일 관리 어플을 통해서만 접근이 가능하다.
    즉, 파일관리 어플을 실행해 사용자가 직접 파일을 선택해줘야 가능하다.

FileApp을 통해 선택 후 저장

기본 Downloads폴더로 뜸

FileApp을 통해 쓰기


    lateinit var writeActivityLauncher:ActivityResultLauncher<Intent>

// onCreate안에 
// 쓰기용 launcher
        val contracts1 = ActivityResultContracts.StartActivityForResult()
        writeActivityLauncher = registerForActivityResult(contracts1){
            // 사용자가 저장할 파일을 선택하고 돌아오면 ResultCode는 RESULT_OK가 들어온다.
            if(it.resultCode == RESULT_OK){
                // 사용자가 선택한 파일의 정보를 가지고 있는 Intent로 파일 정보를 가져온다.
                if(it.data!= null){
                    // 저장할 파일에 접근할 수 있는 객체로 부터 파일 정보를 가져온다.
                    // w : 쓰기, a : 이어쓰기 , r : 읽기
                    val des1 = contentResolver?.openFileDescriptor(it.data?.data!!,"w")
                    // 스트림을 생성한다. 
                    val fos = FileOutputStream(des1?.fileDescriptor)
                    val dos = DataOutputStream(fos)
                    dos.writeInt(300)
                    dos.writeDouble(33.33)
                    dos.writeBoolean(true)
                    dos.writeUTF("문자열 3")
                    dos.flush()
                    dos.close()
                    
                    activityMainBinding.textView.text = "Downloads 폴더에 저장"
                }
            }
        }
        
        // activityBinding.run 안에 
        // FileApp을 통한 접근
            button5.setOnClickListener {
                // 파일 관리 앱의 액티비티를 실행한다.
                val fileIntent = Intent(Intent.ACTION_CREATE_DOCUMENT)
                fileIntent.addCategory(Intent.CATEGORY_OPENABLE)
                // MimeType을 설정한다.
                // MimeType이란? 파일에 저장되어 있는 데이터의 양식이 무엇인지를 타나내는 문자열
                // https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/MIME_types
                fileIntent.type = "*/*"
                writeActivityLauncher.launch(fileIntent)
            }

        
        

FileApp을 통해 읽기


lateinit var readActivityLauncher:ActivityResultLauncher<Intent>   

// run 안에서 실행 -> onCreate에서 실행 
// 읽기용 launcher                                                                            
val contracts2 = ActivityResultContracts.StartActivityForResult()                          
readActivityLauncher = registerForActivityResult(contracts2){                               
    // RESULT_OK 일 때만..                                                                    
    if(it.resultCode == RESULT_OK){                                                        
        // 가져온 데이터가 있을 경우에만..                                                              
        if(it.data != null){                                                               
            // 선택한 파일의 경로 정보를 가져온다.                                                        
            val dest1 = contentResolver.openFileDescriptor(it.data?.data!!, "r")           
                                                                                           
            val fis = FileInputStream(dest1?.fileDescriptor)                               
            val dis = DataInputStream(fis)                                                 
                                                                                           
            val data1 = dis.readInt()                                                      
            val data2 = dis.readDouble()                                                   
            val data3 = dis.readBoolean()                                                  
            val data4 = dis.readUTF()                                                      
                                                                                           
            dis.close()                                                                    
                                                                                           
            activityMainBinding.textView.text = "data1 : ${data1}\n"                       
            activityMainBinding.textView.append("data2 : ${data2}\n")                      
            activityMainBinding.textView.append("data3 : ${data3}\n")                      
            activityMainBinding.textView.append("data4 : $data4")                          
        }                                                                                  
    }                                                                                      
}    




button6.setOnClickListener {                           
    // 파일 관리 앱의 액티비티를 실행한다.                           
    val fileIntent = Intent(Intent.ACTION_OPEN_DOCUMEN
    fileIntent.type = "*/*"                           
    readActivityLauncher.launch(fileIntent)           
}                                                     

0개의 댓글

관련 채용 정보