파일 입출력은 말 그대로 디바이스에 파일로 저장하고 그 파일을 뷰에 출력해주는 일련의 과정을 말합니다.
우선 화면단에서 파일 입출력을 보여주기 위해 레이아웃을 만들어줍니다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/editText"
android:layout_width="494dp"
android:layout_height="48dp"
android:inputType="textPersonName"
android:ems="10"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintVertical_bias="0.172"
/>
<Button
android:id="@+id/read"
android:text="읽기"
android:layout_width="323dp"
android:layout_height="49dp"
app:layout_constraintBottom_toTopOf="@+id/write"
android:layout_marginBottom="48dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editText"
app:layout_constraintHorizontal_bias="0.501"
app:layout_constraintVertical_bias="1.0"
/>
<Button
android:id="@+id/write"
android:text="쓰기"
android:layout_width="323dp"
android:layout_height="49dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/clear"
app:layout_constraintHorizontal_bias="0.501"
android:layout_marginBottom="52dp"/>
<Button
android:id="@+id/clear"
android:text="CLEAR"
android:layout_width="323dp"
android:layout_height="49dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.501"
android:layout_marginBottom="360dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
코드를 뜯어가며 하나씩 살펴보겠습니다.
import android.os.Bundle
import android.provider.ContactsContract.Intents.Insert.NOTES
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.io.*
import java.lang.Exception
class MainActivity : AppCompatActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val read = findViewById<Button>(R.id.read)
val write = findViewById<Button>(R.id.write)
val clear = findViewById<Button>(R.id.clear)
read.setOnClickListener(this)
write.setOnClickListener(this)
clear.setOnClickListener(this)
}
우선은 뷰에 있는 버튼 세개를 가져옵니다. 각각의 버튼은 클릭 이벤트 리스너가 걸립니다.
override fun onClick(view: View?) {
val edit = findViewById<EditText>(R.id.editText)
if(view?.id == R.id.read) {
var reader:BufferedReader? = null
try {
val `in`: InputStream? = openFileInput(NOTES)
if (`in` != null) {
reader = BufferedReader(InputStreamReader(`in`))
var str: String?
val buf = StringBuffer()
// 파일 읽기
while (reader.readLine().also { str = it } != null) {
println("$str")
buf.append("""$str""") // 실제로 값을 읽는 위치
}
edit.setText(buf.toString())
}
} catch (e:FileNotFoundException) {
// e.printStackTrace()
println(e.message)
println("파일을 찾을 수 없음")
} catch (e:Exception) {
e.printStackTrace()
Toast.makeText(this, "exception: $e", Toast.LENGTH_SHORT).show()
} finally {
if(reader != null)
try { reader.close() }
catch (e:Exception) { e.printStackTrace() }
}
메인 클래스가 View.OnClickListener
를 상속받으므로 onClick
함수가 구현되어야 합니다.
view
를 매개변수로 받아서 이 변수의 아이디가 위에서 선언한 버튼인 경우에 대해 조건식을 세워줍니다.
우선 읽기입니다.
BufferedReader
를 준비해줍니다. 그 다음으로 InputStream
을 `in
`으로 잡아주었습니다. 백틱을 붙여준 이유는 예약어를 변수명으로 사용하기 위함입니다.
InputStream
은 입력된 데이터를 빨아들이는 것으로 보면 됩니다.
이 때 파일을 스트림으로 읽기 위해 openFileInput
을 사용하였습니다. NOTES
는 파일 이름입니다.
읽어들인 파일이 null
이 아닐 때 BufferedReader
로 파일을 읽어주고 읽은 내용을 저장할 변수 str
을 준비합니다.
그리고 문자열의 추가/변경을 위해 StringBuffer
도 준비해줍니다.
줄단위로 문장을 읽으면서 str
에 while
루프를 돌며 문장을 추가해주는데, 이것이 null
이 아닐 때, 즉 문장이 입력 되어있을 때
StringBuffer
에 값을 추가해줍니다.
저장된 값을 텍스트입력창에 뿌려줍니다.
기본적으로 파일 입출력은 예외발생 사항이 있을 수 있기 때문에 try catch
처리를 해주는 것이 좋습니다.
} else if (view?.id == R.id.write) {
var out:OutputStreamWriter? = null
try {
// MODE_PRIVATE : 덮어쓰기, MODE_APPEND : 이어쓰기
out = OutputStreamWriter(openFileOutput(NOTES, MODE_PRIVATE))
out.write(edit.text.toString()) // 실제로 값을 써줌
Toast.makeText(this, "데이터 저장", Toast.LENGTH_SHORT).show()
} catch (e:Exception) {
e.printStackTrace()
} finally {
if (out != null) try {
out.close()
} catch (e:IOException) { e.printStackTrace() }
}
} else if (view?.id == R.id.clear) {
edit.setText("")
}
}
}
write
버튼을 눌렀을 때 처리할 작업입니다.
스트림으로 파일을 써내려갈 OutputStreamWriter
를 준비해줍니다.
그리고 파일을 읽어서 써주기 위해 openFileOutput
을 사용하고 매개변수로 파일 이름과 MODE_PRIVATE
이라는 덮어쓰기 기능을 넣어줍니다.
데이터가 저장되었다면 토스트를 띄워 데이터 저장에 성공했음을 알려줍니다.