Code Monkey home page Code Monkey logo

daily-study's Introduction

daily-study

Development Tips Archive ๐Ÿ’จ

This repository contains a collection of simple and easy development tips primarily focused on Android. Intended for the purpose of archiving - sometimes you just want to revisit those tips. links

daily-study's People

Contributors

itssweetrain avatar

Watchers

 avatar

daily-study's Issues

File ์‚ฌ์ด์ฆˆ ๊ตฌํ•˜๋Š” ๋ฐฉ๋ฒ•

File()

java.io.File์˜ length() : ํŒŒ์ผ์˜ byte๋‹จ์œ„ ํฌ๊ธฐ๋ฅผ ๋ฆฌํ„ด

import java.io.File;
import java.io.IOException;
public class GetFileSize {
public static void main(String[] args) throws IOException { ย 
    File file = new File("d:\\example\\image.jpg");

    long bytes = file.length();
    long kilobyte = bytes / 1024;
    long megabyte = kilobyte / 1024;
  
    System.out.println(bytes + " byte"); // 3980059 byte
    System.out.println(kilobyte + " kb"); // 3886 kb
    System.out.println(megabyte + " mb"); // 3 mb
  }
}

MB > KB > Byte

configuration on demand

Gradle ๋นŒ๋“œ ์‹œ์Šคํ…œ์—์„œ์˜ Configuration on Demand

๋นŒ๋“œ ํ”„๋กœ์„ธ์Šค๊ฐ€ ํŠน์ • ์ž‘์—…์„ ์‹คํ–‰ํ•  ๋•Œ ๊ทธ ์ž‘์—…์— ํ•„์š”ํ•œ ํ”„๋กœ์ ํŠธ๋งŒ ๊ตฌ์„ฑํ•จ. ๋•Œ๋ฌธ์— multiple modules project์—์„œ๋งŒ ์œ ์˜๋ฏธํ•จ. Gradle ํ”„๋กœ์ ํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ์„œ๋ธŒํ”„๋กœ์ ํŠธ๋ฅผ ๊ตฌ์„ฑํ•˜์ง€๋งŒ, configuration on demand๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‹ค์ œ๋กœ ํ•„์š”ํ•œ ์„œ๋ธŒํ”„๋กœ์ ํŠธ๋งŒ ๊ตฌ์„ฑํ•จ. ์ด๋ฅผ ํ†ตํ•ด ์ดˆ๊ธฐ ๊ตฌ์„ฑ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•  ์ˆ˜ ์žˆ์Œ.

// settings.gradle ํŒŒ์ผ์—์„œ ์„ค์ •
org.gradle.configureondemand=true

๋Ÿฐํƒ€์ž„ ๊ตฌ์„ฑ์—์„œ์˜ Configuration on Demand

์•ฑ์ด ์‹คํ–‰ ์ค‘์ผ ๋•Œ์˜ configuration on demand๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•  ๋•Œ๋งŒ ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๋‚˜ ์„ค์ •์„ ๋กœ๋“œํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธ. ์ด๋Š” ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ํšจ์œจ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋จ.
e.g. ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ๋•Œ๋งŒ ํ•ด๋‹น ๊ธฐ๋Šฅ์— ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ๋กœ๋“œํ•˜๊ฑฐ๋‚˜ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ฐฉ์‹.

์ด ๋‘ ๊ฐ€์ง€ ๋งฅ๋ฝ ๋ชจ๋‘ configuration on demand๋Š” ํ•„์š”ํ•  ๋•Œ๋งŒ ํ•„์š”ํ•œ ๊ฒƒ์„ ๊ตฌ์„ฑํ•˜์—ฌ ํšจ์œจ์„ฑ์„ ๋†’์ด๋Š” ๊ฐœ๋…์„ ์ค‘์‹ฌ์œผ๋กœ ํ•จ.

gradle docs : https://docs.gradle.org/7.6/userguide/multi_project_configuration_and_execution.html#sec:configuration_on_demand

buildConfigField, resValue, manifestPlaceholders

buildConfigField, resValue, manifestPlaceholders

There are 3 types of variables in build.gradle : buildConfigField, resValue, manifestPlaceholders.
All of them are defined in Gradle "build" task, so their scope is "build".


Using buildConfigField will generate an actual Java constant in your app's generated BuildConfig class.
So for your example, you would then have something like this:

public static class BuildConfig {
    public static final String BASE_URL = "xxxxxxxxxx";
}

Using resValue will generate a resource of the type you specify into your app's res (resources) directory. So for a string, you'd be able to reference it via XML with @string/base_url or with getResources().getString(R.string.base_url).

resValue๋Š” ์•ฑ์˜ ๋นŒ๋“œ ๊ตฌ์„ฑ ์ค‘์—์„œ ๋ฆฌ์†Œ์Šค(๋ฌธ์ž์—ด, ์ •์ˆ˜, ์ƒ‰์ƒ ๋“ฑ) ๊ฐ’์„ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” gradle ์Šคํฌ๋ฆฝํŠธ์˜ ๋ฉ”์„œ๋“œ ์ค‘ ํ•˜๋‚˜. ๋นŒ๋“œ ํƒ€์ž„์— ๊ฐ’์„ ์ƒ์„ฑํ•˜์—ฌ ๋ฆฌ์†Œ์Šค ํŒŒ์ผ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ.


Using manifestPlaceholders allows you to add a substitution in AndroidManifest.xml.
So for example, GCM requires you to have a tag with the name set to X.permission.C2D_MESSAGE, where X is your application ID. So if you have multiple build flavors with different application IDs, you can use a manifestPlaceholders tag and then use it in your AndroidManifest.xml where it will be replaced with the correct value, like:

<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />

For the record, applicationId is automatically added as a manifest placeholder, so there's no need to define it yourself, but that's just an example.

Basically, it depends on where you need it. If it's needed for an XML resource (a layout, menu, etc.), resValue is what you want. If it's needed to be accessed from Java code directly, buildConfigField will do the trick. For substitutions in the manifest, manifestPlaceholders is what you want.

Activity ์ „ํ™˜์‹œ LifeCycle

A : MainActivity
B : DetailActivity

A์—์„œ B๋กœ ์ด๋™์‹œ

Time Line MainActivity DetailActivity
1 onPause()
2 onCreate()
3 onStart()
4 onResume()
5 onStop()

B๊ฐ€ ์ข…๋ฃŒ๋˜๊ณ  A๋กœ ๋ณต๊ท€

Time Line MainActivity DetailActivity
1 onRestart()
2 onStart()
3 onPause()
4 onResume()
5 onStop()
5 onDestroy()
image

code shrinking, resource shrinking

minifyEnabled ์™€ shrinkResources

์•ฑ์˜ ๋นŒ๋“œ ์„ค์ • ์ค‘์—์„œ proguard ๋ฐ ๋ฆฌ์†Œ์Šค ์ถ•์†Œ์™€ ๊ด€๋ จ๋œ ๋ถ€๋ถ„.


์šฉ์–ด

code shrinking : ์•ฑ ๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ข…์† ํ•ญ๋ชฉ์—์„œ ๋ฏธ์‚ฌ์šฉ ํด๋ž˜์Šค, ํ•„๋“œ, ๋ฉ”์„œ๋“œ, ์†์„ฑ์„ ๊ฐ์ง€ํ•˜์—ฌ ์‚ญ์ œ. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ข…์† ํ•ญ๋ชฉ์—์„œ ๋ช‡ ๊ฐœ์˜ API๋งŒ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ถ•์†Œ๋Š” ์•ฑ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฝ”๋“œ๋ฅผ ์‹๋ณ„ํ•˜๊ณ  ์•ฑ์—์„œ ๊ทธ ์ฝ”๋“œ๋งŒ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Œ.

resource shrinking : ์•ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ข…์† ํ•ญ๋ชฉ์˜ ๋ฏธ์‚ฌ์šฉ ๋ฆฌ์†Œ์Šค๋ฅผ ํฌํ•จํ•˜์—ฌ ํŒจํ‚ค์ง•๋œ ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ญ์ œํ•จ. ๋ฆฌ์†Œ์Šค ์ถ•์†Œ๋Š” ์ฝ”๋“œ ์ถ•์†Œ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์—ฌ ๋ฏธ์‚ฌ์šฉ ์ฝ”๋“œ๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋” ์ด์ƒ ์ฐธ์กฐ๋˜์ง€ ์•Š๋Š” ๋ฆฌ์†Œ์Šค๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Œ.

obfuscation : ํด๋ž˜์Šค์™€ ๋ฉค๋ฒ„ ์ด๋ฆ„์„ ์ค„์—ฌ DEX ํŒŒ์ผ ํฌ๊ธฐ๋ฅผ ์ค„์ž„.

optimization : ์ฝ”๋“œ๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ  ๋‹ค์‹œ ์ž‘์„ฑํ•˜์—ฌ ์•ฑ DEX ํŒŒ์ผ์˜ ํฌ๊ธฐ๋ฅผ ๋” ์ค„์ž„. ์˜ˆ๋ฅผ ๋“ค์–ด ์ฃผ์–ด์ง„ if/else ๊ตฌ๋ฌธ์˜ else {} ๋ถ„๊ธฐ๊ฐ€ ์ „ํ˜€ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Œ์„ R8์—์„œ ๊ฐ์ง€ํ•œ ๊ฒฝ์šฐ R8์ด else {} ๋ถ„๊ธฐ ์ฝ”๋“œ๋ฅผ ์‚ญ์ œํ•จ.


์ ์šฉ

  1. minifyEnabled true: proguard ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ถ•์†Œ(minify)ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ. ์ฝ”๋“œ ์ถ•์†Œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ตœ์ ํ™”ํ•˜๊ณ  ๋‚œ๋…ํ™”ํ•˜์—ฌ ์•ฑ ํฌ๊ธฐ๋ฅผ ์ค„์ด๊ณ  ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋จ. (๋””์ปดํŒŒ์ผ์„ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ฌ)

  2. shrinkResources true: ๋ฆฌ์†Œ์Šค ์ถ•์†Œ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ. ๋ฆฌ์†Œ์Šค ์ถ•์†Œ๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฆฌ์†Œ์Šค(์ด๋ฏธ์ง€, ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ ๋“ฑ)๋ฅผ ์ œ๊ฑฐํ•˜์—ฌ ์•ฑ์˜ ํฌ๊ธฐ๋ฅผ ์ค„์ด๋Š” ๋ฐ ๋„์›€์ด ๋จ. ์ด๋Š” ์•ฑ์„ ๋” ๊ฐ€๋ณ๊ฒŒ ๋งŒ๋“ค๊ณ  ๋‹ค์šด๋กœ๋“œ ๋ฐ ์„ค์น˜ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋จ. ๋ฆฌ์†Œ์Šค ์ถ•์†Œ๋Š” ์ฝ”๋“œ ์ถ•์†Œ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ๋งŒ ์ž‘๋™ํ•จ.

