영구적인 Data저장

소정·2023년 2월 28일
0

Android_with_Java

목록 보기
18/33

기기에 데이터 저장
램에서 보조기억장치()로 파일로 데이터를 저장하는 방법
램이 아닌 하드 디스크는 전부 file 형태로 저장되어 있음

[1] Internal Storage(내부)

  • 내부저장소는 private함 내가 저장할 수 있는 곳이 정해져있다
    data/data/
  • 절대 외부에선 저장 불가능
  • 저장한 데이터는 앱을 지우기 전까지만 저장됨

🔨 데이터 저장과 로드

1.데이터 저장 순서

① 저장할 값 가져옴
② 내부 저장소(internal Storage)의 전용 공간에 Data.txt 라는 파일에 이 문자열 데이터를 저장하기
-> 파일쪽으로 데이터를 내보내는 스트림 열기 : FileOutputStream
-> 액티비티클래스 안에 이미 내부 저장소의 파일을 여는 기능메소드가 존재함 : openFileOutput

FileOutputStream fos = openFileOutput("Data.txt", MODE_APPEND);

💡 openFileOutput

openFileOutput(저장파일 이름, mode)

  • MODE_PRIVATE : 프라이빗 하면서 덮어쓰기
  • MODE_APPEND : 프라이빗 하면서 이어붙이기

③ 바이트스트림을 보조문자스트림으로 변환 : PrintWriter
-> FileOutputStream은 바이트 단위로 데이터를 저장해야 하기에 불편함
그래서 문자스트림으로 변환해 여기서 더 나아가서 '보조문자스트림(문장 단위 저장)'을 끄면 더 편함

2.데이터 로드 순서

** 1,2,3 세트임
① 파일 읽어오기 (바이트로 읽어옴) : FileInputStream
② 바이트 스트림 => 문자스트림으로 변환(한글자씩 읽음) : InputStreamReader
③ 문자스트림 -> 보조 문자 스트림으로 변환(한줄 단위로 읽음) : BufferedReader
④ 파일의 끝이 몇문장인지 모르니 문자열 쌓아두는 애 생성 : StringBuffer
⑤ while문으로 파일 읽기 : .readLine()
-> .readLine()은 줄바꿈 문자를 제외하고 읽어옴
-> buffer.append(line+"\n"); //줄바꿈 문자를 어거지로 추가해줘야함
⑥ buffer에 append된 파일 출력

🔨 main.java


package com.bsj0420.ex54internalstorage;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

public class MainActivity extends AppCompatActivity {

    EditText et;
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et = findViewById(R.id.et);
        tv = findViewById(R.id.tv);

        findViewById(R.id.btn_save).setOnClickListener(view -> clickSave());

