Archives
Recent Posts
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Today
Total
관리 메뉴

안드로이드 개발자의 창고

[43일차 Android] 파일 입출력 본문

Computer/Android

[43일차 Android] 파일 입출력

Wise-99 2023. 7. 10. 18:11

 

 

 

출처 : 안드로이드 앱스쿨 2기 윤재성 강사님 수업 PPT

 

 

 

📖 파일 입출력

  • 안드로이드는 애플리케이션이 데이터를 저장할 수 있는 저장소를 두가지로 제공하고 있다.
    • 내부 저장소
      • 애플리케이션을 통해서만 접근이 가능하다.
      • openFileInput, openFileOutput
    • 외부 저장소
      • 단말기 내부의 공유 영역으로 모든 애플리케이션이 접근 가능하다.
      • 단말기를 컴퓨터에 연결하면 탐색기를 통해 접근할 수 있는 영역을 의미한다.
      • FileInputStream, FileOutputStream
  • Scoped Storage 정책
    • 외부 저장소에 저장된 파일은 모든 애플리케이션이 접근할 수 있어 보안에 문제가 발생함에 따라 보안 강화를 위해 외부 저장소 접근을 제한한 정책

 


 

예제 코드

내부 저장소

  • 파일을 저장한 애플리케이션만 사용이 가능하다.
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("문자열1")

    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에 반영
    textView.text = "data1 : ${data1}\n"
    textView.append("data2 : ${data2}\n")
    textView.append("data3 : ${data3}\n")
    textView.append("data4 : $data4")

}

결과

Device File Explorer - data - data - 해당 앱의 패키지 이름 - files 폴더 확인

 

실행화면

 

 

 

외부 저장소

button3.setOnClickListener {
    // 외부 저정소의 경로를 가져온다.
    // emulated/Android/data/패키지명/files
    val filePath = getExternalFilesDir(null).toString()

    // getExternalFilesDir 메서드의 매개변수에는 문자열을 넣어줄 수 있으며 files의 하위 폴더 이름을 넣어서 사용할 수 있다.
    // null을 넣으면 files까지의 경로가 된다.
    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에 반영
    textView.text = "data1 : ${data1}\n"
    textView.append("data2 : ${data2}\n")
    textView.append("data3 : ${data3}\n")
    textView.append("data4 : $data4")
}

결과

에뮬레이터의 파일 앱 - emulated - Android - data - 패키지명 - files에서 확인

 

실행 화면

 

 

 

File 앱을 통한 접근

// 쓰기용 런처
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 폴더에 저장"
        }
    }
}

// 읽기용 런처
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")
        }
    }
}

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)
}

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

결과

에뮬레이터 File 앱 - Download에서 확인