์•ฑ์˜ ํฌ๊ธฐ๋ฅผ ์ตœ์ ํ™”ํ•˜๊ณ  ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š”๋ฐ ๋„์›€, ํŠนํžˆ ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์„ ๋ฐฐํฌํ•  ๋•Œ ํ•„์š”ํ•œ ์ž์›์„ ์ตœ์†Œํ•œ์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Œ.


๋‹จ์  (true ์„ ํƒ์‹œ)

  1. ์œ ์ง€ํ•  ์ฝ”๋“œ ์ถฉ๋ถ„ํžˆ ํ™•์ธ์‹œ proguard์— ์ ์šฉ ์‹œ์ผœ์ค˜์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€
  2. ๋นŒ๋“œ์†๋„๊ฐ€ ๋Š๋ ค์ง€๊ธฐ ๋•Œ๋ฌธ์— debug ๋ชจ๋‘์—์„  false๊ฐ€ ์ข‹์Œ(๊ตฌ๊ธ€๊ถŒ์žฅ)

android developer document

(๋งํฌ)

android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type.
            minifyEnabled true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            shrinkResources true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

java.time ํŒจํ‚ค์ง€ ์ •๋ฆฌ

๋งŽ์ด์“ฐ๋Š” ๊ฒƒ๋“ค ํ•˜๋‚˜์”ฉ ์ถ”๊ฐ€์˜ˆ์ •

LocalDate.now() : ํ˜„์žฌ ์‹œ์Šคํ…œ์˜ ๊ธฐ๋ณธ ์‹œ๊ฐ„๋Œ€(default time zone)์— ๋”ฐ๋ผ ํ˜„์žฌ ๋‚ ์งœ๋ฅผ ๊ฐ€์ ธ์˜ด

๊ธฐ๋ณธ ์‹œ๊ฐ„๋Œ€ ์‚ฌ์šฉ: LocalDate.now()๋Š” JVM์ด ์‹คํ–‰ ์ค‘์ธ ์‹œ์Šคํ…œ์˜ ๊ธฐ๋ณธ ์‹œ๊ฐ„๋Œ€ ์„ค์ •์„ ์‚ฌ์šฉํ•˜์—ฌ ํ˜„์žฌ ๋‚ ์งœ๋ฅผ ๊ฐ€์ ธ์˜ด.
๋กœ์ปฌ ์‹œ๊ฐ„๋Œ€ ๋ฐ˜์˜: ์‹œ์Šคํ…œ์˜ ์‹œ๊ฐ„๋Œ€ ์„ค์ •์— ๋”ฐ๋ผ ํ˜„์žฌ ๋‚ ์งœ๊ฐ€ ๊ฒฐ์ •๋˜๋ฏ€๋กœ, ์˜ˆ๋ฅผ ๋“ค์–ด ํ•œ๊ตญ ์‹œ๊ฐ„๋Œ€(KST)์—์„œ๋Š” ํ•œ๊ตญ ๊ธฐ์ค€์˜ ํ˜„์žฌ ๋‚ ์งœ๋ฅผ ๋ฐ˜ํ™˜ํ•จ.

ZoneId zoneId = ZoneId.of("Asia/Seoul"); // ์„œ์šธ ์‹œ๊ฐ„๋Œ€
LocalDate todayInSeoul = LocalDate.now(zoneId);

remember๊ณผ mutableStateOf

In other words the deference between this line

val text = remember { mutableStateOf("") }

and this

val text = remember { "" }

mutableStateOf()

it's an observable that observes the values when underlaying values gets changes and updates the UI. like we use liveData and stateFlows but LiveData also has lifecycle mechanism and almost same goes with StateFlows.

remember{}

It persists the data across the recomposition.
what is recomposition? When the state get changes, the composable that holds that value recomposes itself to give us updated value.
for example: we have a textView(composable) that observes the value, right now which is 1. We press the button and increment the value with 2. What happens here when the value goes from 1 to 2 the textView(composable) recreates(recomposes) itself and shows us the updated value which is 2, this is known as recomposition.

when we use this: val text = remember{ mutableStateOf("") } that's means we are not only observing the data but also persisting the data across the recomposition.


The remember keyword can store a mutable or an immutable object.
If you pass mutableStateOf to remember, any time the value of that object changes, it will force recomposition of the composables that are reading that value.

compose background, padding, clickable ์ˆœ์„œ

Row(
    modifier = Modifier
        .fillMaxWidth()
        .padding(bottom = 12.dp) //background ์ด์ „ padding > margin
        .clip(RoundedCornerShape(4.dp)) //background ์ด์ „ clipํ•ด์•ผ ๋ชจ์–‘ ์œ ์ง€
        .background(gray10)
        .clickable {
            clickSelectDrinkUnit(item.drinkType, item.drinkUnit)
        }
        .padding(16.dp) //background ๋‹ค์Œ padding > ์•ˆ์ชฝ padding
}

Row() ์ „์ฒด๋ฅผ ํด๋ฆญ ์˜์—ญ์œผ๋กœ ์žก๊ณ ์‹ถ์œผ๋ฉด .padding(bottom = 12.dp) ๋ณด๋‹ค ์•„๋ž˜์— ์ˆœ์„œ๋ฅผ ์žก์•„์•ผํ•จ.
์œ„์— ์œ„์น˜ํ•˜๋ฉด margin 12.dp ๊นŒ์ง€ ๊ฐ™์ด ์˜์—ญ์ด ์žกํž˜.

res/raw vs assets

ํ”„๋กœ์ ํŠธ res/ ๋””๋ ‰ํ„ฐ๋ฆฌ ๋‚ด๋ถ€์—์„œ ์ง€์›๋˜๋Š” ๋ฆฌ์†Œ์Šค ๋””๋ ‰ํ„ฐ๋ฆฌ

๋””๋ ‰ํ„ฐ๋ฆฌ ๋ฆฌ์†Œ์Šค์œ ํ˜•
raw/ ์›์‹œ ํ˜•ํƒœ๋กœ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ์ž„์˜์˜ ํŒŒ์ผ. ์›์‹œ InputStream์œผ๋กœ ์ด๋Ÿฌํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์—ด๋ ค๋ฉด ๋ฆฌ์†Œ์Šค ID(R.raw.filename)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Resources.openRawResource()๋ฅผ ํ˜ธ์ถœ

๊ทธ๋Ÿฌ๋‚˜ ์›๋ž˜ ํŒŒ์ผ ์ด๋ฆ„๊ณผ ํŒŒ์ผ ๊ณ„์ธต ๊ตฌ์กฐ์— ์•ก์„ธ์Šคํ•ด์•ผ ํ•œ๋‹ค๋ฉด res/raw/ ๋Œ€์‹  assets/ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋ฆฌ์†Œ์Šค๋ฅผ ์ €์žฅํ•ด์•ผํ•จ. assets/ ์˜ ํŒŒ์ผ์—๋Š” ๋ฆฌ์†Œ์Šค ID๊ฐ€ ๋ถ€์—ฌ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ AssetManager๋ฅผ ์‚ฌ์šฉํ•ด์„œ๋งŒ ์ฝ์„ ์ˆ˜ ์žˆ์Œ

dark ๋ชจ๋“œ์šฉ lottie ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ raw-night ๊ณผ ๊ฐ™์ด qualifier ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ assets ์€ ๋ถˆ๊ฐ€๋Šฅํ•จ

String color accent

String color accent ์ฃผ๋Š” ๋ฒ•

  1. SpannableStringBuilder()
  binding.txtTitle.text =
      SpannableStringBuilder()
          .append("ํ•ด๋‹น ๊ณต๊ณ ๋Š”")
          .color(ContextCompat.getColor(requireContext(), R.color.secondary_color)) {
              append("๊ฐœ์ธ ์ •๋ณด ๋ณดํ˜ธ")
          }
          .append("๋ฅผ ์œ„ํ•œ ๋‚ด์šฉ์„ ๋‹ด๊ณ ์žˆ์–ด")
          .color(ContextCompat.getColor(requireContext(), R.color.secondary_color)) {
              append("๋ณธ์ธ๋งŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค")
          }

content provider ์ƒ๋ช…์ฃผ๊ธฐ

application ๋ณด๋‹ค content provider๊ฐ€ ๋จผ์ € onCreate ๋จ.

ContentProvider๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ ๋ฐ์ดํ„ฐ ๊ณต์œ  ๋ฐ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์ปดํฌ๋„ŒํŠธ๋กœ, ๋‹ค๋ฅธ ์•ฑ์ด๋‚˜ ์•ฑ ๋‚ด์˜ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์™€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋จ. ContentProvider์˜ ๊ฒฝ์šฐ, ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ ์•ฑ ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์™€ ์—ฐ๋™๋˜๋ฏ€๋กœ Context๋ฅผ ์–ป๋Š” ๋ฐฉ๋ฒ•์— ์ฃผ์˜ํ•ด์•ผ ํ•จ.

ContentProvider ํด๋ž˜์Šค๋Š” Context๋ฅผ ์ง์ ‘ ์ƒ์†๋ฐ›์ง€ ์•Š์Œ. ๊ทธ๋Ÿฌ๋‚˜ getContext() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด Context์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Œ. ์ด ๋ฉ”์„œ๋“œ๋Š” ContentProvider๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ์‹œ์Šคํ…œ์— ์˜ํ•ด ํ˜ธ์ถœ๋˜๊ณ , ํ•ด๋‹น ์ปจํ…์ŠคํŠธ๋Š” ContentProvider์˜ ๋ผ์ดํ”„์‚ฌ์ดํด ๋™์•ˆ ์œ ํšจํ•จ.

getContext()๋ฅผ ํ†ตํ•ด ์–ป์€ Context๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ Application ์ปจํ…์ŠคํŠธ์ž„. ๋”ฐ๋ผ์„œ ContentProvider์—์„œ ์–ป์€ Context๋Š” ํ•ด๋‹น ์•ฑ ์ „์ฒด์— ๋Œ€ํ•œ ์ปจํ…์ŠคํŠธ์ž„.

Called when the application is starting, before any activity, service, or receiver objects(excluding content providers) have been created.

image

content provider -> initialize(startup) dependencies (xx wrapper initialize) -> base applciation

buildConfigField๋Š” ๋นŒ๋“œ๋˜๋ฉด์„œ ๋ฌธ์ž์—ด์ด ์ˆจ๊ฒจ์ง€๋Š”๊ฐ€

