ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 안드로이드 스튜디오 Retrofit
    Android Studio 2023. 8. 10. 21:17
    728x90

    Retrofit 이란?

    통신 라이브러리 중 가장 많이 사용되는 대표적인 라이브러이 입니다.

     

    REST API 통신을 위해 구현되었으며, Squareup사의 OkHttp 라이브러리의 상위 구현체 입니다.

    Retrofit은 OkHttp를 네트워크 계층으로 활용하고 그 위에 구축되어 있습니다.

     

    AsyncTask 없이 Background Thread 실행 -> Callback을 통해 Main Thread에서 UI 업데이트

     

    Retrofit 장점

    1. 빠른 성능 :  Asynctask를 사용하지 않고 자체적인 비동기 실행과 스레드 관리를 통해 속도가 많이 개선

     

    2. 간단한 구현 : Retrofit에서는 Request, Response 설정 등 반복적인 작업을 라이브러리에서 넘겨서 처리하므로 작업량이 줄어들고 사용하기 굉장히 편리합니다. 또한 동기 / 비동기에 대해서 쉽게 구현을 할 수 있습니다.

     

    3. 가독성 : 애노테이션 사용으로 코드의 가독성이 뛰어나며, 직관적이 설계가 가능합니다.

     

     

     

    안드로이드 스튜디오에서 사용하기 위한 셋팅으로는 하기와 같습니다.

     

    1. Manifest 에 인터넷 권한을 허용하는 코드를 작성합니다.

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

     

    2. Gradle 의존성을 추가 합니다.

    // Retrofit 라이브러리
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    
    // Gson 변환기 라이브러리
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    
    // Scalars 변환기 라이브러리
    implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'

    Gson Converter : JSON 타입의 응답 결과를 객체로 변환해주는 Converter

    Scalars Converter - 응답결과를 String자체로 받아서 보여주는 Converter

    Moshi Converter Jackson Converter등 이 외의 여러 Converter가 있습니다.

     

    3. REST API로 받아올 데이터를 변환하여 매핑할 데이터 클래스 선언

    class Repository : ArrayList<RepositoryItem>()
    
    data class License(
        val key: String,
        val name: String,
        val node_id: String,
        val spdx_id: String,
        val url: String
    )
    data class Owner(
        val avatar_url: String,
        val events_url: String,
        val followers_url: String,
        val following_url: String,
        val gists_url: String,
        val gravatar_id: String,
        val html_url: String,
        val id: Int,
        val login: String,
        val node_id: String,
        val organizations_url: String,
        val received_events_url: String,
        val repos_url: String,
        val site_admin: Boolean,
        val starred_url: String,
        val subscriptions_url: String,
        val type: String,
        val url: String
    )
    package com.test.retrofitstudy
    
    data class RepositoryItem(
        val allow_forking: Boolean,
        val archive_url: String,
        val archived: Boolean,
        val assignees_url: String,
        val blobs_url: String,
        val branches_url: String,
        val clone_url: String,
        val collaborators_url: String,
        val comments_url: String,
        val commits_url: String,
        val compare_url: String,
        val contents_url: String,
        val contributors_url: String,
        val created_at: String,
        val default_branch: String,
        val deployments_url: String,
        val description: String,
        val disabled: Boolean,
        val downloads_url: String,
        val events_url: String,
        val fork: Boolean,
        val forks: Int,
        val forks_count: Int,
        val forks_url: String,
        val full_name: String,
        val git_commits_url: String,
        val git_refs_url: String,
        val git_tags_url: String,
        val git_url: String,
        val has_discussions: Boolean,
        val has_downloads: Boolean,
        val has_issues: Boolean,
        val has_pages: Boolean,
        val has_projects: Boolean,
        val has_wiki: Boolean,
        val homepage: String,
        val hooks_url: String,
        val html_url: String,
        val id: Int,
        val is_template: Boolean,
        val issue_comment_url: String,
        val issue_events_url: String,
        val issues_url: String,
        val keys_url: String,
        val labels_url: String,
        val language: String,
        val languages_url: String,
        val license: License,
        val merges_url: String,
        val milestones_url: String,
        val mirror_url: Any,
        val name: String,
        val node_id: String,
        val notifications_url: String,
        val open_issues: Int,
        val open_issues_count: Int,
        val owner: Owner,
        val `private`: Boolean,
        val pulls_url: String,
        val pushed_at: String,
        val releases_url: String,
        val size: Int,
        val ssh_url: String,
        val stargazers_count: Int,
        val stargazers_url: String,
        val statuses_url: String,
        val subscribers_url: String,
        val subscription_url: String,
        val svn_url: String,
        val tags_url: String,
        val teams_url: String,
        val topics: List<String>,
        val trees_url: String,
        val updated_at: String,
        val url: String,
        val visibility: String,
        val watchers: Int,
        val watchers_count: Int,
        val web_commit_signoff_required: Boolean
    )

     

    interface GithubService {
        @GET("users/Kotlin/repos")
        fun users(): Call<Repository>
    }

     

    @GET("users/Kotlin/repos") - 요청메소드 GET, baseUrl에 연결될 EndPoint 'posts/{post} 


    반환타입 Call<Repository> - Call은 응답이 왔을때 Callback으로 불려질 타입


    PostResult - 요청GET에 대한 응답데이터를 받아서 DTO 객체화할 클래스 타입 지정 


    메소드명 "users" - 자유롭게 설정, 통신에 영향 x

     

    Retrofit.Build를 통해 Retrofit 인스턴스 생성
      - baseUrl, Converter, Client 설정 부분 (baseUrl는 꼭 / 로 끝나야 함, 아니면 예외 발생)
      - Converter는 여러개 등록 가능, 등록 순서대로 변환 가능여부 판단, 변환 불가능하면 

         다음 컨버터 확인      

     GsonConverter를 마지막에 넣는걸 추천 (Gson은 변환이 불가능해도 가능하다고 반응함)
    Interface 객체 구현
          - retrofit을 통한 객체 구현, 추상 메소드 중 사용할 메소드 Call 객체에 등록
    동기 / 비동기 통신작업 실행 
          - 비동기 enqueue 작업으로 실행, 통신종료 후 이벤트 처리를 위해 Callback 등록
          - onResponse 성공 / onFailure 실패 구분하여 메인스레드에서 처리할 작업 등록
            (onResponse가 무조건 성공 X, 실패코드(3xx & 4xx 등)에도 호출 - isSuccesful() 확인이 필요

     

    package com.test.retrofitstudy
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.view.LayoutInflater
    import android.view.ViewGroup
    import androidx.recyclerview.widget.LinearLayoutManager
    import androidx.recyclerview.widget.RecyclerView
    import com.bumptech.glide.Glide
    import com.test.retrofitstudy.databinding.ActivityMainBinding
    import com.test.retrofitstudy.databinding.RvItemBinding
    import retrofit2.Call
    import retrofit2.Callback
    import retrofit2.Response
    import retrofit2.Retrofit
    import retrofit2.converter.gson.GsonConverterFactory
    import retrofit2.http.GET
    
    
    interface GithubService {
        @GET("users/Kotlin/repos")
        fun users(): Call<Repository>
    }
    
    class MainActivity : AppCompatActivity() {
    
        lateinit var activityMainBinding: ActivityMainBinding
    
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(activityMainBinding.root)
    
            val retrofit = Retrofit.Builder()
                .baseUrl("https://api.github.com") // baseUrl 등록( 반드시 '/'로 마무리
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    
            val adapter = CustomAdapter()
    
            activityMainBinding.run{
                recyclerView.adapter = adapter
                recyclerView.layoutManager = LinearLayoutManager(this@MainActivity)
            }
    
            activityMainBinding.buttonRequest.setOnClickListener{
                val githubService = retrofit.create(GithubService::class.java)
                githubService.users().enqueue(object: Callback<Repository> { // enqueue 를 사용하여 비동기 통신 실행 + 통신 완료 후 이벤트 처리 위한 Callback 리스너 등록
                    override fun onResponse(call: Call<Repository>, response: Response<Repository>) {
                        if(response.isSuccessful){ // 정상적으로 통신이 된 경우 여기 부분은 메인 스레드에서 작업하는 부분이다.
                            activityMainBinding.textView.text = "통신 성공"
                            adapter.userList = response.body() as Repository
                            adapter.notifyDataSetChanged()
                        } else {
                            // 통신이 실패한 경우
                            // onRespons가 무조건 성공 응답이 아니기에 확인이 필요하다.
                            activityMainBinding.textView.text = "통신 실패"
                            adapter.userList?.clear()
                            adapter.notifyDataSetChanged()
                        }
    
                    }
    
                    override fun onFailure(call: Call<Repository>, t: Throwable) {
                        // 인터넷 끊김, 예외 발생 등 시스템적인 이유
                        activityMainBinding.textView.text = "통신 오류"
                    }
                })
            }
        }
    
        inner class CustomAdapter: RecyclerView.Adapter<CustomAdapter.Holder>() {
            var userList: Repository? = null
    
            inner class Holder(val binding: RvItemBinding) : RecyclerView.ViewHolder(binding.root) {
                fun setUser(user: RepositoryItem?){
                    user?.let{
                        binding.textName.text = user.name
                        binding.textId.text = user.git_url
                        Glide.with(binding.imageAvater).load(user.owner.avatar_url).into(binding.imageAvater)
                    }
    
    
                }
            }
    
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
                val binding = RvItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                return Holder(binding)
            }
    
            override fun getItemCount(): Int {
                return userList?.size?: 0
            }
    
            override fun onBindViewHolder(holder: Holder, position: Int) {
                val user = userList?.get(position)
                return holder.setUser(user)
            }
    
        }
    
    
    }
    
    
    
    
    
Designed by Tistory.