        findViewById(R.id.btn_load).setOnClickListener(view -> clickLoad());

    }

    void clickSave(){
        //1. 저장할 값 가져옴
        String data = et.getText().toString();

        et.setText("");

        //내부 저장소(internal Storage)의 전용 공간에 Data.txt 라는 파일에
        //이 문자열 데이터를 저장하기
        
        //파일쪽으로 데이터를 내보내는 스트림 열기
        //액티비티클래스 안에 이미 내부 저장소의 파일을 여는 기능메소드가 존재함 : openFileOutput
        //MODE_PRIVATE : 프라이빗 하면서 덮어쓰기
        //MODE_APPEND : 프라이빗 하면서 이어붙이기
        try {
            //2.
            FileOutputStream fos = openFileOutput("Data.txt", MODE_APPEND);
            //fos는 바이트 단위로 데이터를 저장해야 하기에 불편함
            //그래서 문자스트림으로 변환
            //여기서 더 나아가서 '보조문자스트림'을 끄면 더 편함

            //FileOutputStream : 파일이 없으면 자동으로 만듦

            //3.
            PrintWriter writer = new PrintWriter(fos);

            writer.println(data);
            writer.flush();
            writer.close();

            tv.setText("SAVED");

        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }


    }

    void clickLoad(){
        //내부 저장소에 Data.txt 라는 파일에서 데이타 읽어오기
        try {
            //1. 파일 읽어오기 (바이트로 읽어옴)
            FileInputStream fis = openFileInput("Data.txt");
            //2. 바이트 스트림 => 문자스트림으로 변환(한글자씩 읽음)
            InputStreamReader isr = new InputStreamReader(fis);
            //3. 문자스트림 ->  보조 문자 스트림으로 변환(한줄 단위로 읽음)
            //1,2,3 세트임
            BufferedReader reader = new BufferedReader(isr);

            //4. 파일의 끝이 몇문장인지 모르니 문자열 쌓아두는 애 생성
            StringBuffer buffer = new StringBuffer();

            //5. while문으로 파일 읽기
            while (true) {
                String line = reader.readLine(); //.readLine()은 줄바꿈 문자를 제외하고 읽어옴
                if(line == null) break;

                buffer.append(line+"\n"); //줄바꿈 문자를 어거지로 추가해줘야함
            }
            
            //6. buffer에 append된 파일 출력
            tv.setText(buffer.toString());
            

        } catch (FileNotFoundException e) {
            Toast.makeText(this, "해당파일을 찾을 수 없습니다", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}



[2] External Storage(외부)

  • 내장 되어 있는 외장메모리(sd카드)도 있어서 물리적으로 생각하면 안됨
  • 앱 전용 공간은 앱을 지우면 같이 날아감
  • 앱을 지워도 파일을 남기고 싶으면 공용공간(미디어/not 미디어)에 넣어야됨
  • 29버전 이후 저장소에 저장하는 패러다임이 2가지로 나뉘어짐 (미디어 파일때문에..)
    ① 레거시 스토리지(Legasy Storage) : 공용공간과 내 전용 공간으로 나뉘어짐
    ② 스코프드 스토리지(Scoped Storage) : 개별공간이고 뭐고 아무도 접근 불가 공용공간이라는 말이 사라지고 미디어/not 미디어 공간으로 바뀜

💡 앱 전용 공간 파일 저장 위치

🔨 데이터 저장과 로드

1.데이터 저장 순서

① 외부메모리(SD카드)가 있는지 확인
-> Environment.getExternalStorageState();
② 이 외부메모리의 상태가 연결(mounted) 되어 있지 않은가 확인
-> if(!state.equals(Environment.MEDIA_MOUNTED))
MEDIA_MOUNTED 말고는 아무것도 저장할 수 없는 상태
③ 저장 할 데이터 가져오기
④ 직접 스트림을 열기 위해 파일 객체 생성
-> 내부 저장소와 다르게 액티비티에 스트림 곧바로 열어주는 기능이 없음

4-1) 파일이 저장될 경로 : 앱에게 할당된 개별 영역
-> 디렉토리 목록들을 액티비티한테 얻어옴
File[] dirs = getExternalFilesDirs("MyDir"); //(하위 폴더명)

파일이 저장될 경로 확인

4-2) 파일명과 경로를 결합하여 File 객체를 생성

File file = new File(dirs[0], "Data.txt");

⑤ file과 연결하는 무지개로드 만들기
5-1) 곧바로 문자스트림으로 만들 수 있음
FileWriter fw = new FileWriter(file, true); //true : 이어붙이기 모드

5-2) 문자스트림 -> 문장
PrintWriter writer = new PrintWriter(fw);

2.데이터 로드 순서

① SD카드가 읽을 수 있는 상태인지 확인
② 파일의 경로 찾아오기
③ FileReader() 생성
④ 바이트 -> 한줄씩 : BufferedReader()
⑤ 여러줄 입력 했을 수 있으니 글자 쌓아두기 : StringBuffer()


main.java

package com.bsj0420.ex55datastorageexternal;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Environment;
import android.widget.EditText;
import android.widget.TextView;