buildConfigField ๋Š” ์•ฑ Build ์‹œ์— ์ฝ”๋“œ์— ๋ฌธ์ž์—ด์„ ์น˜ํ™˜ํ•ด๋ฒ„๋ฆผ.

buildConfigField๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋‚˜ ์ค‘์š”ํ•œ ๋ฌธ์ž์—ด์„ ์ฝ”๋“œ์— ์ˆจ๊ธฐ๋ ค๊ณ  ํ•˜์ง€๋งŒ, ์ด๋Ÿฌํ•œ ๊ฐ’๋“ค์€ ์‹ค์ œ๋กœ ์•ฑ์ด ๋นŒ๋“œ๋  ๋•Œ ํ•ด๋‹น ์œ„์น˜์— ์ง์ ‘ ์‚ฝ์ž…๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋ฅผ ๋””์ปดํŒŒ์ผํ•˜๋ฉด ํ•ด๋‹น ๋ฌธ์ž์—ด์ด ๊ทธ๋Œ€๋กœ ๋…ธ์ถœ๋จ.

//์ž‘์„ฑํ•œ ์ฝ”๋“œ
String decStr = new AesCrypto(BuildConfig.SOME_KEY).decrypt(encStr); 
//๋นŒ๋“œ ํ›„ ๋ฌธ์ž์—ด๋กœ ์น˜ํ™˜
String decStr = new AesCrypto("gDE4g5DFghsf5523HDSFdfg").decrypt(encStr); ์ด๋ ‡๊ฒŒ SOME_KEY ์˜ ๋ฌธ์ž์—ด๋กœ ์น˜ํ™˜๋˜๋Š” ๊ฒ๋‹ˆ๋‹ค.

diag dim

dialog dim ์ฒ˜๋ฆฌ ํ•˜๋Š” ๋ฒ•

getDialog().getWindow().setDimAmount(0.7f);
<item name="android:backgroundDimAmount">0.7</item>

A extends BaseDialog๋ฅผ ํ•ด๋„ BaseDialog์˜ setStyle์ด ์ ์šฉ๋˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ A์˜ setStyle์ด ์ ์šฉ๋จ. ๊ทธ๋ฆฌ๊ณ  xml์ด ๊ทธ ์œ„์ž„.


0์€ no dimming, 1์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก black์— ๊ฐ€๊นŒ์›€

Values are between 0 and 1. Zero meaning there is no dimming, and 1 meaning 100% black.

activity, fragment์—์„œ binding class ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ

fragment์—์„œ viewBidning

viewBinding์„ ์‚ฌ์šฉํ•˜๋ฉด binding class์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด view ์ฐธ์กฐ๊ฐ€ ๊ฐ€๋Šฅํ•จ.
fragment๋Š” view๋ณด๋‹ค ์ˆ˜๋ช…์ด ๊น€. ๋”ฐ๋ผ์„œ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ lifecycle์—์„œ ๋ทฐ๊ฐ€ ์ œ๊ฑฐ๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” onDestroyView() ์ฝœ๋ฐฑ ๋ฉ”์†Œ๋“œ์—์„œ binding class์˜ ์ธ์Šคํ„ด์Šค๋ฅผ null ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์ฐธ์กฐ๋ฅผ ์ •๋ฆฌํ•ด์ฃผ์–ด์•ผ ํ•จ.

Fragments outlive their views. 
Make sure you clean up any references to the binding class instance in the fragment's onDestroyView() method.
// ํ”„๋ž˜๊ทธ๋จผํŠธ๋Š” ๋ทฐ๋ณด๋‹ค ์˜ค๋ž˜ ์ง€์†๋ฉ๋‹ˆ๋‹ค. ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ onDestroyView()๋ฉ”์„œ๋“œ์—์„œ ๊ฒฐํ•ฉ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค ์ฐธ์กฐ๋ฅผ ์ •๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
override fun onDestroyView() {
      super.onDestroyView()
      _binding = null
}

Not All ViewBinding Need Null Setting

ํ•ญ์ƒ null์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ๋ฅผ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋จ. activity๋Š” view๋ณด๋‹ค outliveํ•˜์ง€ ์•Š์Œ.

private lateinit var binding: ResultProfileBinding

lateinit var ์ด ๋ณ€์ˆ˜๋Š” ์˜ค์ง activity๊ฐ€ onCreate() ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งŒ ์ƒ์„ฑ.

The reason is that the Activity will not outlive the ViewBinding. 
The view of the Activity is always available since onCreate() until the end of the Activity Destroy.

image

The binding is lateinit var binding, is because this variable is not set when the activity is first constructed. It is only constructed during onCreate().

diag tag

dialog tag์˜ ์—ญํ• 

tag ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” Android ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ฌธ์ž์—ด. ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์‹๋ณ„์ž๋กœ ์‹๋ณ„ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋จ.
FragmentTransaction์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ tag๋ฅผ ์ง€์ •ํ•˜๋ฉด ํ•ด๋‹น ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ์Œ. ์ด๊ฒƒ์€ ํŠนํžˆ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ๋‚˜์ค‘์— ๊ฒ€์ƒ‰ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•ด์•ผ ํ•  ๋•Œ ์œ ์šฉํ•จ.

ex) FragmentTransaction์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ

MyFragment myFragment = new MyFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragment_container, myFragment, "myFragmentTag");
ft.commit();
//"myFragmentTag"๊ฐ€ tag

MyFragment myFragment = (MyFragment) getSupportFragmentManager().findFragmentByTag("myFragmentTag");

if (myFragment != null) {
    // ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์ฐพ์•˜์œผ๋ฏ€๋กœ ์ž‘์—… ์ˆ˜ํ–‰
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.remove(myFragment);
    ft.commit();
}

tag์— ๋Œ€ํ•œ ์„ค๋ช…

/**
 * Display the dialog, adding the fragment to the given FragmentManager.  This
 * is a convenience for explicitly creating a transaction, adding the
 * fragment to it with the given tag, and {@link FragmentTransaction#commit() committing} it.
 * This does <em>not</em> add the transaction to the fragment back stack.  When the fragment
 * is dismissed, a new transaction will be executed to remove it from
 * the activity.
 * @param manager The FragmentManager this fragment will be added to.
 * @param tag The tag for this fragment, as per
 * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
 */
public void show(@NonNull FragmentManager manager, @Nullable String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit();
}

theme status bar ์—†์• ๊ธฐ

์ƒํƒœ๋ฐ” ์ƒ‰์ƒ๋ณ€๊ฒฝ

<item name="android:statusBarColor">@android:color/white</item>

์ƒํƒœ๋ฐ” ์—†์• ๊ธฐ

<item name="android:windowFullscreen">true</item>

onCreateView(), onDestroyView() ์ดํ›„ viewLifecycleOwner access

java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView()
at androidx.fragment.app.Fragment.getViewLifecycleOwner(Fragment.java:377)
at ....initOnBackPressedCallback

  override fun onAttach(context: Context) {
        super.onAttach(context)
        initOnBackPressedCallback()
    }

๊ตฌ๊ธ€ ์Šคํ† ์–ด์—์„œ ์•ฑ์„ ์—…๋ฐ์ดํŠธ์‹œ ์ธ์Šคํ„ด์Šค

๊ตฌ๊ธ€ ์Šคํ† ์–ด์—์„œ ์•ฑ์„ ์—…๋ฐ์ดํŠธ์‹œ ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ๊ธฐ๋Š”๊ฐ€

์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์Œ.
์•ฑ์„ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ์ƒˆ๋กœ์šด APK ํŒŒ์ผ์ด ๊ธฐ๊ธฐ๋กœ ๋‹ค์šด๋กœ๋“œ๋˜๊ณ  ์„ค์น˜๋จ. ๊ทธ๋Ÿฌ๋‚˜ ์‚ฌ์šฉ ์ค‘์ธ ์•ฑ์˜ ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๋Š” ์ข…๋ฃŒ๋˜์ง€ ์•Š๊ณ , ์—…๋ฐ์ดํŠธ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„์—๋Š” ์ƒˆ๋กœ์šด APK ํŒŒ์ผ์˜ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋จ.

์•ฑ์„ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ๋‹จ์ˆœํžˆ ์•ฑ์˜ ์ƒˆ ๋ฒ„์ „์ด ๊ธฐ์กด์˜ ์„ค์น˜๋œ ์•ฑ์œผ๋กœ ๋Œ€์ฒด๋จ. ๋”ฐ๋ผ์„œ ์•ฑ์ด ์—…๋ฐ์ดํŠธ๋˜๋”๋ผ๋„ ์‚ฌ์šฉ ์ค‘์ธ ์•ฑ์˜ ์ธ์Šคํ„ด์Šค๋Š” ์ข…๋ฃŒ๋˜์ง€ ์•Š๊ณ  ์œ ์ง€.

fragment onCreateView, onViewCreated

onCreateView์™€ onViewCreated

onCreateView
onCreateView๋Š” Fragment๊ฐ€ ์ฒ˜์Œ์œผ๋กœ UI๋ฅผ ๊ทธ๋ฆด ๋•Œ ํ˜ธ์ถœ.
์ด ๋ฉ”์„œ๋“œ์—์„œ๋Š” Fragment์˜ UI๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ๊ด€๋ จ๋œ View๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•จ. ์—ฌ๊ธฐ์„œ LayoutInflater๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ XML ๋ ˆ์ด์•„์›ƒ์„ ์ธํ”Œ๋ ˆ์ดํŠธํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋กœ ๋‚˜์˜จ View๋ฅผ ์ดˆ๊ธฐํ™” ๋ฐ ์„ค์ •. return ๊ฐ’ View๋Š” Fragment์˜ UI ์š”์†Œ๋กœ ์‚ฌ์šฉ๋จ.

onViewCreated
onViewCreated๋Š” onCreateView์—์„œ ๋ฐ˜ํ™˜๋œ View๊ฐ€ ์ƒ์„ฑ๋œ ํ›„์— ํ˜ธ์ถœ๋จ.
onCreateView์—์„œ ๋ฐ˜ํ™˜๋œ View์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ์‹œ์ ์—์„œ๋Š” View๊ฐ€ ์ด๋ฏธ ์ƒ์„ฑ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— UI์— ๋Œ€ํ•œ ์ž‘์—…์ด ๊ฐ€๋Šฅํ•จ.
View์— ๋Œ€ํ•œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€์ ์ธ UI ์กฐ์ž‘์„ ํ•  ์ˆ˜ ์žˆ์Œ.

onCreateView๋Š” UI๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ดˆ๊ธฐํ™”ํ•˜๋Š” ์—ญํ• , onViewCreated๋Š” ์ƒ์„ฑ๋œ UI์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•จ.

git pull ์ทจ์†Œ

git pull ์ทจ์†Œํ•˜๊ณ  ๋˜๋Œ๋ฆฌ๊ธฐ

