97일차(2)/Android App(62) : Internal, External Storage 저장 (write)
- 새 모듈 생성 step24fileio
- 안드로이드에서의 파일 입출력 관련 학습
- 안드로이드의 파일 시스템에 앱이 이렇게 설치되어 있다고 하면,
앱마다 각각 내부 저장소(Internal Storage) 가 있다. 각각의 앱의 고유한 저장소이다.
- SharedPreference 는 어떤 xml문서를 만드는데, 이 xml문서가 만들어지는 공간이다.
- 이 내부 저장소 말고 다른 외부 저장소도 있다.
- External Storage(sdcard) 는 내부적으로 들어있다. 확장할 수도 있다.
- 각각의 내부저장소에서 외부저장소로 파일을 보내서 생성할 수도 있다.
- 이런 외부저장소는 저장소를 외부에 공개할 수도 있다(특정 권한을 걸어두는 것도 가능)
- 이 정보를 Content Provider 를 통해서 다른 앱에 서비스할 수도 있다.
- 안드로이드 앱에서 어떤 공간에 파일을 생성해서 저장할 일이 많다. 저장할 곳을 internal, external 중에 선택해서 생성하면 된다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context=".MainActivity"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="문자열 입력..."
android:id="@+id/inputMsg"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="파일에 저장"
android:id="@+id/saveBtn"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="파일에 저장(External)"
android:id="@+id/saveBtn2"/>
</LinearLayout>
MainActivity
package com.example.step24fileio;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.PrintWriter;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
EditText inputMsg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//필요한 UI 의 참조값 얻어오기
inputMsg=findViewById(R.id.inputMsg);
Button saveBtn=findViewById(R.id.saveBtn);
//버튼에 리스너 등록
saveBtn.setOnClickListener(this);
Button saveBtn2=findViewById(R.id.saveBtn2);
saveBtn2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.saveBtn:
saveToInternal();
//saveToInternal2();
break;
case R.id.saveBtn2:
saveToExternal();
break;
}
new AlertDialog.Builder(this)
.setTitle("알림")
.setMessage("저장했습니다.")
.setNeutralButton("확인", null)
.create()
.show();
}
//외부 저장 장치에 저장하기
public void saveToExternal(){
//입력한 문자열을 읽어온다.
String msg=inputMsg.getText().toString();
//외부 저장 장치의 폴더를 가리키는 File 객체
File externalDir=getExternalFilesDir(null);
//해당 폴더의 절대경로를 얻어낸다.
String absolutePath=externalDir.getAbsolutePath();
Log.d("absolutePath", absolutePath);
//텍스트 파일을 만들기 위한 파일 객체 생성
File file=new File(absolutePath+"/memo.txt");
try{
FileWriter fw=new FileWriter(file, true);
fw.append(msg);
fw.flush();
fw.close();
}catch (Exception e){
Log.e("saveToExternal()", e.getMessage());
}
}
//내부 저장 장치에 저장하기
public void saveToInternal(){
//입력한 문자열을 읽어온다.
String msg=inputMsg.getText().toString();
try {
//파일을 저장하기 위한 디렉토리 만들기
File dir=new File(getFilesDir(), "myDir");
if(!dir.exists()){
dir.mkdir();
}
//해당 디렉토리에 파일을 만들기 위한 File 객체
File file=new File(dir, "memo.txt");
FileWriter fw=new FileWriter(file, true);
fw.append(msg+"\n");
fw.flush();
fw.close();
}catch(Exception e){
Log.e("saveToInternal()", e.getMessage());
}
}
//내부 저장 장치에 저장하기
public void saveToInternal2(){
//입력한 문자열을 읽어온다.
String msg=inputMsg.getText().toString();
try {
FileOutputStream fos=openFileOutput("memo2.txt", MODE_APPEND);
PrintWriter pw=new PrintWriter(fos);
pw.println(msg+"\n");
pw.flush();
pw.close();
}catch(Exception e){
Log.e("saveToInternal()", e.getMessage());
}
}
}
- 버튼 onClickListener 를 Activity에 구현하고 메소드 오버라이드
**저장장치와 관련된 안드로이드 공식문서 : 링크
- 앱별 저장소는 내부 저장소를, 공유 저장소는 sdcard(외부 저장소)를 말한다.
- sdcard 에 파일을 저장하려면 어떻게 해야하는지
- context는 액티비티or서비스를 가리킨다.
- openFileInput,Output 메소드로 FileInputStream/FileOutputStream 타입 객체를 얻어낼 수 있다.
- 부모로부터 물려받은 상수값(부모의 필드값) 을 사용하면 된다.
- FileNotFoundException이 발생하므로 try~catch문으로 묶어준다.
- inputMsg 에 들어오는 값을 읽어와서 파일에 write 한다.
- FileOutStream에는 write 기능이 없기 때문에
File 객체를 생성하고, 이 File 타입을 받는 FileWriter 객체의 append() 메소드를 사용하기
- append() : 문자열을 쭉 붙여 나열해주는 메소드
- device file explorer 안의 data-data 폴더 안으로 들어가면 모듈명으로 생성된 폴더가 있다.
- 모듈명 폴더-files 안에 생성된 폴더와 파일! 여기가 앱의 고유한 저장장치, internal storage이다.
- 앱의 고유한 공간 안에 myDir 이라는 폴더가 만들어졌다. 내부 저장장치에 폴더,파일을 만들어 기록한 것이다.
- 내가 입력한 메세지를 memo.txt 안에서 이렇게 확인해볼 수 있다.
- getFilesDir() 로 디렉토리 생성 가능
- 만들어둔 myDir 폴더(디렉토리)안에 memo.txt라는 파일객체를 만들고
안에 텍스트를 넣어주었다.
- 이런 내부저장소에 저장하는 작업은 가상기기에서만 확인할 수 있고, 실 기기에서는 확인할 수 없다.(보안 문제)
- 폴더와 파일을 생성하고, FileWriter를 사용해서 기록해주었다.
FileWriter fw=new FileWriter(file, true);
fw.append(msg+"\n");
fw.flush();
fw.close();
- 저장시 개행기호를 추가해주면 msg와 개행기호가 함께 전달되어 파일에 append된다.
- 복사해서 saveToInternal2 메소드 추가. 다른 방법으로 파일 저장 테스트하기!
- PrintWriter는 생성자의 인자로 OutputStream을 받아준다. println() 기능을 사용해서 저장해주기
- 열어놓았던 device file explorer를 최신 버전으로 보기 위해서는
폴더 우클릭- Synchronize 해주면 된다.
- openFileOutput() 메소드를 사용하면 파일이 하위 폴더 없이도 바로 만들어진다.
- 이 메소드를 통해서 얻어내는 FileOutputStream은 그 Files 폴더 안쪽에 뭔가를 만들어낼 수 있는 OutputStream이다.
- 그러나 이 FileOutputStream을 통해서 문자열을 만들기는 불편하므로, PrintWriter 객체를 사용해서 문자열을 저장한 것.
- MODE_PRIVATE 으로 하면 기존의 내용은 지워지고 새로 입력한 내용만 들어가있으며,
MODE_APPEND 은 위와 같이 입력한 내용이 누적된다.
- 레이아웃에서 saveBtn2 버튼을 추가
- manifest에 permission 필요하고, permission 켜기 등 작업이 필요
- 내부에는 저장이 많이 안된다. 외부에 저장할 일이 많다.
- storage-emulated
- 이 폴더가 기본 외부 저장장치의 경로이다.
- 이전에는 AndroidManifest.xml에 아래 permission 표기가 필요했지만, 버전업 되면서 permission이 필요없게 되었다.
(AndroidManifest.xml 을 따로 수정하지 않아도 된다)
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- WRITE를 하면 READ는 자동으로 승인된다.
- permission을 명시해야 한다. 사용자가 직접 승인을 켜야한다는 코드!
- 권한이 없으면 여기서 리턴하고, 권한이 있으면 아래 메소드를 수행하도록 구조를 짠다.
- 어떤 권한 요청을 하게되면 결과를 받을 메소드가 필요하다.
- onRequestPermissionsResult 메소드 오버라이드
- 코드가 0번인 경우 이 결과 메소드 안쪽 코드가 실행되도록 분기하기.
- 외부 저장장치의 폴더를 가리키는 메소드. 인자는 그냥 null을 넣어주면 된다.
//외부 저장장치에 저장하기
public void saveToExternal(){
//입력한 문자열을 읽어온다.
String msg=inputMsg.getText().toString();
//외부 저장장치의 폴더를 가리키는 File 객체
File externalDir=getExternalFilesDir(null);
//해당 폴더의 절대경로를 얻어낸다.
String absolutePath=externalDir.getAbsolutePath();
//텍스트 파일을 만들기 위한 File 객체 생성
File file=new File(absolutePath+"/memo.txt");
try {
FileWriter fw=new FileWriter(file, true);
fw.append(msg);
fw.flush();
fw.close();
} catch (IOException e) {
Log.e("saveToExternal", e.getMessage());
}
}
- 외부 저장장치에 파일을 저장하기 위한 코드!
File externalDir=getExternalFilesDir(null);
String absolutePath=externalDir.getAbsolutePath();
- File 객체를 사용해 외부 저장장치 폴더를 찾아 해당 폴더의 절대경로를 요청한 것
- 로그를 찍어보면 내부 저장소에 저장할 때와 경로가 다른 것을 확인할 수 있다.
- emulated 폴더 안의 0-Android-data-(패키지명 폴더)-files 안에 파일이 생성되었다.
- 해당 위치를 찾아 들어가보면 저장한 내용이 들어가있는 것을 확인할 수 있다.
* 앱을 제거(uninstall) 하면 이 저장된 파일, 폴더는 어떻게 될까?
- 앱을 삭제하면 외부 저장장치이지만 같이 삭제된다.
- 외부 저장장치가 내부 저장장치보다 사용 용량은 크지만,
다른 앱에서 이 폴더에 접근할 수는 없다. 보안이 유지된다.
- 내부저장장치를 보아도 data-data 안에 할당받은 공간이 같이 삭제되었다고 나온다.
- 버튼 클릭시 저장되었다는 알림이 뜨도록 하기
- onClick 안에 저장되었다는 알림창 추가!
- 버튼 클릭시 저장되었다는 알림이 출력된다.
- 이처럼 앱에서 Android File System 안에 파일을 만들 수 있다면,
서버에서 어떤 정보를 내려받아서 저장해두면 인터넷이 되지 않는 상황에서도 파일을 사용할 수 있다.
'국비교육(22-23)' 카테고리의 다른 글
99일차(1)/Android App(64) : 카메라 앱으로 사진 촬영, 저장 (0) | 2023.03.03 |
---|---|
98일차(1)/Android App(63) : Internal, External Storage에서 읽기 (read) (1) | 2023.03.02 |
97일차(1)/Android App(61) : mp3 파일 재생 예제 / 되감기, 빨리감기 기능 구현 (0) | 2023.02.24 |
96일차(1)/Android App(60) : mp3 파일 재생 예제 / 곡 연속 재생 설정 (0) | 2023.02.24 |
95일차(1)/Android App(59) : mp3 파일 재생 예제 / 곡 목록 출력 (0) | 2023.02.22 |