Lesson 11: Connect to the internet

HanbinΒ·2021λ…„ 9μ›” 7일
0

Teach Android Development

λͺ©λ‘ 보기
11/13
post-thumbnail

πŸ’‘ Teach Android Development

κ΅¬κΈ€μ—μ„œ μ œκ³΅ν•˜λŠ” ꡐ윑자료λ₯Ό μ •λ¦¬ν•˜κΈ° μœ„ν•œ ν¬μŠ€νŠΈμž…λ‹ˆλ‹€.

Android Development Resources for Educators

Android permissions

Permissions

앱은 λ―Όκ°ν•œ μ‚¬μš©μž 데이터와 νŠΉμ • μ‹œμŠ€ν…œ κΈ°λŠ₯에 λŒ€ν•œ μ ‘κ·Ό κΆŒν•œμ„ μš”μ²­ν•΄μ•Ό ν•©λ‹ˆλ‹€.

  • Android μ‚¬μš©μžμ˜ 개인 정보λ₯Ό λ³΄ν˜Έν•©λ‹ˆλ‹€.
  • AndroidManifest.xmlμ—μ„œ <uses-permission> νƒœκ·Έλ‘œ μ„ μ–Έν•©λ‹ˆλ‹€.

Permissions granted to your app

  • 보호 λ ˆλ²¨μ— 따라 μ„€μΉ˜ λ˜λŠ” λŸ°νƒ€μž„ 쀑에 κΆŒν•œμ„ λΆ€μ—¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 각 κΆŒν•œμ—λŠ” 일반, μ„œλͺ…, μœ„ν—˜κ³Ό 같은 보호 레벨이 μžˆμŠ΅λ‹ˆλ‹€.
  • λŸ°νƒ€μž„ 쀑에 λΆ€μ—¬λœ κΆŒν•œμ˜ 경우 μ‚¬μš©μžμ—κ²Œ 앱에 λŒ€ν•œ μ ‘κ·Ό κΆŒν•œμ„ λͺ…μ‹œμ μœΌλ‘œ μŠΉμΈν•˜κ±°λ‚˜ κ±°λΆ€ν•˜λΌλŠ” λ©”μ‹œμ§€λ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€.

Permission protection levels

Add permissions to the manifest

AndroidManifest.xml에 USE_BIOMETRIC κΆŒν•œμ„ μΆ”κ°€ν•©λ‹ˆλ‹€. USE_BIOMETRIC κΆŒν•œμ„ 톡해 생체 인식 방식을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 일반 보호 레벨이며 μ•± μ„€μΉ˜ μ‹œ λΆ€μ—¬λ©λ‹ˆλ‹€.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.sampleapp">
    <uses-permission android:name="android.permission.USE_BIOMETRIC" />
    <application>
        <activity
            android:name=".MainActivity" ... >
            ...
        </activity>
    </application>
</manifest>

Internet access permissions

앱이 λ„€νŠΈμ›Œν¬ μž‘μ—…μ„ μˆ˜ν–‰ν•΄μ•Ό ν•˜λŠ” 경우 AndroidManifest.xml에 μ•„λž˜ κΆŒν•œμ„ μ„ μ–Έν•΄μ•Ό ν•©λ‹ˆλ‹€. 일반 λ ˆλ²¨μ΄λ―€λ‘œ μΆ”κ°€ μž‘μ—…μ΄ ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Request dangerous permissions

  • μœ„ν—˜ν•œ κΆŒν•œμ΄ ν•„μš”ν•œ κΈ°λŠ₯에 μ ‘κ·Όν•  λ•Œ μ‚¬μš©μžμ—κ²Œ κΆŒν•œμ„ λΆ€μ—¬ν•˜λΌλŠ” λ©”μ‹œμ§€λ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€.
  • κΆŒν•œμ΄ ν•„μš”ν•œ 이유λ₯Ό μ‚¬μš©μžμ—κ²Œ μ„€λͺ…ν•©λ‹ˆλ‹€.
  • μ‚¬μš©μžκ°€ κΆŒν•œμ„ κ±°λΆ€ν•˜λ©΄ μ •μƒμ μœΌλ‘œ 볡귀해야 ν•©λ‹ˆλ‹€.(앱은 계속 μž‘λ™ν•΄μ•Ό 함.)