(ex.git pull origin main)

commit changes ๊ทธ๋Œ€๋กœ ๋‚จ๊ธฐ๊ณ  ์‹ถ์„ ๋•Œ

git reset --soft HEAD~1

๋ชจ๋‘ reset.

git reset --hard ORIG_HEAD

git merge ์ทจ์†Œํ•˜๊ณ  ๋˜๋Œ๋ฆฌ๊ธฐ

git reset --merge ORIG_HEAD

git commit ์ทจ์†Œํ•˜๊ณ  ๋˜๋Œ๋ฆฌ๊ธฐ

git reset --hard HEAD

ํ•œ ๋‹จ๊ณ„ ์•ž commit์ด๋‚˜ commit์„ ์‹คํ–‰ํ•˜๊ธฐ ์ „ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅ.

git add ์ทจ์†Œํ•˜๊ณ  ๋˜๋Œ๋ฆฌ๊ธฐ

git reset HEAD

release key ๊ด€๋ฆฌํ•˜๊ธฐ ๋กœ์ปฌ ํŒŒ์ผ ๊ฒฝ๋กœ, ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ์„ค์ •

๋กœ์ปฌ ํŒŒ์ผ ๊ฒฝ๋กœ ์„ค์ •ํ•  ๋•Œ

/Users/parkdanbi/Desktop/project/keystore/release_key.jks

์‹œ์Šคํ…œ ํ™˜๊ฒฝ ์„ค์ •ํ•  ๋•Œ

ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋Š” ์šด์˜์ฒด์ œ์—์„œ ์ด๋ฆ„(Name)๊ณผ ๊ฐ’(value)๋กœ ๊ด€๋ฆฌ๋˜๋Š” ๋ฌธ์ž์—ด ์ •๋ณด.
์šด์˜์ฒด์ œ๊ฐ€ ์„ค์น˜๋  ๋•Œ ๊ธฐ๋ณธ์ ์ธ ๋‚ด์šฉ์ด ์„ค์ •๋˜์ง€๋งŒ, ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ ์‘์šฉํ”„๋กœ๊ทธ๋žจ์ด ์„ค์น˜๋  ๋•Œ ์ž๋™์ ์œผ๋กœ ๋ณ€๊ฒฝ๋˜๊ธฐ๋„ ํ•จ. ์ž๋ฐ” ์ง„์˜์—์„œ๋Š” OS์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜์˜ ๊ฐ’์„ System.getenv( ) ๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Œ.

//String value = Sytstem.getenv(String name); 

System.out.println("์ „์ฒด OS ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฐ’ : " + System.getenv());
System.out.println("OS ํ™˜๊ฒฝ๋ณ€์ˆ˜ NVM_SYMLINK ๊ฐ’: " + System.getenv("NVM_SYMLINK"));

System.getenv("HOME")

๋ฆฌ๋ˆ…์Šค์—์„œ๋Š” System.getenv("HOME") ๊ฐ€ ์ ˆ๋Œ€๊ฒฝ๋กœ(/home/user)๋ฅผ ๋ฆฌํ„ดํ•จ.
On Linux System.getenv("HOME") return absolute path /home/user, but on Windows return Users\user.

ํ”„๋กœ์ ํŠธ ์•ˆ ๋ฆด๋ฆฌ์ฆˆํ‚ค๋ฅผ ๋„ฃ๋Š”๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์•ˆ๋“œ๋กœ์ด๋“œ ํด๋”์— ์ €์žฅํ•ด์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ.

System.getenv("HOME") + "/.android/keystore/release_key.jks

data source์™€ repository ๋‚˜๋ˆ„๋Š” ๊ธฐ์ค€

android์—์„œ data source์™€ repository๋ฅผ ๋‚˜๋ˆ„๋Š” ๊ธฐ์ค€

์—ญํ•  ๋ฐ ์ฑ…์ž„ ๋ถ„๋ฆฌ:
๋ฐ์ดํ„ฐ ์†Œ์Šค (DataSource): ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ์ปฌ ๋˜๋Š” ์›๊ฒฉ ์†Œ์Šค์—์„œ ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ• ์„ ์ˆ˜ํ•จ. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋กœ์ปฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ, ์บ์‹œ ๋“ฑ์ด ํฌํ•จ๋  ์ˆ˜ ์žˆ์Œ.
๋ ˆํฌ์ง€ํ† ๋ฆฌ (Repository): ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ณ , UI์™€ ํ†ต์‹ ํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•จ. ์ฆ‰, ๋ฐ์ดํ„ฐ์˜ ์†Œ์Šค๋ฅผ ์ถ”์ƒํ™”ํ•˜๊ณ , ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ˜• ๋ฐ ๊ฐ€๊ณต์„ ๋‹ด๋‹นํ•จ.

์•ฑ ์•„ํ‚คํ…์ฒ˜์— ๋”ฐ๋ฅธ ๋ถ„๋ฆฌ:
์ผ๋ฐ˜์ ์œผ๋กœ Android ์•ฑ์—์„œ๋Š” MVVM ๋˜๋Š” Clean Architecture์™€ ๊ฐ™์€ ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด์„ ์‚ฌ์šฉํ•จ. ์ด๋Ÿฐ ํŒจํ„ด์—์„œ๋Š” ๋ฐ์ดํ„ฐ ์†Œ์Šค์™€ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๊ฐ€ ๊ฐ์ž์˜ ์ฑ…์ž„์„ ๊ฐ€์ง€๋ฉฐ ์„œ๋กœ์˜ ๊ตฌํ˜„์„ ์•Œ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์ด ์ด์ƒ์ ์ž„.

๊ฐ๊ฐ์˜ ์ฑ…์ž„์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ ์†Œ์Šค์™€ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์Œ. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๋ณ€๊ฒฝํ•ด๋„ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์˜ ์ฝ”๋“œ๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•„์•ผ ํ•จ.

// ๋ฐ์ดํ„ฐ ์†Œ์Šค
interface RemoteDataSource {
    suspend fun fetchData(): Result<Data>
}
// ๋ ˆํฌ์ง€ํ† ๋ฆฌ
class DataRepository(private val remoteDataSource: RemoteDataSource) {
    suspend fun getData(): Result<Data> {
        return remoteDataSource.fetchData().map { .. //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๋งž๊ฒŒ ๊ฐ€๊ณต
    }
}

์ด์™€ ๊ฐ™์ด RemoteDataSource๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ• , DataRepository๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Œ.

find, firstOrNull

find, firstOrNull ์ฐจ์ด

find์™€ firstOrNull์€ Kotlin ์ปฌ๋ ‰์…˜์—์„œ ์š”์†Œ๋ฅผ ์ฐพ๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”์„œ๋“œ. ๋™์ž‘๊ณผ ๋ฐ˜ํ™˜๊ฐ’์—์„œ ์ฐจ์ด๊ฐ€ ์žˆ์Œ.

  • find:

    • find ๋ฉ”์„œ๋“œ๋Š” ์ปฌ๋ ‰์…˜์—์„œ ์กฐ๊ฑด์— ๋งž๋Š” ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์ฐพ์Œ.
    • ๋งŒ์•ฝ ์กฐ๊ฑด์— ๋งž๋Š” ์š”์†Œ๊ฐ€ ์—†์œผ๋ฉด null์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ณ  ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ด.
    • ๋”ฐ๋ผ์„œ find ๋ฉ”์„œ๋“œ๋Š” ์ปฌ๋ ‰์…˜ ๋‚ด์—์„œ ์กฐ๊ฑด์— ๋งž๋Š” ์š”์†Œ๊ฐ€ ๋ฐ˜๋“œ์‹œ ์กด์žฌํ•จ์„ ๊ฐ€์ •ํ•˜๊ณ  ์‚ฌ์šฉํ•จ.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฆฌ์ŠคํŠธ์—์„œ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์ฐพ์„ ๋•Œ ์‚ฌ์šฉ๋จ.
  • firstOrNull:

    • firstOrNull ๋ฉ”์„œ๋“œ๋Š” ์ปฌ๋ ‰์…˜์—์„œ ์กฐ๊ฑด์— ๋งž๋Š” ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์ฐพ์Œ.
    • ์กฐ๊ฑด์— ๋งž๋Š” ์š”์†Œ๊ฐ€ ์—†์œผ๋ฉด null์„ ๋ฐ˜ํ™˜. ์ฆ‰, ์š”์†Œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ์—๋„ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š์Œ.
    • ๋”ฐ๋ผ์„œ firstOrNull ๋ฉ”์„œ๋“œ๋Š” ์กฐ๊ฑด์— ๋งž๋Š” ์š”์†Œ๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉ๋จ.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฆฌ์ŠคํŠธ์—์„œ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์ฐพ์„ ๋•Œ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์ง€๋งŒ, ์š”์†Œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ ์‚ฌ์šฉ๋จ.
  • first ๋Š” ์กฐ๊ฑด์— ๋งž๋Š” ์š”์†Œ๊ฐ€ ์—†์œผ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ.

activity? vs requireActivity()

activity? vs requireActivity()

activity!!๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์ง์ ‘ ๊ฐ€์ ธ์˜ด. ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ null์ด๋ฉด NullPointerException์„ ๋ฐœ์ƒ์‹œํ‚ด.

requireActivity()๋Š” ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ์•กํ‹ฐ๋น„ํ‹ฐ์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์ง€ ์•Š๊ฑฐ๋‚˜ ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ null์ด๋ฉด IllegalStateException์„ ๋ฐœ์ƒ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ ์•ˆ์ •์„ฑ ์ธก๋ฉด์—์„œ ๋” ์•ˆ์ „ํ•จ. > ? ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ null์ด ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” requireActivity()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋จ.

Call

Call

public interface Call<T> extends Cloneable {
  /**
   * Synchronously send the request and return its response.
   *
   * @throws IOException if a problem occurred talking to the server.
   * @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request or
   *     decoding the response.
   */
  Response<T> execute() throws IOException;

ํ•ด๋‹น ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋จ.

  1. ์ธํ„ฐํŽ˜์ด์Šค ์„ค๋ช…:
    Call๋Š” ์ œ๋„ค๋ฆญํ•œ ํ˜•ํƒœ๋กœ ์ •์˜๋˜์–ด ์žˆ์Œ. ๋Š” HTTP ์‘๋‹ต์—์„œ ๊ธฐ๋Œ€๋˜๋Š” ๋ฐ์ดํ„ฐ ํ˜•์‹์„ ๋‚˜ํƒ€๋ƒ„.

