17. [저장소에 데이터 보관]

728x90

[17. 저장소에 데이터 보관]

<앱 데이터 보관 방법>

(1) 외부 서버에 저장 -> 통신으로 주고받기

(2) 내부 저장소에 저장 :    3가지 제공
    - 데이터베이스에 보관
    - 파일에 보관
    - 공유된 프리퍼런스에 보관 

[17-1. 데이터베이스에 보관]

[안드로이드의 DBMS] | SQLite

-SQLite : 오픈소스로 만들어진 데이터베이스 관리 시스템

-테이블의 데이터를 앱 저장소에 파일로 저장.

-외부 앱에서 접근 불가

-실제 데이터를 SQLite 가 관리해줌

[질의문 작성]

-SQLite 사용하려면 SQLiteDatabase API 이용해야 함

[질의문을 실행해주는 함수 사용]

    (1) SQLiteDatabase 객체 생성 (= 데이터베이스 객체)

- openOrCreateDatabase() 함수 호출하여 객체 받기

val db = openOrCreateDatabase(“DB파일명”, Context.MODE_PRIVATE, null)

   (2) 이 객체에 정의된 질의문 실행 함수 사용

execSQL(질의문, 데이터배열) create/alter/drop/insert/update/delete문 실행 함수
rawQuery(질의문, 데이터배열) select문 실행 (테이블 데이터 조회)

     ⇒ rawQuery() 함수 반환 객체 = Cursor 객체

             1) Cursor 객체로 행 선택 : moveTo~ 함수 이용

moveToFirst() 첫 번째 행 선택
moveToLast() 마지막 행 선택
moveToNext() 다음 행 선택
meveToPosition() 매개변수로 지정한 위치의 행 선택
moveToPrevious() 이전 행 선택

              2) Cursor 객체 선택한 행의 -> 열 데이터 가져오기

-타입에 따라 선택. 매개변수에 가져올 데이터 저장된 열 인덱스 지정

getString(int 열 인덱스)
getInt(int 열 인덱스)
getDouble(int 열 인덱스)

[질의 함수 사용]

-insert() / update() /delete()/ query() 함수에 질의문의 각 항목별 정보를 매개변수로 대입

                                                                -> 질의문으로 만들어 실행됨


   1) insert(), update() ContentValues 객체 : 열 데이터 집합

-이 객체에 -형태로 데이터 집합 저장 (키에 열 테이블이름, 값에 데이터)

public long insert (String table, String nullColumnHack, ContentValues values)
public int update (String table, ContentValues values, String whereClause, String[] wereArgs)
val values = ContentValues()
values.put(“name”, “kkang”)
values.put(“phone”, “010112”)
db.insert(“USER_TB”, null, values)​

    2) query() 함수의 매개변수 정보

-각 매개변수로 질의문 만들어 실행해줌

table 조회할 테이블명
columns 가져올 값의 열 이름 배열로 지정
selection select문의 where 조건절 문자열
sesctionArgs 질의문 속 ?에 대응하는 데이터 배열
groupBy select문의 group by절 문자열
having select문의 having 조건
orderBy select문의 order by 조건

[데이터베이스 관리] : SQLiteOpenHelper 클래스 이용

-SQLiteOpenHelper 클래스 : DB 관리 코드 추상화 클래스

      -테이블 생성/변경/제거 코드의 추상화

      -이 클래스 상속받는 하위 클래스 작성해서 DB 관리 O

구조적 분리 작성 O
-(
테이블 생성 코드/테이블 변경 코드) 분리 작성 O

-( 데이터 조작 질의문 ) 따로 작성

     <SQLiteOpenHelper의 하위 클래스 >

