ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TECHIT 앱 스쿨 2기: Android 46일차 (23.07.04)
    [THEC!T] 앱 스쿨2기 : Android 2023. 7. 5. 00:59
    728x90

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

     

    오늘은 어플리케이션 클래스에 대한 설명으로 시작하였습니다.

    어플리케이션 클래스는 간단하게 말하면 같은 애플리케이션 당 하나씩 둘 수 있으며, 애플리케이션 내부라면 어디서든 가져다가 사용할 수 있습니다.

     

    사용 예시

    package com.test.android67_applicationclass
    
    import android.app.Application
    
    class AppClass: Application() {
    
        var value = 0
        var value2 = 0.0
        lateinit var value3: String
    
    }
    package com.test.android67_applicationclass
    
    import android.content.Intent
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import com.test.android67_applicationclass.databinding.ActivityMainBinding
    
    class MainActivity : AppCompatActivity() {
    
        lateinit var activityMainBinding : ActivityMainBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
    
            activityMainBinding.run{
                buttonMain.setOnClickListener {
                    //ApplicatonClass
                    // 애플리케이션 당 하나씩 둘 수 있으며 어디서든지 접근이 가능하다.
                    // Application을 상속받은 클래스를 만드로 프로퍼티들을 정의한다.
                    // AndroidManfest.xml에 application 태그의 name 속성제 지정하여 사용한다.
    
                    val appClass = application as AppClass
                    appClass.value = 100
                    appClass.value2 = 11.11
                    appClass.value3 = "문자열"
    
                    val secondIntent = Intent(this@MainActivity, SecondActivity::class.java)
                    startActivity(secondIntent)
    
                }
            }
    
    
            setContentView(activityMainBinding.root)
        }
    }
    package com.test.android67_applicationclass
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import com.test.android67_applicationclass.databinding.ActivitySecondBinding
    
    class SecondActivity : AppCompatActivity() {
    
        lateinit var activitySecondBinding: ActivitySecondBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            activitySecondBinding = ActivitySecondBinding.inflate(layoutInflater)
    
            activitySecondBinding.run{
                // AppleicationClass 객체를 가져온다.
                val appClass = application as AppClass
    
                textViewSecond.text = "${appClass.value}\n"
                textViewSecond.append("${appClass.value2}\n")
                textViewSecond.append("${appClass.value3}")
            }
    
            setContentView(activitySecondBinding.root)
        }
    }

    다음으로는 내외부 저장소와, FileStream 에 대해서 알려주셨습니다.

     

    내부 저장소:
    내부 저장소는 애플리케이션의 기본 저장 공간으로 사용됩니다. 앱이 설치되면 시스템에 의해 생성되고 관리됩니다. 일반적으로 앱 데이터, 데이터베이스, 설정, 캐시 파일 등을 저장하는 데 사용됩니다. 다른 앱에서 액세스할 수 없으며, 앱을 삭제하면 내부 저장소의 데이터도 함께 삭제됩니다. 따라서 내부 저장소에 저장된 데이터는 앱의 범위에 제한되어 있습니다.

    외부 저장소:
    외부 저장소는 디바이스의 외부에 위치한 저장 장치를 가리킵니다. 일반적으로 SD 카드 또는 기타 외부 저장 장치를 의미합니다. 외부 저장소는 앱 간 데이터 공유, 미디어 파일 (사진, 비디오, 음악 등) 저장 등에 사용됩니다. 사용자가 앱을 삭제하더라도 외부 저장소의 데이터는 보존됩니다. 다른 앱에서도 액세스할 수 있으며, 일부 권한을 통해 외부 저장소에 데이터를 읽고 쓸 수 있습니다.

    내부 저장소와 외부 저장소의 주요 차이점은 다음과 같습니다:

    앱의 범위: 내부 저장소는 앱 자체에서만 액세스할 수 있으며, 외부 저장소는 다른 앱과 공유할 수 있습니다.
    데이터 보존: 앱 삭제 시 내부 저장소의 데이터는 함께 삭제되지만, 외부 저장소의 데이터는 보존됩니다.
    용량 제한: 내부 저장소는 디바이스의 내부 메모리에 저장되기 때문에 제한된 용량을 가질 수 있지만, 외부 저장소는 추가적인 저장 장치로 확장할 수 있습니다.

     

     

    사용예시

    내부저장소 쓰기

    // MODE_PRIVATE : 덮어 씌우기
    // MODE_APPEND : 이어서 쓰기
    val fos = openFileOutput("data1.dat", MODE_PRIVATE)
    val dos = DataOutputStream(fos)

    내부 저장소 읽기

    val fis = openFileInput("data1.dat")
    val dis = DataInputStream(fis)

    * 객체일 경우 : ObjectInputStream(fis)

     

    이외에는 일반적인 FileStream 사용법과 동일합니다. 

     

    외부저장소 쓰기

    val filePath = getExternalFilesDir(null).toString()
    val fos = FileOutputStream("${filePath}/data2.dat")
    val dos = DataOutputStream(fos)

     

    외부저장소 읽기

    // 외부 저장소의 경로를 가져온다
    // emulated/Android/data/패키지명/files
    // getExtermalFilesDir 메서드의 매개변수에는 문자열을 넣어줄 수 있으며
    // files의 하위 폴더 이름을 넣어서 사용할 수 있다.
    // null을 넣으면 files까지의 경로가 된다.
    val filePath = getExternalFilesDir(null).toString()
    val fis = FileInputStream("${filePath}/data2.dat")
    val dis = DataInputStream(fis)

     

    그리고 마지막으로는 FileApp을 통한 접근에 대해서 알려주셨습니다.

     

    사용예시

    lateinit var writeActivityLauncer: ActivityResultLauncher<Intent>
    lateinit var readActivityLauncher: ActivityResultLauncher<Intent>

     

    // FileApp을 통한 접근
    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 = "*/*"
        writeActivityLauncer.launch(fileIntent)
    }
    
    button6.setOnClickListener {
        // 파일 관리 앱의 액티비티를 실행한다.
        val fileIntent = Intent(Intent.ACTION_OPEN_DOCUMENT)
        fileIntent.type = "*/*"
        readActivityLauncher.launch(fileIntent)
    }

     

    // 쓰기용 런처
    val contracts1 = ActivityResultContracts.StartActivityForResult()
    writeActivityLauncer = 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()
                fos.close()
    
                activityMainBinding.textView.text = "Downloads 폴더에 저장."
    
            }
        }
    }
    
    // 읽기용 런처
    val contracts2 = ActivityResultContracts.StartActivityForResult()
    readActivityLauncher = registerForActivityResult(contracts2){
        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")
    
            }
        }
    
    }

     

     

    이후에는 하기 문제를 내주신 후에 각자 작성해볼 시간을 주신 후 같이 작성 및 설명을 해주셨습니다.

     

     

    강사님이 작성해주시면서 설명해주신 코드와 제가 작성한 코드의 차이점으로는 파일을 읽어올때 예외처리에 대한 부분이였습니다. 저의 경우 읽어올 정보가 없을 때 예외가 발생되는 것을 이용하여 while문을 중지 시켰으나, 강사님께서는 저장한 테이터의 갯수 또한 별도로 저장하여 예외처리를 하지 않는 방향으로 작성하셨습니다.

     

    해당 문제에 대한 설명을 마치신 후 상기문제에서 입력받을 데이터는 이름, 나이, 키, 몸무게 이다. 라는 조건으로 변경하여 문제를 내주셨습니다. (복습의 의미) 추가로 데이터가 입력되지 않은 것이 있을 때 팝업창으로 알려주는 기능을 추가해 보았습니다.

     

    해당 문제에 대하여 작성한 애플리케이션의 작동 영상입니다.

     

     

     

    마무리

    요즘 더워서 그런지 아니면 내용의 난이도가 올라가서 그런지 모르겠지만, 약간 슬럼프 비슷하게 온 것 같습니다.

    알고리즘 공부도 진전이 없고, 복습을 하고는 있지만 뭔가 막혀있는 듯한 느낌이고, 심지어 몸상태도 좋지않아 총체적 난국 인 것 같습니다. 그래도 다행인 것은 오늘 배운 내용에 대해서는 지금까지 배웠던 파일 입출력과 일부 차이만 있을 뿐 큰 차이는 없기에 이해하기 수월하여 좋았습니다.

     

    오늘의 마음가짐

    이또한 지나가리라.....

     

Designed by Tistory.