context 키워드는 많이 봐왔지만 아직 개념을 정확히 모르는 것 같아 정리하려고 한다.

context는 사전적 의미로 문맥, 맥락 등을 의미한다. 안드로이드에서도 어플리케이션에 대해서 현재 상태를 나타내는 역할을 한다.
어플리케이션의 현재 상태를 가지고 있다.
시스템이 관리하고 있는 액티비티, 어플리케이션의 정보를 얻기 위해 사용한다.
안드로이드 시스템 서비스에서 제공하는 API에 접근하기 위해 사용한다.
Activity, Application 클래스는 Context 클래스를 상속받은 클래스이다.
Context를 언제 어떤 Context를 사용해야할까??
데이터베이스를 관장하는 AppDatabase 처럼 싱글톤으로 존재하는 경우 초기화할때 context가 필요하게 되는데 그때 activity context로 전달하게 된다면 Activity의 생명주기에 따라 어느 시점에 context는 소멸이 될것이다.
AppDatabase는 싱글톤이기 때문에 해당 Activity Context를 지속적으로 참조하게 되어 메모리 누수가 발생하게 되고 이럴때는 Application Context를 사용하는 것이 바람직하다.
GUI에 관련된 것들에는 Application Context가 정상적으로 동작하지 않을 수 있기 때문에 무조건적으로 Application Context를 쓰는 것은 바람직하지 않다.
즉, 생명주기에 따른 범위(Scope)를 명심하여 Context를 참조하여야 한다.
출처
https://velog.io/@haero_kim/Android-Context-%EB%84%88-%EB%8C%80%EC%B2%B4-%EB%AD%90%EC%95%BC
https://youngdroidstudy.tistory.com/entry/Kotlin-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Context
프로그램의 설정 정보 (사용자의 옵션 선택 사항이나 프로그램의 구성 정보)를 영구적으로 저장하는 용도로 사용
XML 포맷의 텍스트 파일에 키-값 세트로 정보를 저장
SharedPreferences 클래스
getSharedPreferences (name, mode)
ex)
val sharedPref = activity?.getSharedPreferences( getString(R.string.preference_file_key), Context.MODE_PRIVATE)
ex)
val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE)
실습