class DBHelper(context: Context) : SQLiteOpenHelper(context, “testdb”, null, 1) {
   override fun onCreate(db: SQLiteDatabase?){  }
   override fun onUpgrade(db: SQLiteDatabase?, oldVersion:Int, newVersion: Int){ }
onCreate() 앱 설치 후 최초 한 번만 호출됨
이 곳에 데이터베이스 테이블 생성 코드 주로 작성
onUpgrate() DB 버전 정보 바뀔 때마다 반복 호출됨
이 곳에 테이블 스키마 변경 코드 주로 작성 

     <SQLiteOpenHelper 이용 시, SQLiteDatabase 객체 생성 O>

-SQLiteOpenHelper클래스의 readableDatabase/writeableDatabase 속성으로 객체 생성O


[17-2. 파일에 보관]

-파일 : 앱에서 직접 파일을 만들고 코드에서 직접 데이터 읽고 씀

-이터페이스/프리퍼런스 : 내부적으로 파일 저장되지만 코드에서 직접 X. 특정 API 이용

[안드로이드 앱 파일 다루기]

-java.io 패키지 제공 클래스 이용

File 파일. 디렉터리 지칭 클래스
FileInputStream /FileOutputStream 바이트 스트림으로 데이터 읽고 쓰는 클래스
FileReader / FileWriter 문자열 스트림으로 데이터 일고 쓰는 클래스

[안드로이드 파일 저장소 구분]

[내장 메모리의 파일 이용]

-내장 메모리 : 앱 설치되면 시스템에서 자동 할당하는 공간

-다른 앱에서는 접근 X. 주로 민감한 데이터를 내장 메모리에 저장

 방법 java.ioFile 클래스 이용

       - File() 생성자에 전달 (Context 객체의 filesDir 속성 지정/ 파일명)

          <파일 객체 생성 후 '데이터 쓰기'>

val file = File(filesDir, “test.txt”) //파일 객체 생성 
val writeStream: OutputStreamWriter = file.writer() //파일 Writer 생성 
writeStream.write(“hello world”) //데이터 쓰기 
writeStream.flush()

         <파일에 저장한 '데이터 읽기'>

val readStream : BufferedReader = file.reader().buffered() //파일 Reader 생성
readStream.forEachLine {
    Log.d(“kkang”, “$it”) 
}

 

 방법 Context 객체 제공 openFileOuput(), openFileInput() 함수 이용

openFileOutput(“test.txt”, Context.MODE_PRIVATE).use{
   it.write(“hello world” .toByteArray() )  //쓰기 
}
openFileInput(“test.txt”).bufferedReader().forEachLine {
  Log.d(“kkand”, “$it”)  //읽기 
}

[외장 메모리의 파일 이용]

    <기본 준비>

 (1) 외장 메모리 사용 가능 여부 판단

Environment.getExternalStorageState() 함수 반환값 == MEDIA_MOUNTED 이면 사용 가능 상태 O

 (2) 매니페스트 설정

-버전과 파일 이용 방식에 따라 다르지만 API 호환성 위해 모두 설정 필요

     [->매니 페스트 속 퍼미션 설정]

android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE

     [->매니 페스트 속 requestLegacyExternalStorage 값 설정]


[ . 앱별 저장소 이용]

*앱별 저장소 : 개별 앱에 할당된 공간. 기본적으로 해당 앱에서만 접근 O

     -앱이 삭제되면 앱별 저장소 파일도 모두 삭제됨

     -만약, 외부 앱에서 접근 가능하게 하려면 파일 프로바이더로 공개

          <앱별 저장소 위치 구하기> : getExternalFilesDir() 함수

-함수의 매개변수에 파일 종류 나타냄 (Environment 상수값)

Environment.DIRECTORY_PICTURES
Environment.DIRECTORY_DOCUMENTS
Environment.DIRECTORY_MUSIC
Environment.DIRECTORY_MOVIES

           <이 함수 null일 때 반환하는 기본 위치>

/storage/emulated/0/Android/data/패키지명/files

            <파일 프로바이더로 외부 공유>

1) 파일 프로바이더 XML 파일
     - res/xml 디렉터리 하위에 만듬

     - 외부에 공유할 파일 경로 : external-pathpath 속성에 지정