import com.google.android.material.snackbar.Snackbar;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class MainActivity extends AppCompatActivity {

    EditText et;
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et = findViewById(R.id.et);
        tv = findViewById(R.id.tv);

        findViewById(R.id.btn_save).setOnClickListener(view -> clickSave());

        findViewById(R.id.btn_load).setOnClickListener(view -> clickLoad());

    }

    void clickSave(){
        //1. 외부메모리(SD카드)가 있는지 확인
        String state =  Environment.getExternalStorageState();

        //2. 이 외부메모리의 상태가 연결(mounted) 되어 있지 않은가 확인
        if(!(state.equals(Environment.MEDIA_MOUNTED))){
            Snackbar.make(tv,"SD card is not mounted",Snackbar.LENGTH_LONG).show();

            return; //작업 종료
        }

        //3. 저장 할 데이터 가져오기
        String data = et.getText().toString();
        et.setText("");

        //4.
        //"Data.txt" 라는 파일에 데이터 저장
        //내부 저장소와 다르게 액티비티에 스트림 곧바로 열어주는 기능이 없음
        //그래서 직접 스트림을 열기 위해 파일 객체 생성

        //4-1)
        //파일이 저장될 경로 : 앱에게 할당된 개별 영역
        //디렉토리 목록들을 액티비티한테 얻어옴
        File[] dirs = getExternalFilesDirs("MyDir"); //하위 폴더명

        //경로 확인용 for문
        String s="";
        for(File f : dirs) {
            s = s+ f.getPath() + "\n";
        }
        tv.setText(s); //경로 확인

        //4-2)
        //여러 경로중 첫번째가 보통 SDcard
        //파일명과 경로를 결합하여 File 객체를 생성
        File file = new File(dirs[0], "Data.txt");

        //5.
        //file과 연결하는 무지개로드 만들기
        //곧바로 문자스트림으로 만들 수 있음
        try {
            //5-1)
            FileWriter fw  = new FileWriter(file, true); //true : 이어붙이기 모드

            //5-2)
            PrintWriter writer = new PrintWriter(fw);
            writer.println(data);

            writer.flush();
            writer.close();

            tv.setText("SAVED");

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    void  clickLoad(){

        //1. SD카드가 읽을 수 있는 상태인지 확인
        String state = Environment.getExternalStorageState();

        if(state.equals(Environment.MEDIA_MOUNTED)||state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
            //읽을 수 있는 상태

            //2. 파일의 경로 찾아오기
            File[] dirs = getExternalFilesDirs("MyDir");
            File file = new File(dirs[0], "Data.txt"); //파일 객체를 만들어서 정확한 위치 찾아주는 작업

            try {
                //3. FileReader() 생성
                FileReader fr = new FileReader(file);

                //4. 바이트 -> 한줄씩 : BufferedReader()
                BufferedReader reader = new BufferedReader(fr);

                //5. 여러줄 입력 했을 수 있으니 글자 쌓아두기 : StringBuffer()
                StringBuffer buffer = new StringBuffer();
                while (true) {
                    String line = reader.readLine();

                    if(line == null) break;

                    buffer.append(line + "\n");
                }

                tv.setText(buffer.toString());

            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    }
}



[3] SharedPreference(환경설정)

  • 단순한 설정값 저장하기 위한것 (진동, 로그인 저장값 등...)
  • xml 형식으로 저장됨
  • stream없이 자동으로 저장됨
  • 내부 저장소에 저장된다
  • 덮어쓰기 저장만 가능함
  • 기본형 자료형을 쉽게 저장할 수 있다

🔨 데이터 저장과 로드

데이터 저장 순서

① 저장 할 데이터 얻어오기
② "Data.xml" 파일에 데이터를 SharedPreferences로 저장하기 위해 객체 '얻어'오기 : getSharedPreferences()

📢 getPreferences() vs getSharedPreferences()
getPreferences() : 이 액티비티에서만 쓸수 있음
getSharedPreferences(파일이름,모드) : 앱 전체에서 쓸 수 있음
모드는 무조건 MODE_PRIVATE

③ 저장작업

  • 3-1) 작성자 생성 : edit()
  • 3-2) 작성자에 값 저장 : editor.putString(식별자, 값)

④ 작업 완료 : commit() - 꼭!!

데이터 로드 순서

① 저장한 파일 객체 SharedPreferences한테 '얻어'오기
② 자료형 기준으로 get : //get자료형(식별자, 디폴트 밸류)
③ 읽어온 값 확인

main.java

package com.bsj0420.ex56datastoragesharedpreference;

import androidx.appcompat.app.AppCompatActivity;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    EditText etName, etAge;

    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        etName = findViewById(R.id.et_name);
        etAge = findViewById(R.id.et_age);
        tv = findViewById(R.id.tv);

        findViewById(R.id.btn_save).setOnClickListener(view -> clickSave());
        findViewById(R.id.btn_load).setOnClickListener(view -> clickLoad());

    }

    void clickSave(){
        //1.저장 할 데이터 얻어오기
        String name = etName.getText().toString();
        int age = Integer.parseInt(etAge.getText().toString());

        //2. SharedPreferences로 저장
        // "Data.xml" 파일에 데이터를 저장하기 위해 객체 '얻어'오기
        SharedPreferences pref = getSharedPreferences("Data",MODE_PRIVATE);
        //getPreferences() : 이 액티비티에서만 쓸수 있음
        //getSharedPreferences() : 앱 전체에서 쓸 수 있음
        //모드는 무조건 MODE_PRIVATE

        //3. 저장작업
        //3-1.작성자 생성 : edit()
        SharedPreferences.Editor editor =pref.edit(); //작성자가 리턴됨

        //3-2. 작성자에 값 저장 : editor.putString(식별자, 값)
        editor.putString("name",name);
        editor.putInt("age",age);

        //4. 작업 완료 - 꼭!!
        editor.commit(); // 내부적으로 트랜잭션 하고 있음

    }


    void clickLoad(){

        //1. 저장한 파일 객체  SharedPreferences한테 '얻어'오기
        SharedPreferences pref = getSharedPreferences("Data",MODE_PRIVATE);

        //2. 자료형 기준으로 get : //get자료형(식별자, 디폴트 밸류)
        String name = pref.getString("name","무(無)명"); 
        int age = pref.getInt("age",0);

        //3. 확인
        tv.setText(name +" : "+age);
    }
    
    void clickClear(){
    
    	SharedPreferences pref = getSharedPreferences("Data",MODE_PRIVATE);
        SharedPreferences.Editor editor =pref.edit();
        editor.clear().commit()
        
    }

}