  2. ๋ฉ”์„œ๋“œ ์„ค๋ช…:
    Response execute() throws IOException;: ์ด ๋ฉ”์„œ๋“œ๋Š” HTTP ์š”์ฒญ์„ ๋™๊ธฐ์ ์œผ๋กœ ๋ณด๋‚ด๊ณ , ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ์˜ ์‘๋‹ต์„ ๋ฐ›์•„์˜ด.
    execute ๋ฉ”์„œ๋“œ๋Š” IOException์„ ๋˜์งˆ ์ˆ˜ ์žˆ์Œ. ์ด๋Š” ์„œ๋ฒ„์™€์˜ ํ†ต์‹  ์ค‘์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ์„ ๋‚˜ํƒ€๋ƒ„.
    RuntimeException๊ณผ ๊ทธ ์„œ๋ธŒํด๋ž˜์Šค๋“ค์„ ๋˜์งˆ ์ˆ˜๋„ ์žˆ์Œ. ์ด๋Š” ์š”์ฒญ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์‘๋‹ต์„ ๋””์ฝ”๋”ฉํ•˜๋Š” ๋™์•ˆ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ.

  3. ์š”์•ฝ:
    Call ์ธํ„ฐํŽ˜์ด์Šค๋Š” Retrofit์—์„œ HTTP ์š”์ฒญ์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ, execute ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ์š”์ฒญ์„ ๋™๊ธฐ์ ์œผ๋กœ ๋ณด๋‚ด๊ณ  ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ์˜ ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•จ . ์ด ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋“ค์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์Œ.

commit --amend

1. ๊ฐ€์žฅ ์ตœ๊ทผ์˜ commit ์ˆ˜์ •

push ์ „์˜ commit

git commit --amend

์œ„์™€ ๊ฐ™์ด amend ๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— commit ํ•œ ๋‚ด์šฉ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Œ.

git commit --amend ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ปค๋ฐ‹์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์ฐฝ์ด ๋œจ๋ฉด, ์ˆ˜์ •์„ ์™„๋ฃŒํ•œ ํ›„ esc -> :wq


2. ๋” ์˜ค๋ž˜๋œ commit ์ˆ˜์ • or ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ commit ์ˆ˜์ •

๋งŒ์ผ ์œ„์—์„œ๋ถ€ํ„ฐ ์„ธ ๋ฒˆ์งธ ์ปค๋ฐ‹์„ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค๋ฉด

git rebase -i HEAD~3

์ˆ˜์ •ํ•˜๊ณ  ์‹ถ์€ ์ปค๋ฐ‹ ์˜†์˜ pick ์ด๋ผ๋Š” ๋ฌธ๊ตฌ > reword ๋กœ ๋ฐ”๊ฟ”์คŒ.
ex) ๋‘ ๋ฒˆ์งธ ์ปค๋ฐ‹๊ณผ ์„ธ ๋ฒˆ์งธ ์ปค๋ฐ‹์„ ์ˆ˜์ •

pick e499d89 Delete CNAME
reword 0c39034 Better README
reword f7fde4a Change the commit message but push the same commit.

esc -> :wq ๋ฅผ ํ†ตํ•ด ์ปค๋ฐ‹ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ €์žฅ์„ ํ•ด์ฃผ๊ณ  ๋‚˜๋ฉด, ๋‘ ๊ฐœ์˜ ์ปค๋ฐ‹์„ ๊ฐ๊ฐ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์ฐฝ์ด ์ˆœ์„œ๋Œ€๋กœ ๋„์›Œ์ง.
์ปค๋ฐ‹ ์ˆ˜์ • ํ›„, :wq ๋ฅผ ํ†ตํ•ด ์ €์žฅ.


3. ์ด๋ฏธ ์ปค๋ฐ‹์„ push ํ•ด remote ์— ์˜ฌ๋ฆฐ ์ƒํ™ฉ์ผ ๋•Œ

์ปค๋ฐ‹์ด ์ด๋ฏธ remote ์— ์ ์šฉ๋œ ์ƒํ™ฉ์ด๋ผ๋ฉด, force ๋ฅผ ํ†ตํ•ด ์ˆ˜์ •๋œ ์ปค๋ฐ‹์„ ๊ฐ•์ œ๋กœ push

git push --force ๋ธŒ๋žœ์น˜

mutableStateOf vs mutableStateFlow

๐Ÿ’ก Concept :

๊ฐ€๋ณ€์˜ state ๊ฐ’์„ ๊ด€์ฐฐํ•ด์„œ ๊ฐ’์˜ ๋ณ€ํ™”์— ๋”ฐ๋ผ ๋”ฐ๋ผ UI ๋‚˜ ๋‹ค๋ฅธ ๋กœ์ง์— ๋Œ€ํ•œ ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Œ


mutableStateOf

androidx.compose.runtime
Compose UI๋ฅผ ์œ„ํ•œ API UI์— ํŠนํ™”๋˜์–ด ์žˆ์Œ

๊ด€์ฐฐ๋  ์ˆ˜ ์žˆ๋Š” MutableState๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๋Š”๋ฐ, ์ด ํƒ€์ž…์œผ๋กœ ๋œ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ปดํฌ์ €๋ธ”์ด ๋‹ค์‹œ recompose ๋˜๋„๋ก ํ•จ. ์ฆ‰, ๊ฐ’์— ๋”ฐ๋ผ์„œ Composable UI์˜ ๋ณ€๊ฒฝ์„ ํ•ด์ฃผ์–ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๊ธฐ ์ ํ•ฉํ•จ.

mutableStateFlow

private val _state = MutableStateFlow(MyProfileUIState())
    val state: StateFlow<MyProfileUIState> = _state.asStateFlow()

// StateFlow๋Š” MutableStateFlow์˜ read-only ํƒ€์ž…์˜ ๋ถ€๋ชจ, state flow ๊ฐ’์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด mutableStateFlow

Kotlin์˜ Coroutine Flow API

MutableStateFlow : ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” Flow. ์—ฌ๊ธฐ์„œ๋Š” MyProfileUIState ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๊ฐ–๊ณ  ์žˆ์Œ.

StateFlow: ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” Flow.

MutableStateFlow์—์„œ ์ƒ์„ฑ๋œ StateFlow๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด asStateFlow() ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•จ. ์•„๋ž˜๋Š” asStateFlow() ์˜ ๋Œ€ํ•œ ์„ค๋ช….

Represents this mutable state flow as a read-only state flow

StateFlow๋Š” ์—…๋ฐ์ดํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ๊ฐ’์„ ๊ฐ€์ง€๋Š” State๋ผ๋Š” ๊ฒƒ์„ ๊ฐ€์ง€๊ณ , collector์—๊ฒŒ emit(์ „ํŒŒ)ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ž„. State๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด emit์„ ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•˜๋Š” hot stream. StateFlow๋Š” Complete๋ผ๋Š” ๊ฐœ๋…์ด ์—†์ด ๊ณ„์†ํ•ด์„œ ๊ด€์ฐฐ๋  ์ˆ˜ ์žˆ์Œ

_state๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” MutableStateFlow.
_state๋ฅผ ์™ธ๋ถ€์—์„œ ์ง์ ‘ ์ ‘๊ทผํ•˜์ง€ ์•Š๊ณ , ์™ธ๋ถ€์—๋Š” state๋กœ ๋…ธ์ถœ๋จ.

enum class values()์˜ name

enum class values()์˜ name์ด๋ž€

enum class A {
  JOB_POSTING("jobPosting"), JOB_APPLICATION("jobApplication")
}

์ด๋ ‡๊ฒŒ ์žˆ์œผ๋ฉด, name์€ exactly as declared in its enum declaration์ธ JOB_POSTING์„ ๋งํ•˜๋Š” ๊ฒƒ.

 val firstNavigationTab = MyProfileTab.values()
            .find { it.name == firstTabName }
            ?: MyProfileTab.PROFILE

name์— ๋Œ€ํ•œ ์„ค๋ช…

 /**
     * Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant
     * is assigned an ordinal of zero).
     */

override ํ•œ dialog super.onDismiss ์ˆœ์„œ

super.onDismiss(dialog) ์˜ ์œ„์น˜ ์ˆœ์„œ

super.onDismiss(dialog)์˜ ํ˜ธ์ถœ ์œ„์น˜๋Š” ์ค‘์š”. ์ด ํ˜ธ์ถœ์€ ๋ถ€๋ชจ ํด๋ž˜์Šค์ธ DialogFragment์˜ onDismiss ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ. ๋”ฐ๋ผ์„œ ์ด ํ˜ธ์ถœ์„ ์–ด๋””์— ์œ„์น˜์‹œํ‚ค๋Š๋ƒ์— ๋”ฐ๋ผ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ๊ธฐ๋ณธ ๋™์ž‘์ด ์–ธ์ œ ์ˆ˜ํ–‰๋˜๋Š”์ง€์— ์˜ํ–ฅ์„ ๋ฏธ์นจ.

๋ณดํ†ต์€ super.onDismiss(dialog)๋ฅผ ๋ฉ”์„œ๋“œ์˜ ๋งˆ์ง€๋ง‰ ๋ถ€๋ถ„์— ๋‘๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ . ๋งŒ์•ฝ super.onDismiss(dialog)๋ฅผ ํŠน์ • ๋™์ž‘ ์ด์ „์— ํ˜ธ์ถœํ•œ๋‹ค๋ฉด ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์‚ฌ๋ผ์ง€๊ธฐ ์ „์— ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ๋™์ž‘์ด ์ˆ˜ํ–‰๋˜์–ด ์›ํ•˜๋Š” ํ–‰๋™๋Œ€๋กœ ๋˜์ง€ ์•Š์Œ.

override fun onDismiss(dialog: DialogInterface) {
    // ํŠน์ • ๋™์ž‘ ์ˆ˜ํ–‰
        viewModel.sendDismissLogger(
            when (dismissType) {
                DismissType.CLOSE -> LogConstant.CLOSE
                DismissType.DIM -> LogConstant.DIM_CLICK
            }
        )

    // ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ๋™์ž‘ ํ˜ธ์ถœ
    super.onDismiss(dialog)
}

setColorFilter

 binding.img.setColorFilter(R.color.secondary100)

์ด ์ ์šฉ์ด ์•ˆ๋˜๋Š” ์ด์œ .

setColorFilter ๋ฉ”์„œ๋“œ์˜ ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ƒ‰์ƒ์„ ์„ค์ •ํ•  , ContextCompat.getColor๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์†Œ์Šค ID ๋Œ€์‹  ์‹ค์ œ ์ƒ‰์ƒ ๊ฐ’์„ ์‚ฌ์šฉํ•ด์•ผ ํ•จ.
ํ˜„์žฌ ์ฝ”๋“œ์—์„œ๋Š” ๋ฆฌ์†Œ์Šค ID๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ ์ ˆํ•œ ์ƒ‰์ƒ์ด ์ „๋‹ฌ๋˜์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ ์ƒ‰์ƒ์ด ๋‚˜์˜ด.