plugins { .... id 'kotlin-kapt' } ..... dependencies { ...... def room_version = "2.5.1" implementation "androidx.room:room-runtime:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version" // optional - Kotlin Extensions and Coroutines support for Room implementation "androidx.room:room-ktx:$room_version" // optional - Test helpers testImplementation "androidx.room:room-testing:$room_version" }
(SQLite보다 Room을 사용할 것을 권장함)
ex)
@Entity(tableName = "student_table") // 테이블 이름을 student_table로 지정함 data class Student ( @PrimaryKey @ColumnInfo(name = "student_id") val id: Int, val name: String )
@Query("SELECT * from table") fun getAllData() : List<Data>
@Query("SELECT * from table") fun getAllData() : LiveData<List<Data>>
@Query("SELECT * FROM student_table WHERE name = :sname")
suspend fun getStudentByName(sname: String): List<Student>
인자 sname을 여기에서 :sname으로 사용
fun 앞에 suspend는 Kotlin coroutine을 사용하는 것이다. 나중에 이 메소드를 부를 때는 runBlocking {} 내에서 호출해야 한다.
LiveData는 비동기적으로 동작하기 때문에 coroutine으로 할 필요가 없다.
@Dao interface MyDAO { @Insert(onConflict = OnConflictStrategy.REPLACE) // INSERT, key 충돌이 나면 새 데이터로 교체 suspend fun insertStudent(student: Student) @Query("SELECT * FROM student_table") fun getAllStudents(): LiveData<List<Student>> // LiveData<> 사용 @Query("SELECT * FROM student_table WHERE name = :sname") suspend fun getStudentByName(sname: String): List<Student> @Delete suspend fun deleteStudent(student: Student); // primary key is used to find the student // ... }
@Database(entities = [Student::class, ClassInfo::class, Enrollment::class, Teacher::class], version = 1) abstract class MyDatabase : RoomDatabase() { abstract fun getMyDao() : MyDAO companion object { private var INSTANCE: MyDatabase? = null private val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { 생략 } } private val MIGRATION_2_3 = object : Migration(2, 3) { override fun migrate(database: SupportSQLiteDatabase) { 생략 } } fun getDatabase(context: Context) : MyDatabase { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder( context, MyDatabase::class.java, "school_database") .addMigrations(MIGRATION_1_2, MIGRATION_2_3) .build() } return INSTANCE as MyDatabase } } }
Room.databaseBuilder(...).addMigrations(MIGRATION_1_2, MIGRATION_2_3)
>
private val MIGRATION_1_2 = object : Migration(1, 2) { // version 1 -> 2
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE student_table ADD COLUMN last_update INTEGER")
}
}
>
private val MIGRATION_2_3 = object : Migration(2, 3) { // version 2 -> 3
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE class_table ADD COLUMN last_update INTEGER")
}
}
myDao = MyDatabase.getDatabase(this).getMyDao() runBlocking { // (주의) UI를 블록할 수 있는 DAO 메소드를 UI 스레드에서 바로 호출하면 안됨 myDao.insertStudent(Student(1, "james")) // suspend 지정되어 있음 } val allStudents = myDao.getAllStudents() // LiveData는 Observer를 통해 비동기적으로 데이터를 가져옴
LiveData의 핵심 특징
1. 수명주기 인식
2. UI와 데이터 상태의 일관성 유지
3. 중앙 집중적인 데이터 관리
4. 데이터 변경에 따른 자동 업데이트
val allStudents = myDao.getAllStudents() allStudents.observe(this) { // Observer::onChanged() 는 SAM 이기 때문에 lambda로 대체 val str = StringBuilder().apply { for ((id, name) in it) { append(id) append("-") append(name) append("\n") } }.toString() binding.textStudentList.text = str }
실습

신기하당
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <!-- 정밀 위치 권한 요청 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> ... </manifest>
private fun requestLocationPermission() { if (ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { // 권한이 없을 경우, 사용자에게 요청 ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_ACCESS_FINE_LOCATION ) } else { // 권한이 이미 있을 경우, 위치 정보를 사용할 수 있음 getLocation() } } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray ) { when (requestCode) { PERMISSION_REQUEST_ACCESS_FINE_LOCATION -> { if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // 권한이 부여되면 위치 정보를 사용할 수 있음 getLocation() } else { // 권한이 거부되면, 기능 사용 불가 } return } } } private fun getLocation
- requestLocationPermission : 앱에 위치 권한이 있는지 확인. 없다면 AcitivtyCompat.requestPermissions 메소드를 사용하여 권한을 요청
onRequestPermissionsResult : 권한이 없다면 처리
getLocation : 권한 부여시 getLocation 메소드를 호출하여 위치 정보 사용 가능
val manager = getSystemService(LOCATION_SERVICE) as LocationManager
var result = "All Providers : " val providers = manager.allProviders for (provider in providers) { result += " $provider. " } Log.d("maptest", result) // All Providers : passive, gps, network..
result = "Enabled Providers : " val enabledProviders = manager.getProviders(true) for (provider in enabledProviders) { result += " $provider. " } Log.d("maptest", result) // Enabled Providers : passive, gps, network..
if (ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) { val location: Location? = manager.getLastKnownLocation(LocationManager.GPS_PROVIDER) location?.let{ val latitude = location.latitude val longitude = location.longitude val accuracy = location.accuracy val time = location.time Log.d("map_test", "$latitude, $location, $accuracy, $time") } }
val listener: LocationListener = object : LocationListener { override fun onLocationChanged(location: Location) { Log.d("map_test,","${location.latitude}, ${location.longitude}, ${location.accuracy}") } } manager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10_000L, 10f, listener) // (.. 생략 ..) // manager.removeUpdates(listener)
다음과 같이 선언 implementation 'com.google.android.gms:play-services:12.0.1'
FusedLocationProviderClient : 위치정보를 얻습니다.
GoogleApiClient: 위치 제공자 준비 등 다양한 콜백을 제공합니다.
GoogleApi Client에서는 GoogleApiClient.ConnectionCallbacks와 GoogleApiClient.OnConnection FailedListener 인터페이스를 구현한 객체를 지정한다.
val connectionCallback = object: GoogleApiClient.ConnectionCallbacks{ override fun onConnected(p0: Bundle?) { // 위치 제공자를 사용할 수 있을 때 // 위치 획득 } override fun onConnectionSuspended(p0: Int) { // 위치 제공자를 사용할 수 없을 때 } } val onConnectionFailCallback = object : GoogleApiClient.OnConnectionFailedListener{ override fun onConnectionFailed(p0: ConnectionResult) { // 사용할 수 있는 위치 제공자가 없을 때 } } val apiClient = GoogleApiClient.Builder(this) .addApi(LocationServices.API) .addConnectionCallbacks(connectionCallback) .addOnConnectionFailedListener(onConnectionFailCallback) .build()
val providerClient = LocationServices.getFusedLocationProviderClient(this)
apiClient.connect()
// 위치 제공자를 사용할 수 있는 상황일 때 override fun onConnected(p0: Bundle?) { if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) === PackageManager.PERMISSION_GRANTED){ providerClient.lastLocation.addOnSuccessListener( this@MainActivity, object: OnSuccessListener<Location> { override fun onSuccess(p0: Location?) { p0?.let { val latitude = p0.latitude val longitude = p0.longitude Log.d("map_test", "$latitude, $longitude") } } } ) apiClient.disconnect() } }
구글맵 실습중 manifest에 추가해도 권한 설정이 안불러와졌는데...
https://velog.io/@kang9366/Unresolved-reference-Manifest
블로그 보고 해결했다...후