2) 매니페스트에 <provider> 태그로 XML 파일 등록
      -<provider> 의 하위 <meta-data>태그의 resource 속성에 파일 명시

[ . 공용 저장소 이용]

*공용 저장소 : 모든 앱을 위한 공간

     -앱을 삭제해도 파일 삭제 X

     -안드로이드 시스템에서 해당 파일 종류에 따라 지정한 폴더임

     -공용 저장소는 시스템이 제공하는 API 이용하여 접근


       <공용 저장소에 저장된 파일 정보 가져와서 로그로 출력>

contentResolver.query() 함수첫 번째 매개변수에 지정 가능 Uri

MediaStore.Images 공용 저장소 DCIM. Pictures 디렉터리 가리킴
MediaStore.Video DCIM. Movies, Pictures 디렉터리 가리킴
MediaStore.Audio Alarms, Audiobooks, Music, Notifications, Podcasts, Ringtones 디렉터리 가리킴

        <읽어온 파일 정보 이용 -> 데이터 Uri 값 가져오기>

ContentUris.withAppendedId() 함수두 번째 매개변수가 가져온 이미지 식별값

반환된 Uri값으로 이미지 읽을 수 있는 InputStream 객체 얻어옴


[17-3. 공유된 프리퍼런스에 보관]

[공유된 프리퍼런스 이용]

-공유된 프리퍼런스 : 플랫폼 API에서 제공하는 클래스. 데이터를 키-값 형태로 저장

-간단한 데이터 저장에 유용. 데이터를 내장 메모리 앱 폴더의 XML 파일로 저장

       <SharedPrefernce 객체 얻는 방법>

          1) '액티비티 단위'로 데이터 저장 방법
                   Activity.getPreferences(int mode)
                -이 함수 호출한 액티비티 클래스명 XML 파일 자동 생성되고 이곳에 데이터 저장됨


          2) '앱 전체' 데이터 키-값 형태 저장 방법
                 Context.getSharedPreferences(String name, int mode)
                -이 함수 첫 번째 매개변수에 지정한 문자열 파일에 데이터 저장됨

 

       <공유된 프리퍼런스 이용 데이터 저장>

    (1) SharedPreferencesedit() 함수로 ‘SharedPreferences.Editor 객체 얻기

    (2) 얻은 객체의 put_함수 이용해서 데이터 담기

putBoolean (String key, boolean value)
putInt (String key, int value)
putFloat (String key, float value)
putString (String key, String value)

    (3) commit() 호출하는 순간 데이터 저장됨


       <공유된 프리퍼런스 이용 데이터 가져오기>

-SharedPreferencesgetter() 함수 이용

getBoolean (String key, boolean defValue)
getFloat (String key, float defValue)
getInt (String key, int defValue)
getLong (String key, long defValue)
getString (String key, String defValue)

[앱 설정 화면 만들기]

-AndroidXPreference 이용 권장 : 앱 설정 기능 제공 제트팩의 API

          ≫ <preference 이용 방법>

      (1) build.gradle 파일에 라이브러리 dependencies 추가

implementation ‘androidx.preference:preference-ktx:1.1.1’

      (2) 설정 XML 파일 만들기

-루트 태그 <PreferenceScreen>

-태그 하위에 <SwitchPreferenceCompat>, <Preference> 로 설정 항목 준비

      (3) XML 파일을 코드에 적용

-PreferenceFragmentCompat 클래스 상속받은 프래그먼트로 설정 화면 준비

-상위 클래스의 onCreatePreferences() 함수 재정의하면서 내부에

setPreferencesFromResource() 로 위 설정 XML 파일 전달

       (4) 이 프래그먼트를 액티비티에 출력


          ≫ <설정 화면 구성> | 설정 항목 너무 多 경우

    1) 한 화면 안에, 관련 있는 항목끼리 묶어 화면 구성

   : <PreferenceCategory> 태그 사용