 binding.img.setColorFilter(ContextCompat.getColor(requireContext(), R.color.secondary))

kapt

kapt

Kotlin Annotation Processing Tool. Kotlin ์ฝ”๋“œ์—์„œ ์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์‹ฑ์„ ์œ„ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ.

์ž๋ฐ”์—์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ annotationProcessor๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์‹ฑ์„ ์„ค์ •ํ•˜๋Š” ๋ฐ˜๋ฉด, Kotlin์—์„œ๋Š” kapt๋ฅผ ์‚ฌ์šฉํ•จ. Kotlin ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋จ.

image


.
Hilt, Room์€ ์ž๋ฐ”์™€ ์–ด๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋œ library์ด๊ธฐ ๋•Œ๋ฌธ์—
์ฝ”ํ‹€๋ฆฐ์—์„œ ํžํŠธ ์–ด๋…ธํ…Œ์ด์…˜ ์ฐธ์กฐํ•ด์„œ ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์ž๋ฐ”์™€ ์ƒํ˜ธ์šด์šฉ์„ฑ์„ ์œ„ํ•ด kapt ๊ฐ€ ์ง์ ‘ ์ปดํŒŒ์ผํƒ€์ž„์— stub์ด๋ผ๋Š” ํŒŒ์ผ์„ ์ž‘์„ฑํ•ด์คŒ. ์ด๋Ÿฐ ๊ฒƒ ๋•Œ๋ฌธ์— ๋นŒ๋“œํ•  ๋•Œ ์ปดํŒŒ์ผ์‹œ๊ฐ„์ด ๋Š˜์–ด๋‚˜๊ฒŒ ๋˜์ง€๋งŒ ์ž๋ฐ” ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ kapt ๊ฐ€ ์ƒ์„ฑํ•ด์ค€ stub ์ž๋ฐ” ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”ํ‹€๋ฆฐ์œผ๋กœ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋„ ํ•ด์„ํ•  ์ˆ˜ ์žˆ์Œ. ๊ฒฐ๊ตญ ํžํŠธ ์–ด๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์‹ฑ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋จ.

๋”ฐ๋ผ์„œ Hilt, Room๊ณผ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” Kotlin ํ”„๋กœ์ ํŠธ์—์„œ kapt ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์‹ฑ์„ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•จ.

setMargin programatically

setMargin programatically

binding.txtTitle.updateLayoutParams<ViewGroup.MarginLayoutParams> {
    setMargins(0, 0, 0, resources.getDimensionPixelSize(R.dimen.margin_24))
 }

์ •์  ํƒ€์ž… ์–ธ์–ด, ๋™์  ํƒ€์ž… ์–ธ์–ด

  • ์ •์  ํƒ€์ž… ์–ธ์–ด : ๋ณ€์ˆ˜์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ์ปดํŒŒ์ผ ์‹œ์— ๊ฒฐ์ •. ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•  ๋•Œ ํ•ด๋‹น ๋ณ€์ˆ˜์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •. ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ณ€์ˆ˜์˜ ํƒ€์ž…์„ ํ™•์ธํ•˜๊ณ , ์ž˜๋ชป๋œ ํƒ€์ž…์˜ ๊ฐ’์„ ํ• ๋‹นํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ด

  • ๋™์  ํƒ€์ž… ์–ธ์–ด : ๋ณ€์ˆ˜์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ์‹คํ–‰ ์‹œ๊ฐ„(runtime)์— ๊ฒฐ์ •๋˜๋Š” ์–ธ์–ด. ๋‹ค์‹œ ๋งํ•ด, ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•  ๋•Œ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ , ๋ณ€์ˆ˜์— ๊ฐ’์„ ํ• ๋‹นํ•  ๋•Œ ํ•ด๋‹น ๊ฐ’์˜ ํƒ€์ž…์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ํƒ€์ž…์ด ๊ฒฐ์ •๋จ. ํ”„๋กœ๊ทธ๋žจ์„ ์ž‘์„ฑํ•  ๋•Œ ๋” ์œ ์—ฐํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ์ง€๋งŒ, ์‹คํ–‰ ์ค‘์— ํƒ€์ž… ๊ด€๋ จ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์„ ๋‚ดํฌํ•˜๊ณ  ์žˆ์Œ.

android:visibility, tools:visibility

android:visibility์™€ tools:visibility

์‹ค์ œ ๋Ÿฐํƒ€์ž„ ๋™์ž‘ :
android:visibility : ์ด ์†์„ฑ์€ ๋Ÿฐํƒ€์ž„ ๋™์ž‘์— ์˜ํ–ฅ์„ ์คŒ. ํ•ด๋‹น ๋ทฐ์˜ ์‹ค์ œ ๊ฐ€์‹œ์„ฑ์„ ์ œ์–ด.
tools:visibility : ์ด ์†์„ฑ์€ ๋ ˆ์ด์•„์›ƒ ํŽธ์ง‘๊ธฐ์—์„œ๋งŒ ์‚ฌ์šฉ๋จ. ๋””์ž์ธ ํƒ€์ž„์—๋งŒ ํ•ด๋‹น ๋ทฐ์˜ ๊ฐ€์‹œ์„ฑ์„ ์ œ์–ด.

ifEmpty, orEmpty

ifEmpty, orEmpty

  • ifEmpty : collection์ด ๋น„์–ด์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋น„์–ด์žˆ์œผ๋ฉด ๋””ํดํŠธ ๊ฐ’์„ ๋„˜๊ฒจ์คŒ
val list = listOf<Int>()
val result = list.ifEmpty { listOf(1,2,3) }
println(result) // [1,2,3,]
  • orEmpty
    • null์ด๋‚˜ emptyList๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ : list ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜
    • null์ผ ๊ฒฝ์šฐ : emptyList()
    • emptyList์ผ ๊ฒฝ์šฐ : emptyList()

onBackPressed() deprecated onBckPressedCallback

onBackPressed

์•กํ‹ฐ๋น„ํ‹ฐ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋’ค๋กœ ๊ฐ€๊ธฐ ํ‚ค๋ฅผ ๋ˆŒ๋ €์„๋•Œ ์ด๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ.
API ๋ ˆ๋ฒจ 33๋ถ€ํ„ฐ ๋”์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์•„ deprecated ๋จ. ๋Œ€์‹  OnBackInvokedCallback ๋˜๋Š” androidx.activity.OnBackPressedCallback ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ๊ถŒ๊ณ .

FragmentActivity์™€ AppCompatActivity์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค์ธ ComponentActivity๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด OnBackPressedDispatcher(getOnBackPressedDispatcher()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฐ€์ ธ์˜ด)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋’ค๋กœ ๋ฒ„ํŠผ์˜ ๋™์ž‘์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Œ.

 callback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                isEnabled = true
            }
        }

requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)

versionNameSuffix, applicationIdSuffix

versionNameSuffix, applicationIdSuffix

versionNameSuffix : ๋ฒ„์ „๋ช…์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ์‹๋ณ„์ž๋ฅผ ์ œ๊ณต
applicationIdSuffix : ํŒจํ‚ค์ง€ ์‹๋ณ„์ž์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ์‹๋ณ„์ž๋ฅผ ์ œ๊ณต

cp ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์žˆ๋Š” ํŒŒ์ผ์„ ์›ํ•˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋ณต์‚ฌ ๋ถ™์—ฌ๋„ฃ๊ธฐ

cp

: ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์žˆ๋Š” ํŒŒ์ผ์„ ์›ํ•˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋ณต์‚ฌ ๋ถ™์—ฌ๋„ฃ๊ธฐ ํ•˜๊ธฐ

cp [๋ณต์‚ฌํ•  ํŒŒ์ผ] [๋ถ™์—ฌ๋„ฃ๊ธฐํ•  ๋””๋ ‰ํ„ฐ๋ฆฌ]

$ cp file1 dir1

cp ~/Downloads/release_key.jks ~/.android.keystore

singleTop, clearTop

FLAG_ACTIVITY_SINGLE_TOP

์•กํ‹ฐ๋น„ํ‹ฐ ์Šคํƒ์—์„œ ์ตœ์ƒ๋‹จ์— ์žˆ๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ์™€ ์ „ํ™˜ ๋  ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ๊ฐ™์„ ๋•Œ, ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค(์ธํ…ํŠธ)๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ , ์Šคํƒ์— ์žˆ๋˜ ์•กํ‹ฐ๋น„ํ‹ฐ์˜ ์ธํ…ํŠธ๋ฅผ ํ†ตํ•ด ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธ.

์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, onCreate() ์ฝœ๋ฐฑ๋ถ€ํ„ฐ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ onNewIntent() ์ฝœ๋ฐฑ์„ ํ†ตํ•ด, ๊ธฐ์กด ์•กํ‹ฐ๋น„ํ‹ฐ์˜ intent ์ •๋ณด๋ฅผ ๋ฐ›์•„ onResume() ์ฝœ๋ฐฑ์œผ๋กœ ํ˜ธ์ถœ๋จ

onNewIntent() -> onResume()

ํ˜„์žฌ ๋ˆˆ์— ๋ณด์ด๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๋Š” ์ด๋ฏธ ์•กํ‹ฐ๋น„ํ‹ฐ ์Šคํƒ์— ์ ์žฌ๋œ ์ƒํƒœ์ด๋ฉด์„œ, ๊ฐ€์žฅ ์ตœ์ƒ๋‹จ์— ์žˆ๋Š” ์ƒํƒœ(๋ˆˆ์— ๋ณด์ด๋Š” ์ƒํƒœ)์ž„


FLAG_ACTIVITY_CLEAR_TOP

ํ˜„์žฌ ๋ถˆ๋Ÿฌ์˜ฌ ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ B์ธ๋ฐ, ์ด๋ฏธ ์Šคํƒ์— ์กด์žฌํ•œ๋‹ค๋ฉด B๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ์•กํ‹ฐ๋น„ํ‹ฐ๋“ค์„ popํ•ด๋ฒ„๋ฆฌ๊ณ  ์ƒˆ๋กœ ํ˜ธ์ถœํ•œ B ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์Šคํƒ ์ตœ์ƒ๋‹จ์œผ๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธ

์ƒˆ๋กญ๊ฒŒ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒˆ intent๋ฅผ ๊ฐ–๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์ƒ์„ฑํ•˜๊ฒŒ ๋จ. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋ผ์ดํ”„ ์‚ฌ์ดํด๋„ onCreate() -> onResume() ๋กœ ํ˜ธ์ถœ์ด ๋จ. ๋ฐ˜๋Œ€๋กœ ํ˜ธ์ถœํ•˜๋ ค๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์Šคํƒ์— ์—†์œผ๋ฉด ๊ธฐ๋ณธ์ ์ธ intent ๋™์ž‘์„ ํ•˜๊ฒŒ๋จ