🧨 단점
SharedPreference는 데이터가 덮어쓰기 됨



[4] SQLite Database

  • 컬럼값이 세개 이상일때 사용 권장
  • 데이터를 표의 형태로 저장하고 싶을 때 사용
  • CRUD 작업
  • DBMS임 : 데이터베이스(자료)를 제어하는 것
  • 데이터베이스 파일을 만든다는 것은 표 여러개를 담는 것이다(Table)

📢 만들어진 DB파일에 작업할 때 키워드
.execSQL() : 쿼리 작업 중 리턴값이 없는 것들 : c,u,d 작업
.rawQuery() : 값을 리턴 받아야 하는 것 : r 작업

🔨 CRUD 작업

1.Create Table

① text.db 라는 이름으로 데이터베이스 파일을 열거나 또는 생성
-> openOrCreateDatabase(파일명, MODE_PRIVATE 밖에 안됨, 값 읽어올떄 커스텀 하느라 쓰는것)
-> 이 메소드를 실행하면 text.db를 제어할 수 있는 능력을 가진 객체가 리턴됨 : SQLiteDatabase
② 만들어진 DB파일에 테이블(표)를 생성하는 작업
-> SQL언어를 이용해서 원하는 명령어를 Datbase에 수행함

🔨 테이블 생성 쿼리
"CREATE TABLE 테이블이름(id INT(10), name VARCHAR(20), msg TEXT)"

  • CREATE TABLE IF NOT EXISTS : 테이블이 존재하지 않으면 만들어라
  • VARCHAR : 변할 수 있는 CHAR
  • 컬럼명에 no는 예약어라 쓸수 없음

테이블 생성.java

//1. 데이터 저장 할 테이블 만들기
// text.db 라는 이름으로 데이터베이스 파일을 열거나 또는 생성
// -> openOrCreateDatabase(파일명, MODE_PRIVATE 밖에 안됨, 값 읽어올떄 커스텀 하느라 쓰는것)
//이 메소드를 실행하면 text.db를 제어할 수 있는 능력을 가진 객체가 리턴됨 : SQLiteDatabase
sqLiteDatabase = openOrCreateDatabase("text", MODE_PRIVATE, null);

//2. 만들어진 DB파일에 테이블(표)를 생성하는 작업
//SQL언어를 이용해서 원하는 명령어를 Datbase에 수행함
// CREATE TABLE IF NOT EXISTS : 테이블이 존재하지 않으면 만들어라
sqLiteDatabase.execSQL("CREATE TABLE IF NOT EXISTS member(num INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(20) NOT NULL,age INTEGER, email TEXT)");

☝ 만든 테이블 확인 (App inspection : 앱이 실행중일때만 보임)


🧨 주의

value를 넣을 떈 반드시 'value' 작은 따음표 안에 받아야함!!!

2.Create : insert

🔨 isnsert 쿼리
".execSQL(INSERT INTO table_name (column1, column2, column3) VALUES ('"+value1+"', '"+value2+"', '"+value3+"')")

  1. 데이터 값을 얻어 온다
  2. DB에 값을 삽입한다 : execSQL

사용예시


3.Read : selete

3-1) selectAll