- 한 화면에 보이는 항목끼리 구분 지어 출력 O

     2) 항목들을 설정 화면 여러 개에 나누어 화면 구성

    : <Preference> 태그 사용

                코드에서 분할 화면 구성 

        <설정 화면 분할>

1) 분리한 설정 화면 개수만큼, 설정 XML과 프래그먼트를 각각 만들기
2) 분리한 설정화면들을 모두 포함시켜 보여줄 메인 설정 XML’ 작성
3) 메인 설정 XML에서 각 설정 화면을 <Preference> 태그로 지정
4) <Preference> 태그의 fragment 속성에 항목 클릭 시 전환될 설정화면 지정

        <액티비티에서 분할된 설정화면 보이기>

1) PreferenceFragmentCompat.OnPreferenceStartFragmentCallback 인터페이스 구현 액티비티
2) 내부에 onPreferenceStartFragment() 함수 재정의하여 작성
           //이 함수는 설정 화면 바뀔 때마다 자동 호출 함수
            //따라서 이 함수 안에 설정 화면 바뀔 때마다 액션바에 출력될 제목 지정도 가능

               인텐트이용. 오직 XML 설정만으로 구현

- 메인 설정 화면에서 인텐트를 이용해 하위 설정 화면을 띄우는 방식 )

- 메인 설정 화면에서 <Preference> 태그 하위에 <intent> 태그로 바뀔 설정 화면 지정

       (+) 이때 <intent> 태그 하위에 <extra> 태그로 엑스트라 데이터 포함시킬 수 O
       (+) 명시적 or 암시적 인텐트 정보 설정도 가능

          ≫ <코드에서 설정 제어>

- 코드에서 설정 화면 속 설정값을 제어하는 방법

1) 각 설정 항목 객체를 findPreference() 함수로 얻기
       //findPreference() 의 매개변수는 XML에서 작성한 각 설정 태그 key 속성값

2) findPreference()로 얻은 각 설정 항목 객체로 접근하여 각 설정값을 상황에 맞게 코드에서 조정 가능
       (ex. 코드 안에서 isVisible / title / summary 등의 값을 변경 O )

          <코드에서 일부 자동 설정>

- <EditTextPreference> or <ListPreference> 의 경우
               사용자 입력값 / 선택값을 summary에 자동 지정하려면 SimpleSummaryProvider 이용
     - 이 객체 이용하면 사용자 설정값이 그대로 문자열로 출력됨
     - 이 객체의 하위 클래스에서 이벤트 처리도 가능

         <설정한 값 가져오기>

-프리퍼런스 이용하면 설정 내용이 XML 파일로 자동 저장됨
          단, 설정값은 직접 가져와야 함
                    PreferenceManager.getDefaultSharedPreferences() 함수 이용

           <설정 변경 순간을 감지> (2가지)

          1) Preference.OnpreferenceChangeListener 이용하는 방법

  각각의 프리퍼런스 객체마다 각 이벤트 핸들러 직접 지정하여  각 객체 설정 내용 변경 순간의 이벤트를 처리

          2) SharedPreferences.OnSharedPreferenceChangeListener 이용하는 방법

    모든 설정 객체의 변경을 하나의 이벤트 핸들러에서 처리

                    ⇒ [단, 이 방식 이용 시 사용 조건]

1) 설정 프래그먼트 클래스에서 SharedPreferences.OnsharedPreferenceChangeListener 구현
2) onSharedPreferenceChanged() 재정의
3) registerOnSharedPreferenceChangeListener() 이용하여 이벤트 핸들러 등록
4) unregisterOnSharedPreferenceChangeListener() 이용하여 이벤트 등록 해제

[17-4. 개선된 할 일 목록 앱 만들기] : 실습

[참고] : Do It 안드로이드 앱 프로그래밍 with 코틀린 

728x90