onCreate() -> onResume()

Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP

๋‘˜์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด, ์‹œ์ž‘ํ•˜๋ ค๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์ด๋ฏธ ์Šคํƒ์— ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ๊ธฐ์กด ์ธ์Šคํ„ด์Šค๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ฉด์„œ, ํ•„์š”์— ๋”ฐ๋ผ ํ•ด๋‹น ์•กํ‹ฐ๋น„ํ‹ฐ ์œ„์— ์žˆ๋Š” ๋‹ค๋ฅธ ์•กํ‹ฐ๋น„ํ‹ฐ๋“ค์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Œ.

์‹œ์ž‘ํ•˜๋ ค๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์Šคํƒ ์ตœ์ƒ๋‹จ์— ์žˆ๋Š” ๊ฒฝ์šฐ: FLAG_ACTIVITY_SINGLE_TOP์˜ ํšจ๊ณผ๋กœ ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š๊ณ  ๊ธฐ์กด ์ธ์Šคํ„ด์Šค๊ฐ€ ์žฌ์‚ฌ์šฉ๋จ. FLAG_ACTIVITY_CLEAR_TOP์€ ์•„๋ฌด๋Ÿฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Œ.

์‹œ์ž‘ํ•˜๋ ค๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์Šคํƒ ์ค‘๊ฐ„์— ์žˆ๋Š” ๊ฒฝ์šฐ: FLAG_ACTIVITY_CLEAR_TOP์˜ ํšจ๊ณผ๋กœ ํ•ด๋‹น ์•กํ‹ฐ๋น„ํ‹ฐ ์œ„์— ์žˆ๋Š” ๋ชจ๋“  ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์ œ๊ฑฐ๋จ. ๊ทธ๋ฆฌ๊ณ  FLAG_ACTIVITY_SINGLE_TOP์˜ ํšจ๊ณผ๋กœ ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š๊ณ  ๊ธฐ์กด ์ธ์Šคํ„ด์Šค๊ฐ€ ์žฌ์‚ฌ์šฉ๋จ.

๋ฉ”์ธ(A) -> ํŽธ์ง‘(B) -> ์ธ์ฆ์ž…๋ ฅ(C) -> ์ธ์ฆ์™„๋ฃŒ(D) -> ํŽธ์ง‘(B)์œผ๋กœ ์ด๋™ํ•˜๋Š” ๊ฒฝ์šฐ, 
FLAG_ACTIVITY_SINGLE_TOP๊ณผ FLAG_ACTIVITY_CLEAR_TOP์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ์กด ํŽธ์ง‘(B) ์œ„์— ์žˆ๋Š” C์™€ D์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์ œ๊ฑฐ๋˜๊ณ  ๊ธฐ์กด ํŽธ์ง‘(B)์ด ์ตœ์ƒ๋‹จ์œผ๋กœ ์˜ค๊ฒŒ๋จ

๋ฉ”์ธ(A) -> ๊ธฐ์กด ํŽธ์ง‘(B)
๋•Œ๋ฌธ์— ๊ธฐ์กด ํŽธ์ง‘(B)์—์„œ ๋’ค๋กœ๊ฐ€๊ธฐ๋ฅผ ๋ˆŒ๋ €์„์‹œ, ๋ฉ”์ธ(A) ํ™”๋ฉด ๋…ธ์ถœ

๋ฉ”์ธ(A) -> ์ธ์ฆ์ž…๋ ฅ(B) -> ์ธ์ฆ์™„๋ฃŒ(C) -> ํŽธ์ง‘(D)์œผ๋กœ ์ด๋™ํ•  ๋•Œ, ํŽธ์ง‘(D)์—์„œ ๋’ค๋กœ๊ฐ€๊ธฐ๋ฅผ ๋ˆŒ๋ €์„์‹œ ๋ฉ”์ธ(A)์œผ๋กœ ๋–จ์–ด์งˆ ๊ฒƒ์„ ๊ธฐ๋Œ€ํ•  ๋•Œ

์ธ์ฆ์ž…๋ ฅ(B), ์ธ์ฆ์™„๋ฃŒ(C) ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ Manifest์˜ taskAffinity ์— ์ง€์ •ํ•˜์—ฌ C์—์„œ D๋กœ intent ์ด๋™ํ• ๋•Œ finishAffinity() ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ affinity๋‹จ์œ„๋กœ ์Šคํƒ์—์„œ ์ œ๊ฑฐํ•จ

AppCompatimageView

androidx.appcompat.widget.AppCompatimageView

Android Jetpack ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ AppCompatImageView ํด๋ž˜์Šค.
์ด ํด๋ž˜์Šค๋Š” Android์˜ ๊ธฐ๋ณธ ImageView ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜์—ฌ ํ•˜์œ„ ํ˜ธํ™˜์„ฑ์„ ์ œ๊ณตํ•˜๊ณ  AppCompat ์ง€์› ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ์ œ๊ณต๋˜๋Š” ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ํฌํ•จ.

AppCompatImageView์™€ ๊ธฐ๋ณธ ImageView ๋น„๊ต

ํŠน์ง• ImageView AppCompatImageView
๋ฐฑ์›Œ๋“œ ํ˜ธํ™˜์„ฑ ์ตœ์‹  Android ๋ฒ„์ „์˜ ๊ธฐ๋Šฅ์„ ๋ชจ๋“  ๊ธฐ๊ธฐ์—์„œ ๋™์ผํ•˜๊ฒŒ ์ œ๊ณตํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ ๋” ์˜ค๋ž˜๋œ Android ๋ฒ„์ „์—์„œ๋„ ์ตœ์‹  ๊ธฐ๋Šฅ์„ ์ง€์›
๋ฒกํ„ฐ ๋“œ๋กœ์–ด๋ธ” ์ง€์› API ๋ ˆ๋ฒจ 21 ์ด์ƒ์—์„œ๋งŒ ๋ฒกํ„ฐ ๋“œ๋กœ์–ด๋ธ” ์ง€์› ๋ชจ๋“  API ๋ ˆ๋ฒจ์—์„œ ๋ฒกํ„ฐ ๋“œ๋กœ์–ด๋ธ”์„ ์ง€์› (app:srcCompat)
ํ…Œ๋งˆ ์ง€์› ์‹œ์Šคํ…œ ํ…Œ๋งˆ๋ฅผ ์ ์šฉ AppCompat ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ํ…Œ๋งˆ๋ฅผ ์ง€์›

gradle๊ณผ gradle.kts ์ฐจ์ด

gradle๊ณผ gradle.kts ์ฐจ์ด

module ๋งŒ๋“ค ๋•Œ build configuration language์—์„œ Groovy DSL(build.gradle)๊ณผ Kotlin DSL(build.gradle.kts)์„ ํƒํ•  ์ˆ˜ ์žˆ์Œ

ํ™•์žฅ์ž:
gradle: ๊ธฐ๋ณธ์ ์œผ๋กœ Groovy ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉ.
gradle.kts: Kotlin DSL์„ ์‚ฌ์šฉ.

๋ฌธ๋ฒ•:
gradle: Groovy ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉ.
gradle.kts: Kotlin ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉ.

ํƒ€์ž… ์•ˆ์ •์„ฑ:
gradle: Groovy๋Š” ๋™์  ํƒ€์ž… ์–ธ์–ด์ด๋ฏ€๋กœ ํƒ€์ž… ์•ˆ์ •์„ฑ์ด ์ƒ๋Œ€์ ์œผ๋กœ ๋‚ฎ์Œ.
gradle.kts: Kotlin์€ ์ •์  ํƒ€์ž… ์–ธ์–ด๋กœ์„œ ๋†’์€ ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ์ œ๊ณต.

  • ์ •์  ํƒ€์ž… ์–ธ์–ด : ๋ณ€์ˆ˜์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ์ปดํŒŒ์ผ ์‹œ์— ๊ฒฐ์ •. ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•  ๋•Œ ํ•ด๋‹น ๋ณ€์ˆ˜์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •. ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ณ€์ˆ˜์˜ ํƒ€์ž…์„ ํ™•์ธํ•˜๊ณ , ์ž˜๋ชป๋œ ํƒ€์ž…์˜ ๊ฐ’์„ ํ• ๋‹นํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ด

  • ๋™์  ํƒ€์ž… ์–ธ์–ด : ๋ณ€์ˆ˜์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ์‹คํ–‰ ์‹œ๊ฐ„(runtime)์— ๊ฒฐ์ •๋˜๋Š” ์–ธ์–ด. ๋‹ค์‹œ ๋งํ•ด, ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•  ๋•Œ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ , ๋ณ€์ˆ˜์— ๊ฐ’์„ ํ• ๋‹นํ•  ๋•Œ ํ•ด๋‹น ๊ฐ’์˜ ํƒ€์ž…์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ํƒ€์ž…์ด ๊ฒฐ์ •๋จ. ํ”„๋กœ๊ทธ๋žจ์„ ์ž‘์„ฑํ•  ๋•Œ ๋” ์œ ์—ฐํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ์ง€๋งŒ, ์‹คํ–‰ ์ค‘์— ํƒ€์ž… ๊ด€๋ จ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์„ ๋‚ดํฌํ•˜๊ณ  ์žˆ์Œ.

IDE ์ง€์›:
gradle: Groovy๋Š” IntelliJ IDEA, Eclipse ๋“ฑ ๋‹ค์–‘ํ•œ IDE์—์„œ ์ง€์›๋˜์ง€๋งŒ Kotlin DSL์— ๋น„ํ•ด ํ†ตํ•ฉ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ๋œ ๊ฐ•๋ ฅํ•œ ์ง€์›์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ.
gradle.kts: Kotlin์€ IntelliJ IDEA์™€ ๊ฐ™์€ IDE์—์„œ ๊ฐ•๋ ฅํ•œ ์ง€์›์„ ๋ฐ›์Œ. Kotlin DSL์€ ํƒ€์ž… ์•ˆ์ •์„ฑ์ด ๋†’์Œ.

์ฝ”๋“œ ์žฌ์‚ฌ์šฉ:
gradle: Groovy์˜ ๋™์  ํŠน์„ฑ์œผ๋กœ ์ธํ•ด ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์ด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ.
gradle.kts: Kotlin์˜ ์ •์  ํƒ€์ž… ํŠน์„ฑ์œผ๋กœ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์ด ๋” ์‰ฌ์šธ ์ˆ˜ ์žˆ์Œ.

์‚ฌ์‹ค์ƒ Groovy์™€ Kotlin ์–ธ์–ด์ ์ธ ํŠน์ง•์˜ ์ฐจ์ด๋ฟ์ธ๊ฐ€? ์ฐจ์ด๊ฐ€ ๋‚ ๋งŒํ•œ ์ฐจ์ด๋Š” ์—†๋Š” ๋“ฏ.