App permissions best practices

  • 앱이 μž‘λ™ν•˜λŠ” 데 ν•„μš”ν•œ κΆŒν•œλ§Œ μ‚¬μš©ν•©λ‹ˆλ‹€.
  • λΌμ΄λΈŒλŸ¬λ¦¬μ— ν•„μš”ν•œ κΆŒν•œμ„ μ£Όμ˜ν•©λ‹ˆλ‹€.(λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ κΆŒν•œμ΄ μ‚¬μš©λ˜λŠ” μš©λ„λ₯Ό μ•Œμ•„μ•Ό 함.)
  • 투λͺ…ν•΄μ•Ό ν•©λ‹ˆλ‹€.(μ ‘κ·Ό λŒ€μƒκ³Ό 이유λ₯Ό λͺ…ν™•ν•˜κ²Œ 함.)
  • μ‹œμŠ€ν…œ 접근을 λͺ…μ‹μ μœΌλ‘œ λ§Œλ“­λ‹ˆλ‹€.(λ―Όκ°ν•œ κΈ°λŠ₯에 μ ‘κ·Όν•  λ•Œ 지속적인 ν‘œμ‹œλ₯Ό μ œκ³΅ν•¨.)

Connect to, and use, network resources

Retrofit

  • HTTP APIλ₯Ό Kotlin 및 Java μΈν„°νŽ˜μ΄μŠ€λ‘œ μ „ν™˜ν•˜λŠ” λ„€νŠΈμ›Œν‚Ή 라이브러리.
  • μ•±μ—μ„œ μ‚¬μš©ν•  객체둜 μš”μ²­ 및 응닡을 μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • XML 및 JSONκ³Ό 같은 일반적인 Response νƒ€μž… νŒŒμ‹±μ„ μ œκ³΅ν•©λ‹ˆλ‹€.
  • λ‹€λ₯Έ 응닡 νƒ€μž…μ„ μ§€μ›ν•˜λ„λ‘ ν™•μž₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Why use Retrofit?

  • OkHttp와 같은 μ‚°μ—… ν‘œμ€€ 라이브러리λ₯Ό 기반으둜 ν•©λ‹ˆλ‹€.
    • HTTP/2 지원
    • Connection pooling
    • 응닡 캐싱 및 λ³΄μ•ˆ κ°•ν™”
  • μš”μ²­μ„ μ‹€ν–‰ν•˜λŠ”λ° ν•„μš”ν•œ κΈ°λ³Έ μ„€μ •μ—μ„œ κ°œλ°œμžκ°€ ν•  일이 μ μŠ΅λ‹ˆλ‹€.

Add Gradle dependencies

μ•± Gradle νŒŒμΌμ— 쒅속성을 μΆ”κ°€ν•©λ‹ˆλ‹€. JSONμ—μ„œ 객체둜의 λ³€ν™˜μ„ μœ„ν•΄ Moshiλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"

implementation "com.squareup.moshi:moshi:$moshi_version"
implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"

Connect to a web service

HTTP methods

  • GET
  • POST
  • PUT
  • DELETE

Example web service API

Define a Retrofit service

@Path : λ™μ μœΌλ‘œ URL에 λŒ€ν•΄ μž‘λ™ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.
@Query : 쿼리 λ§€κ°œλ³€μˆ˜λ₯Ό μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
@Body : HTTP μš”μ²­ Body에 전달할 수 μžˆμŠ΅λ‹ˆλ‹€.

interface SimpleService {

    @GET("posts")
    suspend fun listPosts(): List<Post>

    @GET("posts/{userId}")
    suspend fun listByUser(@Path("userId") userId:String): List<Post>

    @GET("posts/search")  // becomes post/search?filter=query
    suspend fun search(@Query("filter") search: String): List<Post>

    @POST("posts/new")
    suspend fun create(@Body post : Post): Post
}

Create a Retrofit object for network access

Retrofit.Builder() λ₯Ό μ‚¬μš©ν•˜μ—¬ Retrofit 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ—¬ μ„œλΉ„μŠ€μ˜ ꡬ체적인 κ΅¬ν˜„μ„ λ§Œλ“­λ‹ˆλ‹€.

val retrofit = Retrofit.Builder()
    .baseUrl("https://example.com")
    .addConverterFactory(...)
    .build()