테이블의 모든 데이터들을 검색하여 가져오기

  1. 테이블을 select 하면 select 한 애를 새로운 테이블로 만들고 cutsor로 읽음 : Cursor = .rawQuery()
  2. 레이어를 끝까지 읽어오기 위해 총 레코드 수 찾기
  3. 커서를 첫번째 로우로 이동
  4. 데이터를 쌓아놓기 위한 StringBuffer (보통은 ArrayList에 저장)
  5. for문을 이용해 레코드 읽는다
  6. 화면 출력

사용예시

    //select : rawQuery()
    void clickSelectAll(){
        //member 테이블의 모든 데이터들을 검색하여 가져오기
        //1.
        //테이블을 select 하면 select 한 애를 새로운 테이블로 만들고 cutsor로 읽음
        Cursor cursor = sqLiteDatabase.rawQuery("SELECT * FROM member", null);
        //rawQuery("SELECT * FROM 테이블명", where절 없으면 null);

        if(cursor == null) return; //cursor가 null이란것은 참조변수만 만들어지고 값은 없다는 것

        //2.총 줄(row : 레코드) 수 : 레이어를 끝까지 읽어오기
        int rowCnt = cursor.getCount();

        //3.첫번째 레코드로 이동
        cursor.moveToFirst();

        //4. 데이터를 쌓아놓기 위한 StringBuffer
        StringBuffer buffer = new StringBuffer();

        //5. for문을 이용해 레코드 읽는다
        for(int i=0; i< rowCnt; i++) {
            int num = cursor.getInt(0);
            String name = cursor.getString(1);
            int age = cursor.getInt(2);
            String email = cursor.getString(3);

            buffer.append(num + " "+ name+ " "+ age + " "+ email + "\n");

            cursor.moveToNext(); //다음 레코드로 이동
        }

        //6. 알럿 화면에 결과 보려주기
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(buffer.toString());
        builder.create().show();
    }

3-2) select by where

특정 값만 찾아오기

  1. where절 조건용 변수
  2. 테이블을 select 하면 select 한 애를 새로운 테이블로 만들고 cutsor로 읽음 : Cursor = .rawQuery()
  3. 레이어를 끝까지 읽어오기 위해 총 레코드 수 찾기
  4. 커서를 첫번째 로우로 이동
  5. 데이터를 쌓아놓기 위한 StringBuffer (보통은 ArrayList에 저장)
  6. for문을 이용해 레코드 읽는다
  7. 화면출력

사용예시


    //특정 값만 찾아오기
    void clickSelect(){

        //1. where절 조건용 변수
        String name = etName.getText().toString();

        //2. 테이블을 select 하면 select 한 애를 새로운 테이블로 만들고 cutsor로 읽음 : Cursor = .rawQuery()
        Cursor cursor = sqLiteDatabase.rawQuery("SELECT name,age FROM member WHERE name=?",new String[]{name});
        
        if(cursor == null) return; //커서에 읽을 값 없으면 함수 종료해라

        //3. 레이어를 끝까지 읽어오기 위해 총 레코드 수 찾기
        int rowCnt = cursor.getCount();

        //4. 커서를 첫번째 로우로 이동
        cursor.moveToFirst();

        //5. 데이터를 쌓아놓기 위한 StringBuffer (보통은 ArrayList에 저장)
        StringBuffer buffer = new StringBuffer();

        //6. for문을 이용해 레코드 읽는다
        for(int i=0; i< rowCnt; i++) {
            String name2 = cursor.getString(0); //resultSet 기준으로 index번호 쓴다
            int age = cursor.getInt(1);

            buffer.append(name+ " "+ age + " "+ "\n");
            cursor.moveToNext();
        }

        //7. 화면출력
        new AlertDialog.Builder(this).setMessage(buffer.toString()).create().show();
    }

💡 cursor

select 한 컬럼 기준으로 새로 테이블을 만들고 cursor가 가르킴


4.update : update

🔨 update 쿼리
".execSQL(UPDATE table SET column1 = value, column2 = value2 WHERE column = ? ", new String[]{value})

  1. 조건값(where절)으로 쓸 값 받아오기
  2. execSQL로 데이터 베이스 변경

사용예시


5.Delete : delete

🔨 delete 쿼리
" .execSQL(DELETE FROM table WHERE column =? ", new String[]{value})

  1. 조건값(where절)으로 쓸 값 받아오기
  2. execSQL로 데이터 베이스 변경

사용예시



profile
보조기억장치

0개의 댓글