ConstraintLayout ๋ช‡๊ฐ€์ง€

<ConstraintLayout>
    <ImagView..
        id= img_dot
     //>
    
    <TextView..
        id = txt_title
        app:layout_constraintStart_toEndOf="@+id/img_dot"
      //>
</Constraint...>

constratinStart_toEndOf ๋งŒ ์ฒด์ด๋‹ํ•˜๋ฉด ๋’ค์— text๊ฐ€ ๊ธธ์–ด์ง€๋ฉด ์ž˜๋ฆด ์ˆ˜ ์žˆ์Œ.
constraintEnd_toEndOf="parent" ๊นŒ์ง€ ํ•ด์ค˜์•ผํ•จ.

fragment newInstance constant ์œ„์น˜

์ผ๋ฐ˜์ ์œผ๋กœ newInstance ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Fragment๋ฅผ ์ƒ์„ฑํ•  ๋•Œ Fragment ๋‚ด์—์„œ ์ƒ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์€ ๋ฐฉ๋ฒ•.

  1. Fragment๊ฐ€ ์ž์ฒด์ ์œผ๋กœ ๋” ๋…๋ฆฝ์ , ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’์•„์ง€๊ธฐ ๋•Œ๋ฌธ.
  2. ์—ฌ๋Ÿฌ Fragment ์ธ์Šคํ„ด์Šค์—์„œ ๋™์ผํ•œ ์ƒ์ˆ˜๋ฅผ ๊ณต์œ ํ•˜๋ ค๋ฉด Fragment ๋‚ด์—์„œ ํ•ด๋‹น ์ƒ์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์œ ์ง€๋ณด์ˆ˜ ์ธก๋ฉด์—์„œ ํšจ์œจ์ .
  3. ์ƒ์ˆ˜ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ Fragment ๋‚ด๋ถ€์— ์žˆ์œผ๋ฉด ํ•ด๋‹น Fragment๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋จ์œผ๋กœ ์ˆ˜์ • ๋ฒ”์œ„ ์ตœ์†Œํ™”.

Not possible to fast-forward, aborting.

pull ํ–ˆ์„ ์‹œ Not possible to fast-forward, aborting.

Fatal: Not possible to fast-forward, aborting.
fatal: ์ •๋ฐฉํ–ฅ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ, ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค.

pullํ•˜๋ ค๋Š” ์›๊ฒฉ์ €์žฅ์†Œ์˜ ๋ธŒ๋žœ์น˜์™€ ๋กœ์ปฌ์ €์žฅ์†Œ์˜ ๋ธŒ๋žœ์น˜๊ฐ€ fast-forward ๊ด€๊ณ„๊ฐ€ ์•„๋‹ ๋•Œ์— ๋ฐœ์ƒ.
์ฒ˜์Œ์— pull๋ฐ›์•„์„œ ์ž‘์—…ํ•˜๋”๋ผ๋„ ํŒ€์›์ด remote์— ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ƒํ™ฉ๋„ ์žˆ์Œ.
์›๊ฒฉ์ €์žฅ์†Œ์˜ ์ƒˆ๋กœ์šด commit์ด ์กด์žฌํ•˜๋Š”๋ฐ git pull์„ ํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ๋กœ์ปฌ์ €์žฅ์†Œ์— ์ƒˆ๋กœ์šด commit์„ ํ–ˆ๋‹ค๋ฉด ํ•ด๋‹น ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•จ.
git pull์„ ์Šต๊ด€ํ™”.

git pull --rebase

์ดํ›„ conflict ํ•ด๊ฒฐ ํ›„ git add, git commit, git rebase --continue, push

git merge/rebase command

merge ํ•  ๋•Œ

git add .
git commit

rebase ํ•  ๋•Œ

git add .
git rebase --continue
git push -f

git merge ์ทจ์†Œ

git merge --abort

git rebase ์ทจ์†Œ

git rebase --abort

repeatOnLifeCycle

flow๋Š” ์Šค์Šค๋กœ ๋ผ์ดํ”„ ์‚ฌ์ดํด์„ ์•Œ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— CoroutineScope ์ƒ๋ช…์ฃผ๊ธฐ์— ๋”ฐ๋ผ ๋งž์ถ”์–ด ์‚ฌ์šฉํ•˜๋ฉฐ ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ๋‚˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋‚ญ๋น„๋˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•จ.


lifecycleScope.launch

private val _stateFlow = MutableStateFlow(0)
val stateFlow get() = _stateFlow

private fun count() {
	viewModelScope.launch(Dispatchers.IO) {
    	repeat(10) {
        	_stateFlow.value = it
                delay(3000)
		 }
	 }
 }
    
    
 lifecycleScope.launch {
 	viewModel.stateFlow.collect {
    		Log.d("TAG", "number : $it")
        }
 }

flow๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ collect ํ•˜๋Š” job์ด ์กด์žฌํ•จ. lifecycleScope ์•ˆ์—์„œ flow์— ๋Œ€ํ•œ collect์ด ํ˜ธ์ถœ๋จ.
count() ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ๊ฐ€๊ฑฐ๋‚˜ ์•ฑ ์ข…๋ฃŒ์‹œ.

์‹œ์ 
number : 1
number : 2
number : 3
-- onStop --
number : 4
number : 5
number : 6

activity๊ฐ€ finish() ๋˜์–ด onDestroy๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘์ด ์ค‘๋‹จ. onStop์ด ๋œ ๊ฒฝ์šฐ์—๋„ ์—ฌ์ „ํžˆ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•œ collect์ด ์ผ์–ด๋‚จ. ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ์„ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ์—์„œ collect์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์€ ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ๋‚˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋‚ญ๋น„๋  ์ˆ˜ ์žˆ์Œ

private var job : Job? = null

job = lifecycleScope.launch {
            viewModel.stateFlow().collect {
                Log.d("MainActivity","number : $it")
            }
        }



override fun onStop() {
	super.onStop()
	job?.cancel()
}

๊ทธ๋ž˜์„œ onStop์—์„œ job์„ cancelํ•ด์ฃผ์–ด์•ผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ˆ˜์ง‘๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Œ. ์žŠ๊ณ  cancel์„ ํ•ด์ฃผ์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์ฆ๊ฐ€ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Œ. ์ด๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” lifecycleScope.launchWhenStarted์™€ repeatOnLifecycle์ด ์žˆ์Œ


launchWhenStarted

lifecycleSceop.launchWhenCreated, Started, Resumed scope๊ฐ€ ์žˆ๊ณ  ํ•ด๋‹น ์Šค์ฝ”ํ”„๋Š” when ์ดํ›„ ์ ‘๋ฏธ์–ด์— ํ•ด๋‹นํ•˜๋Š” ์ƒ๋ช…์ฃผ๊ธฐ์— ๋งž์ถฐ ์‹คํ–‰์ด ๋˜๊ณ  ์ƒ๋ช…์ฃผ๊ธฐ์˜ ์ƒํƒœ๊ฐ€ ์ถฉ์กฑ์ด ๋˜์ง€ ์•Š์œผ๋ฉด ์ •์ง€๊ฐ€ ๋˜๋Š” ํ•จ์ˆ˜

lifecycleScope.launchWhenStarted {
	viewModel.stateFlow.collect {
    	Log.d("TAG,"number : $it")
    }
}
์‹œ์ 
-- onStart --
number : 0
number : 1
number : 2
-- onStop --
-- onStart --
number : 3
number : 4

ํ™ˆ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ onStop() ์ƒํƒœ๊ฐ€ ๋˜๋ฉด ์ƒ๋ช…์ฃผ๊ธฐ์˜ ์ƒํƒœ๋ฅผ ์ถฉ์กฑ์‹œํ‚ค์ง€ ๋ชปํ•ด ์ฝ”๋ฃจํ‹ด์ด suspend ์ƒํƒœ๊ฐ€ ๋จ. ๊ทธ ํ›„ ์•ฑ์„ ๋‹ค์‹œ ์ง„์ž…ํ•˜๋ฉด ์กฐ๊ฑด์„ ๋‹ค์‹œ ์ถฉ์กฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘์„ ์žฌ๊ฐœํ•จ. ์ฆ‰ lifecycle์ด destroyed ๋  ๋•Œ job์ด cancel ๋  ์ˆ˜ ์žˆ์Œ


repeatOnLifeCycle

repeatOnLifeCycle์€ ์•ž์˜ ์Šค์ฝ”ํ”„์™€ ๋‹ฌ๋ฆฌ ์žฌ๊ตฌ์„ฑ์ด ๊ฐ€๋Šฅํ•จ. launchWhenstarted๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ํฌ๊ทธ๋ผ์šด๋“œ๋กœ ์ง„์ž…ํ•  ๋•Œ ์ผ์‹œ ์ค‘์ง€๋œ ์ฝ”๋ฃจํ‹ด์„ ์žฌ๊ฐœํ•œ๋‹ค๋ฉด repeatOnLifeCycle์€ ๋‹ค์‹œ ๋Œ์•„์˜ค๋ฉด ์ฒ˜์Œ๋ถ€ํ„ฐ ์žฌ๊ตฌ์„ฑํ•จ. ๋˜ํ•œ, launchWhen ํ•จ์ˆ˜๋Š” ์ž๋™์œผ๋กœ ์ƒ๋ช…์ฃผ๊ธฐ์— ๋งž์ถ”์–ด์ง€์ง€๋งŒ ์ƒ๋ช…์ฃผ๊ธฐ์— ๋งž์ถฐ ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘, ์ทจ์†Œ, ์žฌ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” repeatOnLifeCycle ํ™•์žฅํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ. ์ฆ‰, repeatOnLifecycle ์€ ํ˜ธ์ถœํ•˜๋Š” coroutine์„ suspend ์‹œํ‚ค๊ณ , lifecycle ์ด target state ์—์„œ ๋ฒ—์–ด๋‚˜๋ฉด ์žฌ์‹œ์ž‘ํ•จ. ๊ทธ๋ฆฌ๊ณ  Lifecycle ์ด destroyed ๋˜๋ฉด ํ˜ธ์ถœํ•˜๋Š” coroutine ์„ resume ์‹œํ‚ด.

lifecycleScope.launch {
  	repeatOnLifecycle(Lifecycle.State.STARTED) {
    	viewModel.stateFlow.collect {
        	Log.d("TAG", "number : $it")
        }
    }
}
์‹œ์ 
-- onStart --
number : 0
number : 1
number : 2
-- onStop --
click back btn : -- onDestroy --
-- onStart --
number : 0
number : 1
number : 2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.