val service = retrofit.create(SimpleService::class.java)

End-to-end diagram

Converter.Factory

Response λ₯Ό 클래슀 객체둜 λ³€ν™˜ν•˜λŠ” 데 도움이 λ©λ‹ˆλ‹€.

  • JSON (Gson or Moshi)
  • XML (Jackson, SimpleXML, JAXB)
  • Protocol buffers
  • Scalars (primitives, boxed, and Strings)

Moshi

  • JSON을 객체둜 νŒŒμ‹±ν•˜κ³  κ·Έ λ°˜λŒ€λ‘œ νŒŒμ‹± ν•˜κΈ° μœ„ν•œ JSON 라이브러리.
  • μ•±μ˜ Gradle νŒŒμΌμ— Moshi 라이브러리 쒅속성을 μΆ”κ°€ν•©λ‹ˆλ‹€.
  • Retrofitκ³Ό ν•¨κ»˜ μ‚¬μš©ν•˜λ„λ‘ Moshi λΉŒλ”λ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€.

Moshi JSON encoding

맀핑을 μˆ˜ν–‰ν•˜λŠ” μ–΄λŒ‘ν„°λ₯Ό μžλ™μœΌλ‘œ μƒμ„±ν•˜κΈ° μœ„ν•΄ generateAdapter = true둜 μ„€μ •ν–ˆμŠ΅λ‹ˆλ‹€.

@JsonClass(generateAdapter = true)
data class Post (
    val title: String,
    val description: String,
    val url: String,
    val updated: String,
    val thumbnail: String,
    val closedCaptions: String?)

JSON code

λ‹€μŒμ€ JSON μ‘λ‹΅μž…λ‹ˆλ‹€. MoshiλŠ” 이 응닡을 객체둜 λ³€ν™˜ν•©λ‹ˆλ‹€.

{
    "title":"Android Jetpack: EmojiCompat",
    "description":"Android Jetpack: EmojiCompat",
    "url":"https://www.youtube.com/watch?v=sYGKUtM2ga8",
    "updated":"2018-06-07T17:09:43+00:00",
    "thumbnail":"https://i4.ytimg.com/vi/sYGKUtM2ga8/hqdefault.jpg"
}

Set up Retrofit and Moshi

μ„œλΉ„μŠ€λ₯Ό μΈμŠ€ν„΄μŠ€ν™”ν•˜λŠ” λ™μ‹œμ— MoshiConverterFactory μ„€μ •ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€.

private val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()
val retrofit = Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .baseUrl(BASE_URL)
        .build()
object API {
    val retrofitService : SimpleService by lazy {
        retrofit.create(SimpleService::class.java)
    }
}

Use Retrofit with coroutines

ViewModelμ—μ„œ 코루틴을 μ‹œμž‘ν•©λ‹ˆλ‹€.

viewModelScope.launch {
    Log.d("posts", API.retrofitService.searchPosts("query"))
}

Display images

Glide

  • Android의 Third-party 이미지 λ‘œλ”© 라이브러리.
  • λΆ€λ“œλŸ¬μš΄ μŠ€ν¬λ‘€μ„ μœ„ν•œ μ„±λŠ₯에 초점.
  • 이미지, λΉ„λ””μ˜€ μŠ€ν‹Έ 및 μ• λ‹ˆλ©”μ΄μ…˜ GIF 지원.

Add Gradle dependency

implementation "com.github.bumptech.glide:glide:$glide_version"

Load an image

Glideλ₯Ό lifecycle owner에 μ—°κ²°ν•˜κ³  μ§€μ •λœ URL의 이미지λ₯Ό ImageView에 λ‘œλ“œν•©λ‹ˆλ‹€.

Glide.with(fragment)
    .load(url)
    .into(imageView);

Customize a request with RequestOptions

  • 이미지 crop 적용.
  • transitions 적용.
  • placeholder, error 이미지 μ˜΅μ…˜ μ„€μ •.
  • 캐싱 μ •μ±… μ„€μ •.

RequestOptions example

@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
    imgUrl?.let {
        val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()

        Glide.with(imgView)
            .load(imgUri)
            .apply(RequestOptions()
                .placeholder(R.drawable.loading_animation)
                .error(R.drawable.ic_broken_image))
            .into(imgView)
    }
}

0개의 λŒ“κΈ€